From 9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00 Mon Sep 17 00:00:00 2001 From: Yunhong Jiang Date: Tue, 4 Aug 2015 12:17:53 -0700 Subject: Add the rt linux 4.1.3-rt3 as base Import the rt linux 4.1.3-rt3 as OPNFV kvm base. It's from git://git.kernel.org/pub/scm/linux/kernel/git/rt/linux-rt-devel.git linux-4.1.y-rt and the base is: commit 0917f823c59692d751951bf5ea699a2d1e2f26a2 Author: Sebastian Andrzej Siewior Date: Sat Jul 25 12:13:34 2015 +0200 Prepare v4.1.3-rt3 Signed-off-by: Sebastian Andrzej Siewior We lose all the git history this way and it's not good. We should apply another opnfv project repo in future. Change-Id: I87543d81c9df70d99c5001fbdf646b202c19f423 Signed-off-by: Yunhong Jiang --- kernel/drivers/gpu/drm/nouveau/Kbuild | 66 + kernel/drivers/gpu/drm/nouveau/Kconfig | 70 + kernel/drivers/gpu/drm/nouveau/dispnv04/Kbuild | 11 + kernel/drivers/gpu/drm/nouveau/dispnv04/arb.c | 265 ++ kernel/drivers/gpu/drm/nouveau/dispnv04/crtc.c | 1146 +++++++ kernel/drivers/gpu/drm/nouveau/dispnv04/cursor.c | 70 + kernel/drivers/gpu/drm/nouveau/dispnv04/dac.c | 556 ++++ kernel/drivers/gpu/drm/nouveau/dispnv04/dfp.c | 722 +++++ kernel/drivers/gpu/drm/nouveau/dispnv04/disp.c | 190 ++ kernel/drivers/gpu/drm/nouveau/dispnv04/disp.h | 186 ++ kernel/drivers/gpu/drm/nouveau/dispnv04/hw.c | 825 +++++ kernel/drivers/gpu/drm/nouveau/dispnv04/hw.h | 409 +++ kernel/drivers/gpu/drm/nouveau/dispnv04/nvreg.h | 517 +++ kernel/drivers/gpu/drm/nouveau/dispnv04/overlay.c | 502 +++ .../drivers/gpu/drm/nouveau/dispnv04/tvmodesnv17.c | 592 ++++ kernel/drivers/gpu/drm/nouveau/dispnv04/tvnv04.c | 249 ++ kernel/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c | 837 +++++ kernel/drivers/gpu/drm/nouveau/dispnv04/tvnv17.h | 163 + .../drivers/gpu/drm/nouveau/include/nvif/class.h | 583 ++++ .../drivers/gpu/drm/nouveau/include/nvif/client.h | 39 + .../drivers/gpu/drm/nouveau/include/nvif/device.h | 61 + .../drivers/gpu/drm/nouveau/include/nvif/driver.h | 22 + .../drivers/gpu/drm/nouveau/include/nvif/event.h | 62 + .../drivers/gpu/drm/nouveau/include/nvif/ioctl.h | 128 + kernel/drivers/gpu/drm/nouveau/include/nvif/list.h | 353 +++ .../drivers/gpu/drm/nouveau/include/nvif/notify.h | 39 + .../drivers/gpu/drm/nouveau/include/nvif/object.h | 75 + kernel/drivers/gpu/drm/nouveau/include/nvif/os.h | 44 + .../drivers/gpu/drm/nouveau/include/nvif/unpack.h | 24 + .../gpu/drm/nouveau/include/nvkm/core/client.h | 55 + .../gpu/drm/nouveau/include/nvkm/core/debug.h | 18 + .../gpu/drm/nouveau/include/nvkm/core/device.h | 101 + .../gpu/drm/nouveau/include/nvkm/core/devidx.h | 62 + .../gpu/drm/nouveau/include/nvkm/core/engctx.h | 51 + .../gpu/drm/nouveau/include/nvkm/core/engine.h | 56 + .../gpu/drm/nouveau/include/nvkm/core/enum.h | 21 + .../gpu/drm/nouveau/include/nvkm/core/event.h | 34 + .../gpu/drm/nouveau/include/nvkm/core/gpuobj.h | 64 + .../gpu/drm/nouveau/include/nvkm/core/handle.h | 34 + .../gpu/drm/nouveau/include/nvkm/core/ioctl.h | 7 + .../drivers/gpu/drm/nouveau/include/nvkm/core/mm.h | 40 + .../gpu/drm/nouveau/include/nvkm/core/namedb.h | 53 + .../gpu/drm/nouveau/include/nvkm/core/notify.h | 38 + .../gpu/drm/nouveau/include/nvkm/core/object.h | 203 ++ .../gpu/drm/nouveau/include/nvkm/core/option.h | 17 + .../drivers/gpu/drm/nouveau/include/nvkm/core/os.h | 4 + .../gpu/drm/nouveau/include/nvkm/core/parent.h | 58 + .../gpu/drm/nouveau/include/nvkm/core/printk.h | 29 + .../gpu/drm/nouveau/include/nvkm/core/ramht.h | 20 + .../gpu/drm/nouveau/include/nvkm/core/subdev.h | 119 + .../gpu/drm/nouveau/include/nvkm/engine/bsp.h | 5 + .../gpu/drm/nouveau/include/nvkm/engine/ce.h | 16 + .../gpu/drm/nouveau/include/nvkm/engine/cipher.h | 5 + .../gpu/drm/nouveau/include/nvkm/engine/device.h | 30 + .../gpu/drm/nouveau/include/nvkm/engine/disp.h | 32 + .../gpu/drm/nouveau/include/nvkm/engine/dmaobj.h | 26 + .../gpu/drm/nouveau/include/nvkm/engine/falcon.h | 81 + .../gpu/drm/nouveau/include/nvkm/engine/fifo.h | 127 + .../gpu/drm/nouveau/include/nvkm/engine/gr.h | 88 + .../gpu/drm/nouveau/include/nvkm/engine/mpeg.h | 62 + .../gpu/drm/nouveau/include/nvkm/engine/mspdec.h | 7 + .../gpu/drm/nouveau/include/nvkm/engine/msppp.h | 6 + .../gpu/drm/nouveau/include/nvkm/engine/msvld.h | 7 + .../gpu/drm/nouveau/include/nvkm/engine/pm.h | 34 + .../gpu/drm/nouveau/include/nvkm/engine/sec.h | 5 + .../gpu/drm/nouveau/include/nvkm/engine/sw.h | 50 + .../gpu/drm/nouveau/include/nvkm/engine/vp.h | 5 + .../gpu/drm/nouveau/include/nvkm/engine/xtensa.h | 35 + .../gpu/drm/nouveau/include/nvkm/subdev/bar.h | 33 + .../gpu/drm/nouveau/include/nvkm/subdev/bios.h | 32 + .../drm/nouveau/include/nvkm/subdev/bios/M0203.h | 29 + .../drm/nouveau/include/nvkm/subdev/bios/M0205.h | 29 + .../drm/nouveau/include/nvkm/subdev/bios/M0209.h | 27 + .../drm/nouveau/include/nvkm/subdev/bios/P0260.h | 21 + .../gpu/drm/nouveau/include/nvkm/subdev/bios/bit.h | 11 + .../gpu/drm/nouveau/include/nvkm/subdev/bios/bmp.h | 37 + .../drm/nouveau/include/nvkm/subdev/bios/boost.h | 27 + .../drm/nouveau/include/nvkm/subdev/bios/conn.h | 44 + .../drm/nouveau/include/nvkm/subdev/bios/cstep.h | 26 + .../gpu/drm/nouveau/include/nvkm/subdev/bios/dcb.h | 65 + .../drm/nouveau/include/nvkm/subdev/bios/disp.h | 39 + .../gpu/drm/nouveau/include/nvkm/subdev/bios/dp.h | 31 + .../drm/nouveau/include/nvkm/subdev/bios/extdev.h | 25 + .../gpu/drm/nouveau/include/nvkm/subdev/bios/fan.h | 6 + .../drm/nouveau/include/nvkm/subdev/bios/gpio.h | 46 + .../gpu/drm/nouveau/include/nvkm/subdev/bios/i2c.h | 25 + .../drm/nouveau/include/nvkm/subdev/bios/image.h | 11 + .../drm/nouveau/include/nvkm/subdev/bios/init.h | 20 + .../gpu/drm/nouveau/include/nvkm/subdev/bios/mxm.h | 6 + .../drm/nouveau/include/nvkm/subdev/bios/npde.h | 10 + .../drm/nouveau/include/nvkm/subdev/bios/pcir.h | 16 + .../drm/nouveau/include/nvkm/subdev/bios/perf.h | 41 + .../gpu/drm/nouveau/include/nvkm/subdev/bios/pll.h | 75 + .../gpu/drm/nouveau/include/nvkm/subdev/bios/pmu.h | 35 + .../drm/nouveau/include/nvkm/subdev/bios/ramcfg.h | 141 + .../drm/nouveau/include/nvkm/subdev/bios/rammap.h | 21 + .../drm/nouveau/include/nvkm/subdev/bios/therm.h | 72 + .../drm/nouveau/include/nvkm/subdev/bios/timing.h | 11 + .../drm/nouveau/include/nvkm/subdev/bios/vmap.h | 21 + .../drm/nouveau/include/nvkm/subdev/bios/volt.h | 23 + .../drm/nouveau/include/nvkm/subdev/bios/xpio.h | 18 + .../gpu/drm/nouveau/include/nvkm/subdev/bus.h | 50 + .../gpu/drm/nouveau/include/nvkm/subdev/clk.h | 161 + .../gpu/drm/nouveau/include/nvkm/subdev/devinit.h | 32 + .../gpu/drm/nouveau/include/nvkm/subdev/fb.h | 154 + .../gpu/drm/nouveau/include/nvkm/subdev/fuse.h | 28 + .../gpu/drm/nouveau/include/nvkm/subdev/gpio.h | 44 + .../gpu/drm/nouveau/include/nvkm/subdev/i2c.h | 135 + .../gpu/drm/nouveau/include/nvkm/subdev/ibus.h | 32 + .../gpu/drm/nouveau/include/nvkm/subdev/instmem.h | 49 + .../gpu/drm/nouveau/include/nvkm/subdev/ltc.h | 31 + .../gpu/drm/nouveau/include/nvkm/subdev/mc.h | 28 + .../gpu/drm/nouveau/include/nvkm/subdev/mmu.h | 104 + .../gpu/drm/nouveau/include/nvkm/subdev/mxm.h | 34 + .../gpu/drm/nouveau/include/nvkm/subdev/pmu.h | 54 + .../gpu/drm/nouveau/include/nvkm/subdev/therm.h | 79 + .../gpu/drm/nouveau/include/nvkm/subdev/timer.h | 61 + .../gpu/drm/nouveau/include/nvkm/subdev/vga.h | 30 + .../gpu/drm/nouveau/include/nvkm/subdev/volt.h | 58 + kernel/drivers/gpu/drm/nouveau/nouveau_abi16.c | 537 ++++ kernel/drivers/gpu/drm/nouveau/nouveau_abi16.h | 114 + kernel/drivers/gpu/drm/nouveau/nouveau_acpi.c | 427 +++ kernel/drivers/gpu/drm/nouveau/nouveau_acpi.h | 26 + kernel/drivers/gpu/drm/nouveau/nouveau_agp.c | 195 ++ kernel/drivers/gpu/drm/nouveau/nouveau_agp.h | 10 + kernel/drivers/gpu/drm/nouveau/nouveau_backlight.c | 254 ++ kernel/drivers/gpu/drm/nouveau/nouveau_bios.c | 2127 +++++++++++++ kernel/drivers/gpu/drm/nouveau/nouveau_bios.h | 179 ++ kernel/drivers/gpu/drm/nouveau/nouveau_bo.c | 1661 ++++++++++ kernel/drivers/gpu/drm/nouveau/nouveau_bo.h | 108 + kernel/drivers/gpu/drm/nouveau/nouveau_chan.c | 425 +++ kernel/drivers/gpu/drm/nouveau/nouveau_chan.h | 52 + kernel/drivers/gpu/drm/nouveau/nouveau_connector.c | 1275 ++++++++ kernel/drivers/gpu/drm/nouveau/nouveau_connector.h | 113 + kernel/drivers/gpu/drm/nouveau/nouveau_crtc.h | 90 + kernel/drivers/gpu/drm/nouveau/nouveau_debugfs.c | 64 + kernel/drivers/gpu/drm/nouveau/nouveau_debugfs.h | 22 + kernel/drivers/gpu/drm/nouveau/nouveau_display.c | 910 ++++++ kernel/drivers/gpu/drm/nouveau/nouveau_display.h | 104 + kernel/drivers/gpu/drm/nouveau/nouveau_dma.c | 261 ++ kernel/drivers/gpu/drm/nouveau/nouveau_dma.h | 195 ++ kernel/drivers/gpu/drm/nouveau/nouveau_dp.c | 89 + kernel/drivers/gpu/drm/nouveau/nouveau_drm.c | 1136 +++++++ kernel/drivers/gpu/drm/nouveau/nouveau_drm.h | 202 ++ kernel/drivers/gpu/drm/nouveau/nouveau_encoder.h | 93 + kernel/drivers/gpu/drm/nouveau/nouveau_fbcon.c | 572 ++++ kernel/drivers/gpu/drm/nouveau/nouveau_fbcon.h | 79 + kernel/drivers/gpu/drm/nouveau/nouveau_fence.c | 590 ++++ kernel/drivers/gpu/drm/nouveau/nouveau_fence.h | 107 + kernel/drivers/gpu/drm/nouveau/nouveau_gem.c | 925 ++++++ kernel/drivers/gpu/drm/nouveau/nouveau_gem.h | 46 + kernel/drivers/gpu/drm/nouveau/nouveau_hwmon.c | 648 ++++ kernel/drivers/gpu/drm/nouveau/nouveau_hwmon.h | 43 + kernel/drivers/gpu/drm/nouveau/nouveau_ioc32.c | 69 + kernel/drivers/gpu/drm/nouveau/nouveau_ioctl.h | 7 + kernel/drivers/gpu/drm/nouveau/nouveau_nvif.c | 136 + kernel/drivers/gpu/drm/nouveau/nouveau_platform.c | 252 ++ kernel/drivers/gpu/drm/nouveau/nouveau_platform.h | 72 + kernel/drivers/gpu/drm/nouveau/nouveau_prime.c | 115 + kernel/drivers/gpu/drm/nouveau/nouveau_reg.h | 858 +++++ kernel/drivers/gpu/drm/nouveau/nouveau_sgdma.c | 116 + kernel/drivers/gpu/drm/nouveau/nouveau_sysfs.c | 198 ++ kernel/drivers/gpu/drm/nouveau/nouveau_sysfs.h | 21 + kernel/drivers/gpu/drm/nouveau/nouveau_ttm.c | 438 +++ kernel/drivers/gpu/drm/nouveau/nouveau_ttm.h | 25 + kernel/drivers/gpu/drm/nouveau/nouveau_usif.c | 384 +++ kernel/drivers/gpu/drm/nouveau/nouveau_usif.h | 9 + kernel/drivers/gpu/drm/nouveau/nouveau_vga.c | 129 + kernel/drivers/gpu/drm/nouveau/nouveau_vga.h | 8 + kernel/drivers/gpu/drm/nouveau/nv04_fbcon.c | 280 ++ kernel/drivers/gpu/drm/nouveau/nv04_fence.c | 111 + kernel/drivers/gpu/drm/nouveau/nv10_fence.c | 113 + kernel/drivers/gpu/drm/nouveau/nv10_fence.h | 20 + kernel/drivers/gpu/drm/nouveau/nv17_fence.c | 151 + kernel/drivers/gpu/drm/nouveau/nv50_display.c | 2568 +++++++++++++++ kernel/drivers/gpu/drm/nouveau/nv50_display.h | 45 + kernel/drivers/gpu/drm/nouveau/nv50_fbcon.c | 258 ++ kernel/drivers/gpu/drm/nouveau/nv50_fence.c | 122 + kernel/drivers/gpu/drm/nouveau/nv84_fence.c | 273 ++ kernel/drivers/gpu/drm/nouveau/nvc0_fbcon.c | 261 ++ kernel/drivers/gpu/drm/nouveau/nvc0_fence.c | 84 + kernel/drivers/gpu/drm/nouveau/nvif/Kbuild | 4 + kernel/drivers/gpu/drm/nouveau/nvif/client.c | 130 + kernel/drivers/gpu/drm/nouveau/nvif/device.c | 78 + kernel/drivers/gpu/drm/nouveau/nvif/notify.c | 248 ++ kernel/drivers/gpu/drm/nouveau/nvif/object.c | 304 ++ kernel/drivers/gpu/drm/nouveau/nvkm/Kbuild | 3 + kernel/drivers/gpu/drm/nouveau/nvkm/core/Kbuild | 17 + kernel/drivers/gpu/drm/nouveau/nvkm/core/client.c | 266 ++ kernel/drivers/gpu/drm/nouveau/nvkm/core/engctx.c | 239 ++ kernel/drivers/gpu/drm/nouveau/nvkm/core/engine.c | 75 + kernel/drivers/gpu/drm/nouveau/nvkm/core/enum.c | 66 + kernel/drivers/gpu/drm/nouveau/nvkm/core/event.c | 99 + kernel/drivers/gpu/drm/nouveau/nvkm/core/gpuobj.c | 316 ++ kernel/drivers/gpu/drm/nouveau/nvkm/core/handle.c | 221 ++ kernel/drivers/gpu/drm/nouveau/nvkm/core/ioctl.c | 526 +++ kernel/drivers/gpu/drm/nouveau/nvkm/core/mm.c | 304 ++ kernel/drivers/gpu/drm/nouveau/nvkm/core/namedb.c | 199 ++ kernel/drivers/gpu/drm/nouveau/nvkm/core/notify.c | 163 + kernel/drivers/gpu/drm/nouveau/nvkm/core/object.c | 330 ++ kernel/drivers/gpu/drm/nouveau/nvkm/core/option.c | 121 + kernel/drivers/gpu/drm/nouveau/nvkm/core/parent.c | 159 + kernel/drivers/gpu/drm/nouveau/nvkm/core/printk.c | 103 + kernel/drivers/gpu/drm/nouveau/nvkm/core/ramht.c | 106 + kernel/drivers/gpu/drm/nouveau/nvkm/core/subdev.c | 120 + kernel/drivers/gpu/drm/nouveau/nvkm/engine/Kbuild | 19 + .../drivers/gpu/drm/nouveau/nvkm/engine/bsp/Kbuild | 1 + .../drivers/gpu/drm/nouveau/nvkm/engine/bsp/g84.c | 93 + .../drivers/gpu/drm/nouveau/nvkm/engine/ce/Kbuild | 4 + .../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 | 606 ++++ .../gpu/drm/nouveau/nvkm/engine/ce/fuc/gt215.fuc3 | 2 + .../drm/nouveau/nvkm/engine/ce/fuc/gt215.fuc3.h | 620 ++++ .../drivers/gpu/drm/nouveau/nvkm/engine/ce/gf100.c | 166 + .../drivers/gpu/drm/nouveau/nvkm/engine/ce/gk104.c | 173 + .../drivers/gpu/drm/nouveau/nvkm/engine/ce/gm204.c | 173 + .../drivers/gpu/drm/nouveau/nvkm/engine/ce/gt215.c | 152 + .../gpu/drm/nouveau/nvkm/engine/cipher/Kbuild | 1 + .../gpu/drm/nouveau/nvkm/engine/cipher/g84.c | 184 ++ .../gpu/drm/nouveau/nvkm/engine/device/Kbuild | 12 + .../gpu/drm/nouveau/nvkm/engine/device/acpi.c | 60 + .../gpu/drm/nouveau/nvkm/engine/device/acpi.h | 8 + .../gpu/drm/nouveau/nvkm/engine/device/base.c | 736 +++++ .../gpu/drm/nouveau/nvkm/engine/device/ctrl.c | 199 ++ .../gpu/drm/nouveau/nvkm/engine/device/gf100.c | 358 +++ .../gpu/drm/nouveau/nvkm/engine/device/gk104.c | 326 ++ .../gpu/drm/nouveau/nvkm/engine/device/gm100.c | 190 ++ .../gpu/drm/nouveau/nvkm/engine/device/nv04.c | 89 + .../gpu/drm/nouveau/nvkm/engine/device/nv10.c | 204 ++ .../gpu/drm/nouveau/nvkm/engine/device/nv20.c | 131 + .../gpu/drm/nouveau/nvkm/engine/device/nv30.c | 153 + .../gpu/drm/nouveau/nvkm/engine/device/nv40.c | 427 +++ .../gpu/drm/nouveau/nvkm/engine/device/nv50.c | 478 +++ .../gpu/drm/nouveau/nvkm/engine/device/priv.h | 16 + .../gpu/drm/nouveau/nvkm/engine/disp/Kbuild | 29 + .../gpu/drm/nouveau/nvkm/engine/disp/base.c | 240 ++ .../gpu/drm/nouveau/nvkm/engine/disp/conn.c | 174 + .../gpu/drm/nouveau/nvkm/engine/disp/conn.h | 58 + .../gpu/drm/nouveau/nvkm/engine/disp/dacnv50.c | 99 + .../gpu/drm/nouveau/nvkm/engine/disp/dport.c | 398 +++ .../gpu/drm/nouveau/nvkm/engine/disp/dport.h | 75 + .../drivers/gpu/drm/nouveau/nvkm/engine/disp/g84.c | 272 ++ .../drivers/gpu/drm/nouveau/nvkm/engine/disp/g94.c | 139 + .../gpu/drm/nouveau/nvkm/engine/disp/gf110.c | 1310 ++++++++ .../gpu/drm/nouveau/nvkm/engine/disp/gk104.c | 268 ++ .../gpu/drm/nouveau/nvkm/engine/disp/gk110.c | 103 + .../gpu/drm/nouveau/nvkm/engine/disp/gm107.c | 103 + .../gpu/drm/nouveau/nvkm/engine/disp/gm204.c | 111 + .../gpu/drm/nouveau/nvkm/engine/disp/gt200.c | 148 + .../gpu/drm/nouveau/nvkm/engine/disp/gt215.c | 104 + .../gpu/drm/nouveau/nvkm/engine/disp/hdagf110.c | 73 + .../gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c | 69 + .../gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c | 91 + .../gpu/drm/nouveau/nvkm/engine/disp/hdmigf110.c | 79 + .../gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c | 83 + .../gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c | 92 + .../gpu/drm/nouveau/nvkm/engine/disp/nv04.c | 205 ++ .../gpu/drm/nouveau/nvkm/engine/disp/nv50.c | 2019 ++++++++++++ .../gpu/drm/nouveau/nvkm/engine/disp/nv50.h | 226 ++ .../gpu/drm/nouveau/nvkm/engine/disp/outp.c | 142 + .../gpu/drm/nouveau/nvkm/engine/disp/outp.h | 61 + .../gpu/drm/nouveau/nvkm/engine/disp/outpdp.c | 301 ++ .../gpu/drm/nouveau/nvkm/engine/disp/outpdp.h | 61 + .../gpu/drm/nouveau/nvkm/engine/disp/piornv50.c | 170 + .../gpu/drm/nouveau/nvkm/engine/disp/priv.h | 42 + .../gpu/drm/nouveau/nvkm/engine/disp/sorg94.c | 145 + .../gpu/drm/nouveau/nvkm/engine/disp/sorgf110.c | 124 + .../gpu/drm/nouveau/nvkm/engine/disp/sorgm204.c | 139 + .../gpu/drm/nouveau/nvkm/engine/disp/sornv50.c | 56 + .../drivers/gpu/drm/nouveau/nvkm/engine/disp/vga.c | 219 ++ .../gpu/drm/nouveau/nvkm/engine/dmaobj/Kbuild | 5 + .../gpu/drm/nouveau/nvkm/engine/dmaobj/base.c | 164 + .../gpu/drm/nouveau/nvkm/engine/dmaobj/gf100.c | 176 + .../gpu/drm/nouveau/nvkm/engine/dmaobj/gf110.c | 165 + .../gpu/drm/nouveau/nvkm/engine/dmaobj/nv04.c | 163 + .../gpu/drm/nouveau/nvkm/engine/dmaobj/nv50.c | 195 ++ .../gpu/drm/nouveau/nvkm/engine/dmaobj/priv.h | 28 + .../drivers/gpu/drm/nouveau/nvkm/engine/falcon.c | 277 ++ .../gpu/drm/nouveau/nvkm/engine/fifo/Kbuild | 12 + .../gpu/drm/nouveau/nvkm/engine/fifo/base.c | 282 ++ .../drivers/gpu/drm/nouveau/nvkm/engine/fifo/g84.c | 487 +++ .../gpu/drm/nouveau/nvkm/engine/fifo/gf100.c | 967 ++++++ .../gpu/drm/nouveau/nvkm/engine/fifo/gk104.c | 1139 +++++++ .../gpu/drm/nouveau/nvkm/engine/fifo/gk104.h | 18 + .../gpu/drm/nouveau/nvkm/engine/fifo/gk208.c | 36 + .../gpu/drm/nouveau/nvkm/engine/fifo/gk20a.c | 34 + .../gpu/drm/nouveau/nvkm/engine/fifo/gm204.c | 57 + .../gpu/drm/nouveau/nvkm/engine/fifo/nv04.c | 635 ++++ .../gpu/drm/nouveau/nvkm/engine/fifo/nv04.h | 175 + .../gpu/drm/nouveau/nvkm/engine/fifo/nv10.c | 178 ++ .../gpu/drm/nouveau/nvkm/engine/fifo/nv17.c | 215 ++ .../gpu/drm/nouveau/nvkm/engine/fifo/nv40.c | 356 +++ .../gpu/drm/nouveau/nvkm/engine/fifo/nv50.c | 534 ++++ .../gpu/drm/nouveau/nvkm/engine/fifo/nv50.h | 36 + .../drivers/gpu/drm/nouveau/nvkm/engine/gr/Kbuild | 40 + .../gpu/drm/nouveau/nvkm/engine/gr/ctxgf100.c | 1390 ++++++++ .../gpu/drm/nouveau/nvkm/engine/gr/ctxgf100.h | 228 ++ .../gpu/drm/nouveau/nvkm/engine/gr/ctxgf104.c | 108 + .../gpu/drm/nouveau/nvkm/engine/gr/ctxgf108.c | 806 +++++ .../gpu/drm/nouveau/nvkm/engine/gr/ctxgf110.c | 359 +++ .../gpu/drm/nouveau/nvkm/engine/gr/ctxgf117.c | 284 ++ .../gpu/drm/nouveau/nvkm/engine/gr/ctxgf119.c | 529 ++++ .../gpu/drm/nouveau/nvkm/engine/gr/ctxgk104.c | 1024 ++++++ .../gpu/drm/nouveau/nvkm/engine/gr/ctxgk110.c | 842 +++++ .../gpu/drm/nouveau/nvkm/engine/gr/ctxgk110b.c | 103 + .../gpu/drm/nouveau/nvkm/engine/gr/ctxgk208.c | 564 ++++ .../gpu/drm/nouveau/nvkm/engine/gr/ctxgk20a.c | 62 + .../gpu/drm/nouveau/nvkm/engine/gr/ctxgm107.c | 1028 ++++++ .../gpu/drm/nouveau/nvkm/engine/gr/ctxgm204.c | 1054 ++++++ .../gpu/drm/nouveau/nvkm/engine/gr/ctxgm206.c | 83 + .../gpu/drm/nouveau/nvkm/engine/gr/ctxnv40.c | 694 ++++ .../gpu/drm/nouveau/nvkm/engine/gr/ctxnv40.h | 129 + .../gpu/drm/nouveau/nvkm/engine/gr/ctxnv50.c | 3345 ++++++++++++++++++++ .../gpu/drm/nouveau/nvkm/engine/gr/fuc/com.fuc | 335 ++ .../gpu/drm/nouveau/nvkm/engine/gr/fuc/gpc.fuc | 490 +++ .../drm/nouveau/nvkm/engine/gr/fuc/gpcgf100.fuc3 | 42 + .../drm/nouveau/nvkm/engine/gr/fuc/gpcgf100.fuc3.h | 530 ++++ .../drm/nouveau/nvkm/engine/gr/fuc/gpcgf117.fuc3 | 42 + .../drm/nouveau/nvkm/engine/gr/fuc/gpcgf117.fuc3.h | 537 ++++ .../drm/nouveau/nvkm/engine/gr/fuc/gpcgk104.fuc3 | 42 + .../drm/nouveau/nvkm/engine/gr/fuc/gpcgk104.fuc3.h | 537 ++++ .../drm/nouveau/nvkm/engine/gr/fuc/gpcgk110.fuc3 | 42 + .../drm/nouveau/nvkm/engine/gr/fuc/gpcgk110.fuc3.h | 537 ++++ .../drm/nouveau/nvkm/engine/gr/fuc/gpcgk208.fuc5 | 42 + .../drm/nouveau/nvkm/engine/gr/fuc/gpcgk208.fuc5.h | 473 +++ .../drm/nouveau/nvkm/engine/gr/fuc/gpcgm107.fuc5 | 42 + .../drm/nouveau/nvkm/engine/gr/fuc/gpcgm107.fuc5.h | 605 ++++ .../gpu/drm/nouveau/nvkm/engine/gr/fuc/hub.fuc | 696 ++++ .../drm/nouveau/nvkm/engine/gr/fuc/hubgf100.fuc3 | 40 + .../drm/nouveau/nvkm/engine/gr/fuc/hubgf100.fuc3.h | 1047 ++++++ .../drm/nouveau/nvkm/engine/gr/fuc/hubgf117.fuc3 | 40 + .../drm/nouveau/nvkm/engine/gr/fuc/hubgf117.fuc3.h | 1047 ++++++ .../drm/nouveau/nvkm/engine/gr/fuc/hubgk104.fuc3 | 40 + .../drm/nouveau/nvkm/engine/gr/fuc/hubgk104.fuc3.h | 1044 ++++++ .../drm/nouveau/nvkm/engine/gr/fuc/hubgk110.fuc3 | 40 + .../drm/nouveau/nvkm/engine/gr/fuc/hubgk110.fuc3.h | 1044 ++++++ .../drm/nouveau/nvkm/engine/gr/fuc/hubgk208.fuc5 | 40 + .../drm/nouveau/nvkm/engine/gr/fuc/hubgk208.fuc5.h | 916 ++++++ .../drm/nouveau/nvkm/engine/gr/fuc/hubgm107.fuc5 | 40 + .../drm/nouveau/nvkm/engine/gr/fuc/hubgm107.fuc5.h | 916 ++++++ .../gpu/drm/nouveau/nvkm/engine/gr/fuc/macros.fuc | 261 ++ .../gpu/drm/nouveau/nvkm/engine/gr/fuc/os.h | 8 + .../drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c | 1692 ++++++++++ .../drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.h | 266 ++ .../drivers/gpu/drm/nouveau/nvkm/engine/gr/gf104.c | 127 + .../drivers/gpu/drm/nouveau/nvkm/engine/gr/gf108.c | 134 + .../drivers/gpu/drm/nouveau/nvkm/engine/gr/gf110.c | 116 + .../drivers/gpu/drm/nouveau/nvkm/engine/gr/gf117.c | 136 + .../drivers/gpu/drm/nouveau/nvkm/engine/gr/gf119.c | 190 ++ .../drivers/gpu/drm/nouveau/nvkm/engine/gr/gk104.c | 359 +++ .../drivers/gpu/drm/nouveau/nvkm/engine/gr/gk110.c | 211 ++ .../gpu/drm/nouveau/nvkm/engine/gr/gk110b.c | 116 + .../drivers/gpu/drm/nouveau/nvkm/engine/gr/gk208.c | 190 ++ .../drivers/gpu/drm/nouveau/nvkm/engine/gr/gk20a.c | 49 + .../drivers/gpu/drm/nouveau/nvkm/engine/gr/gm107.c | 470 +++ .../drivers/gpu/drm/nouveau/nvkm/engine/gr/gm204.c | 386 +++ .../drivers/gpu/drm/nouveau/nvkm/engine/gr/gm206.c | 40 + .../drivers/gpu/drm/nouveau/nvkm/engine/gr/nv04.c | 1382 ++++++++ .../drivers/gpu/drm/nouveau/nvkm/engine/gr/nv10.c | 1315 ++++++++ .../drivers/gpu/drm/nouveau/nvkm/engine/gr/nv20.c | 376 +++ .../drivers/gpu/drm/nouveau/nvkm/engine/gr/nv20.h | 26 + .../drivers/gpu/drm/nouveau/nvkm/engine/gr/nv25.c | 158 + .../drivers/gpu/drm/nouveau/nvkm/engine/gr/nv2a.c | 125 + .../drivers/gpu/drm/nouveau/nvkm/engine/gr/nv30.c | 231 ++ .../drivers/gpu/drm/nouveau/nvkm/engine/gr/nv34.c | 159 + .../drivers/gpu/drm/nouveau/nvkm/engine/gr/nv35.c | 159 + .../drivers/gpu/drm/nouveau/nvkm/engine/gr/nv40.c | 527 +++ .../drivers/gpu/drm/nouveau/nvkm/engine/gr/nv40.h | 24 + .../drivers/gpu/drm/nouveau/nvkm/engine/gr/nv50.c | 999 ++++++ .../drivers/gpu/drm/nouveau/nvkm/engine/gr/nv50.h | 9 + .../drivers/gpu/drm/nouveau/nvkm/engine/gr/regs.h | 274 ++ .../gpu/drm/nouveau/nvkm/engine/mpeg/Kbuild | 5 + .../drivers/gpu/drm/nouveau/nvkm/engine/mpeg/g84.c | 94 + .../gpu/drm/nouveau/nvkm/engine/mpeg/nv31.c | 304 ++ .../gpu/drm/nouveau/nvkm/engine/mpeg/nv31.h | 13 + .../gpu/drm/nouveau/nvkm/engine/mpeg/nv40.c | 134 + .../gpu/drm/nouveau/nvkm/engine/mpeg/nv44.c | 185 ++ .../gpu/drm/nouveau/nvkm/engine/mpeg/nv50.c | 225 ++ .../gpu/drm/nouveau/nvkm/engine/mspdec/Kbuild | 3 + .../gpu/drm/nouveau/nvkm/engine/mspdec/g98.c | 109 + .../gpu/drm/nouveau/nvkm/engine/mspdec/gf100.c | 109 + .../gpu/drm/nouveau/nvkm/engine/mspdec/gk104.c | 109 + .../gpu/drm/nouveau/nvkm/engine/msppp/Kbuild | 2 + .../gpu/drm/nouveau/nvkm/engine/msppp/g98.c | 109 + .../gpu/drm/nouveau/nvkm/engine/msppp/gf100.c | 109 + .../gpu/drm/nouveau/nvkm/engine/msvld/Kbuild | 3 + .../gpu/drm/nouveau/nvkm/engine/msvld/g98.c | 110 + .../gpu/drm/nouveau/nvkm/engine/msvld/gf100.c | 109 + .../gpu/drm/nouveau/nvkm/engine/msvld/gk104.c | 109 + .../drivers/gpu/drm/nouveau/nvkm/engine/pm/Kbuild | 9 + .../drivers/gpu/drm/nouveau/nvkm/engine/pm/base.c | 476 +++ .../gpu/drm/nouveau/nvkm/engine/pm/daemon.c | 108 + .../drivers/gpu/drm/nouveau/nvkm/engine/pm/g84.c | 65 + .../drivers/gpu/drm/nouveau/nvkm/engine/pm/gf100.c | 159 + .../drivers/gpu/drm/nouveau/nvkm/engine/pm/gf100.h | 15 + .../drivers/gpu/drm/nouveau/nvkm/engine/pm/gk104.c | 148 + .../drivers/gpu/drm/nouveau/nvkm/engine/pm/gk110.c | 57 + .../drivers/gpu/drm/nouveau/nvkm/engine/pm/gt215.c | 83 + .../drivers/gpu/drm/nouveau/nvkm/engine/pm/nv40.c | 130 + .../drivers/gpu/drm/nouveau/nvkm/engine/pm/nv40.h | 24 + .../drivers/gpu/drm/nouveau/nvkm/engine/pm/nv50.c | 57 + .../drivers/gpu/drm/nouveau/nvkm/engine/pm/priv.h | 90 + .../drivers/gpu/drm/nouveau/nvkm/engine/sec/Kbuild | 1 + .../gpu/drm/nouveau/nvkm/engine/sec/fuc/g98.fuc0s | 698 ++++ .../drm/nouveau/nvkm/engine/sec/fuc/g98.fuc0s.h | 584 ++++ .../drivers/gpu/drm/nouveau/nvkm/engine/sec/g98.c | 149 + .../drivers/gpu/drm/nouveau/nvkm/engine/sw/Kbuild | 4 + .../drivers/gpu/drm/nouveau/nvkm/engine/sw/gf100.c | 141 + .../drivers/gpu/drm/nouveau/nvkm/engine/sw/nv04.c | 139 + .../drivers/gpu/drm/nouveau/nvkm/engine/sw/nv10.c | 122 + .../drivers/gpu/drm/nouveau/nvkm/engine/sw/nv50.c | 234 ++ .../drivers/gpu/drm/nouveau/nvkm/engine/sw/nv50.h | 45 + .../drivers/gpu/drm/nouveau/nvkm/engine/vp/Kbuild | 1 + .../drivers/gpu/drm/nouveau/nvkm/engine/vp/g84.c | 93 + .../drivers/gpu/drm/nouveau/nvkm/engine/xtensa.c | 172 + kernel/drivers/gpu/drm/nouveau/nvkm/subdev/Kbuild | 19 + .../drivers/gpu/drm/nouveau/nvkm/subdev/bar/Kbuild | 4 + .../drivers/gpu/drm/nouveau/nvkm/subdev/bar/base.c | 144 + .../gpu/drm/nouveau/nvkm/subdev/bar/gf100.c | 219 ++ .../gpu/drm/nouveau/nvkm/subdev/bar/gk20a.c | 50 + .../drivers/gpu/drm/nouveau/nvkm/subdev/bar/nv50.c | 271 ++ .../drivers/gpu/drm/nouveau/nvkm/subdev/bar/priv.h | 30 + .../gpu/drm/nouveau/nvkm/subdev/bios/Kbuild | 37 + .../gpu/drm/nouveau/nvkm/subdev/bios/M0203.c | 128 + .../gpu/drm/nouveau/nvkm/subdev/bios/M0205.c | 135 + .../gpu/drm/nouveau/nvkm/subdev/bios/M0209.c | 135 + .../gpu/drm/nouveau/nvkm/subdev/bios/P0260.c | 107 + .../gpu/drm/nouveau/nvkm/subdev/bios/base.c | 206 ++ .../drivers/gpu/drm/nouveau/nvkm/subdev/bios/bit.c | 49 + .../gpu/drm/nouveau/nvkm/subdev/bios/boost.c | 126 + .../gpu/drm/nouveau/nvkm/subdev/bios/conn.c | 97 + .../gpu/drm/nouveau/nvkm/subdev/bios/cstep.c | 122 + .../drivers/gpu/drm/nouveau/nvkm/subdev/bios/dcb.c | 234 ++ .../gpu/drm/nouveau/nvkm/subdev/bios/disp.c | 172 + .../drivers/gpu/drm/nouveau/nvkm/subdev/bios/dp.c | 215 ++ .../gpu/drm/nouveau/nvkm/subdev/bios/extdev.c | 97 + .../drivers/gpu/drm/nouveau/nvkm/subdev/bios/fan.c | 93 + .../gpu/drm/nouveau/nvkm/subdev/bios/gpio.c | 150 + .../drivers/gpu/drm/nouveau/nvkm/subdev/bios/i2c.c | 163 + .../gpu/drm/nouveau/nvkm/subdev/bios/image.c | 77 + .../gpu/drm/nouveau/nvkm/subdev/bios/init.c | 2247 +++++++++++++ .../drivers/gpu/drm/nouveau/nvkm/subdev/bios/mxm.c | 134 + .../gpu/drm/nouveau/nvkm/subdev/bios/npde.c | 58 + .../gpu/drm/nouveau/nvkm/subdev/bios/pcir.c | 68 + .../gpu/drm/nouveau/nvkm/subdev/bios/perf.c | 201 ++ .../drivers/gpu/drm/nouveau/nvkm/subdev/bios/pll.c | 417 +++ .../drivers/gpu/drm/nouveau/nvkm/subdev/bios/pmu.c | 134 + .../gpu/drm/nouveau/nvkm/subdev/bios/priv.h | 23 + .../gpu/drm/nouveau/nvkm/subdev/bios/ramcfg.c | 78 + .../gpu/drm/nouveau/nvkm/subdev/bios/rammap.c | 211 ++ .../gpu/drm/nouveau/nvkm/subdev/bios/shadow.c | 272 ++ .../gpu/drm/nouveau/nvkm/subdev/bios/shadowacpi.c | 112 + .../gpu/drm/nouveau/nvkm/subdev/bios/shadowof.c | 72 + .../gpu/drm/nouveau/nvkm/subdev/bios/shadowpci.c | 109 + .../gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c | 115 + .../gpu/drm/nouveau/nvkm/subdev/bios/shadowrom.c | 70 + .../gpu/drm/nouveau/nvkm/subdev/bios/therm.c | 214 ++ .../gpu/drm/nouveau/nvkm/subdev/bios/timing.c | 166 + .../gpu/drm/nouveau/nvkm/subdev/bios/vmap.c | 111 + .../gpu/drm/nouveau/nvkm/subdev/bios/volt.c | 136 + .../gpu/drm/nouveau/nvkm/subdev/bios/xpio.c | 74 + .../drivers/gpu/drm/nouveau/nvkm/subdev/bus/Kbuild | 6 + .../drivers/gpu/drm/nouveau/nvkm/subdev/bus/g94.c | 58 + .../gpu/drm/nouveau/nvkm/subdev/bus/gf100.c | 80 + .../drivers/gpu/drm/nouveau/nvkm/subdev/bus/hwsq.c | 143 + .../drivers/gpu/drm/nouveau/nvkm/subdev/bus/hwsq.h | 141 + .../drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv04.c | 94 + .../drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv04.h | 21 + .../drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv31.c | 91 + .../drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv50.c | 104 + .../drivers/gpu/drm/nouveau/nvkm/subdev/clk/Kbuild | 12 + .../drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c | 591 ++++ .../drivers/gpu/drm/nouveau/nvkm/subdev/clk/g84.c | 47 + .../gpu/drm/nouveau/nvkm/subdev/clk/gf100.c | 462 +++ .../gpu/drm/nouveau/nvkm/subdev/clk/gk104.c | 500 +++ .../gpu/drm/nouveau/nvkm/subdev/clk/gk20a.c | 680 ++++ .../gpu/drm/nouveau/nvkm/subdev/clk/gt215.c | 533 ++++ .../gpu/drm/nouveau/nvkm/subdev/clk/gt215.h | 18 + .../gpu/drm/nouveau/nvkm/subdev/clk/mcp77.c | 429 +++ .../drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv04.c | 103 + .../drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv40.c | 241 ++ .../drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv50.c | 561 ++++ .../drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv50.h | 28 + .../drivers/gpu/drm/nouveau/nvkm/subdev/clk/pll.h | 11 + .../gpu/drm/nouveau/nvkm/subdev/clk/pllgt215.c | 87 + .../gpu/drm/nouveau/nvkm/subdev/clk/pllnv04.c | 245 ++ .../drivers/gpu/drm/nouveau/nvkm/subdev/clk/seq.h | 14 + .../gpu/drm/nouveau/nvkm/subdev/devinit/Kbuild | 14 + .../gpu/drm/nouveau/nvkm/subdev/devinit/base.c | 96 + .../gpu/drm/nouveau/nvkm/subdev/devinit/fbmem.h | 84 + .../gpu/drm/nouveau/nvkm/subdev/devinit/g84.c | 66 + .../gpu/drm/nouveau/nvkm/subdev/devinit/g98.c | 65 + .../gpu/drm/nouveau/nvkm/subdev/devinit/gf100.c | 127 + .../gpu/drm/nouveau/nvkm/subdev/devinit/gm107.c | 59 + .../gpu/drm/nouveau/nvkm/subdev/devinit/gm204.c | 172 + .../gpu/drm/nouveau/nvkm/subdev/devinit/gt215.c | 150 + .../gpu/drm/nouveau/nvkm/subdev/devinit/mcp89.c | 66 + .../gpu/drm/nouveau/nvkm/subdev/devinit/nv04.c | 470 +++ .../gpu/drm/nouveau/nvkm/subdev/devinit/nv04.h | 22 + .../gpu/drm/nouveau/nvkm/subdev/devinit/nv05.c | 140 + .../gpu/drm/nouveau/nvkm/subdev/devinit/nv10.c | 111 + .../gpu/drm/nouveau/nvkm/subdev/devinit/nv1a.c | 40 + .../gpu/drm/nouveau/nvkm/subdev/devinit/nv20.c | 77 + .../gpu/drm/nouveau/nvkm/subdev/devinit/nv50.c | 174 + .../gpu/drm/nouveau/nvkm/subdev/devinit/nv50.h | 24 + .../gpu/drm/nouveau/nvkm/subdev/devinit/priv.h | 34 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/Kbuild | 44 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/base.c | 165 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/g84.c | 38 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr3.c | 115 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr5.c | 120 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.c | 122 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.h | 28 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk104.c | 37 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk20a.c | 68 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/gm107.c | 37 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/gt215.c | 38 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/mcp77.c | 38 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/mcp89.c | 38 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv04.c | 87 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv04.h | 53 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv10.c | 70 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv1a.c | 43 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv20.c | 94 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv25.c | 60 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv30.c | 140 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv35.c | 61 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv36.c | 61 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv40.c | 75 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv40.h | 14 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv41.c | 68 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv44.c | 77 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv46.c | 57 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv47.c | 44 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv49.c | 44 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv4e.c | 43 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.c | 320 ++ .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.h | 31 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/priv.h | 73 + .../gpu/drm/nouveau/nvkm/subdev/fb/ramfuc.h | 180 ++ .../gpu/drm/nouveau/nvkm/subdev/fb/ramgf100.c | 731 +++++ .../gpu/drm/nouveau/nvkm/subdev/fb/ramgk104.c | 1639 ++++++++++ .../gpu/drm/nouveau/nvkm/subdev/fb/ramgm107.c | 55 + .../gpu/drm/nouveau/nvkm/subdev/fb/ramgt215.c | 1012 ++++++ .../gpu/drm/nouveau/nvkm/subdev/fb/rammcp77.c | 101 + .../gpu/drm/nouveau/nvkm/subdev/fb/ramnv04.c | 79 + .../gpu/drm/nouveau/nvkm/subdev/fb/ramnv10.c | 59 + .../gpu/drm/nouveau/nvkm/subdev/fb/ramnv1a.c | 72 + .../gpu/drm/nouveau/nvkm/subdev/fb/ramnv20.c | 62 + .../gpu/drm/nouveau/nvkm/subdev/fb/ramnv40.c | 212 ++ .../gpu/drm/nouveau/nvkm/subdev/fb/ramnv41.c | 66 + .../gpu/drm/nouveau/nvkm/subdev/fb/ramnv44.c | 64 + .../gpu/drm/nouveau/nvkm/subdev/fb/ramnv49.c | 66 + .../gpu/drm/nouveau/nvkm/subdev/fb/ramnv4e.c | 54 + .../gpu/drm/nouveau/nvkm/subdev/fb/ramnv50.c | 465 +++ .../gpu/drm/nouveau/nvkm/subdev/fb/ramseq.h | 15 + .../gpu/drm/nouveau/nvkm/subdev/fb/regsnv04.h | 22 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/sddr2.c | 93 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/sddr3.c | 119 + .../gpu/drm/nouveau/nvkm/subdev/fuse/Kbuild | 4 + .../gpu/drm/nouveau/nvkm/subdev/fuse/base.c | 51 + .../gpu/drm/nouveau/nvkm/subdev/fuse/gf100.c | 78 + .../gpu/drm/nouveau/nvkm/subdev/fuse/gm107.c | 62 + .../gpu/drm/nouveau/nvkm/subdev/fuse/nv50.c | 76 + .../gpu/drm/nouveau/nvkm/subdev/fuse/priv.h | 7 + .../gpu/drm/nouveau/nvkm/subdev/gpio/Kbuild | 6 + .../gpu/drm/nouveau/nvkm/subdev/gpio/base.c | 251 ++ .../drivers/gpu/drm/nouveau/nvkm/subdev/gpio/g94.c | 73 + .../gpu/drm/nouveau/nvkm/subdev/gpio/gf110.c | 84 + .../gpu/drm/nouveau/nvkm/subdev/gpio/gk104.c | 73 + .../gpu/drm/nouveau/nvkm/subdev/gpio/nv10.c | 115 + .../gpu/drm/nouveau/nvkm/subdev/gpio/nv50.c | 128 + .../gpu/drm/nouveau/nvkm/subdev/gpio/priv.h | 64 + .../drivers/gpu/drm/nouveau/nvkm/subdev/i2c/Kbuild | 16 + .../gpu/drm/nouveau/nvkm/subdev/i2c/anx9805.c | 292 ++ .../drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c | 113 + .../drivers/gpu/drm/nouveau/nvkm/subdev/i2c/base.c | 622 ++++ .../drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bit.c | 233 ++ .../drivers/gpu/drm/nouveau/nvkm/subdev/i2c/g94.c | 279 ++ .../gpu/drm/nouveau/nvkm/subdev/i2c/gf110.c | 106 + .../gpu/drm/nouveau/nvkm/subdev/i2c/gf117.c | 38 + .../gpu/drm/nouveau/nvkm/subdev/i2c/gk104.c | 71 + .../gpu/drm/nouveau/nvkm/subdev/i2c/gm204.c | 219 ++ .../drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv04.c | 128 + .../drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv4e.c | 120 + .../drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv50.c | 133 + .../drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv50.h | 32 + .../drivers/gpu/drm/nouveau/nvkm/subdev/i2c/pad.c | 83 + .../drivers/gpu/drm/nouveau/nvkm/subdev/i2c/pad.h | 56 + .../gpu/drm/nouveau/nvkm/subdev/i2c/padg94.c | 85 + .../gpu/drm/nouveau/nvkm/subdev/i2c/padgm204.c | 85 + .../gpu/drm/nouveau/nvkm/subdev/i2c/padnv04.c | 34 + .../drivers/gpu/drm/nouveau/nvkm/subdev/i2c/port.h | 13 + .../drivers/gpu/drm/nouveau/nvkm/subdev/i2c/priv.h | 87 + .../gpu/drm/nouveau/nvkm/subdev/ibus/Kbuild | 3 + .../gpu/drm/nouveau/nvkm/subdev/ibus/gf100.c | 122 + .../gpu/drm/nouveau/nvkm/subdev/ibus/gk104.c | 139 + .../gpu/drm/nouveau/nvkm/subdev/ibus/gk20a.c | 102 + .../gpu/drm/nouveau/nvkm/subdev/instmem/Kbuild | 5 + .../gpu/drm/nouveau/nvkm/subdev/instmem/base.c | 146 + .../gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c | 440 +++ .../gpu/drm/nouveau/nvkm/subdev/instmem/nv04.c | 185 ++ .../gpu/drm/nouveau/nvkm/subdev/instmem/nv04.h | 36 + .../gpu/drm/nouveau/nvkm/subdev/instmem/nv40.c | 136 + .../gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c | 169 + .../gpu/drm/nouveau/nvkm/subdev/instmem/priv.h | 54 + .../drivers/gpu/drm/nouveau/nvkm/subdev/ltc/Kbuild | 4 + .../drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c | 124 + .../gpu/drm/nouveau/nvkm/subdev/ltc/gf100.c | 244 ++ .../gpu/drm/nouveau/nvkm/subdev/ltc/gk104.c | 59 + .../gpu/drm/nouveau/nvkm/subdev/ltc/gm107.c | 153 + .../drivers/gpu/drm/nouveau/nvkm/subdev/ltc/priv.h | 69 + .../drivers/gpu/drm/nouveau/nvkm/subdev/mc/Kbuild | 11 + .../drivers/gpu/drm/nouveau/nvkm/subdev/mc/base.c | 169 + .../drivers/gpu/drm/nouveau/nvkm/subdev/mc/g94.c | 37 + .../drivers/gpu/drm/nouveau/nvkm/subdev/mc/g98.c | 58 + .../drivers/gpu/drm/nouveau/nvkm/subdev/mc/gf100.c | 76 + .../drivers/gpu/drm/nouveau/nvkm/subdev/mc/gf106.c | 38 + .../drivers/gpu/drm/nouveau/nvkm/subdev/mc/gk20a.c | 37 + .../drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv04.c | 78 + .../drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv04.h | 20 + .../drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv40.c | 44 + .../drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv44.c | 53 + .../drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv4c.c | 36 + .../drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv50.c | 72 + .../drivers/gpu/drm/nouveau/nvkm/subdev/mc/priv.h | 36 + .../drivers/gpu/drm/nouveau/nvkm/subdev/mmu/Kbuild | 6 + .../drivers/gpu/drm/nouveau/nvkm/subdev/mmu/base.c | 480 +++ .../gpu/drm/nouveau/nvkm/subdev/mmu/gf100.c | 237 ++ .../drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv04.c | 151 + .../drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv04.h | 19 + .../drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv41.c | 157 + .../drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv44.c | 247 ++ .../drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv50.c | 241 ++ .../drivers/gpu/drm/nouveau/nvkm/subdev/mxm/Kbuild | 3 + .../drivers/gpu/drm/nouveau/nvkm/subdev/mxm/base.c | 271 ++ .../drivers/gpu/drm/nouveau/nvkm/subdev/mxm/mxms.c | 191 ++ .../drivers/gpu/drm/nouveau/nvkm/subdev/mxm/mxms.h | 22 + .../drivers/gpu/drm/nouveau/nvkm/subdev/mxm/nv50.c | 231 ++ .../drivers/gpu/drm/nouveau/nvkm/subdev/pmu/Kbuild | 9 + .../drivers/gpu/drm/nouveau/nvkm/subdev/pmu/base.c | 268 ++ .../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 | 1865 +++++++++++ .../gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf110.fuc4 | 70 + .../drm/nouveau/nvkm/subdev/pmu/fuc/gf110.fuc4.h | 1795 +++++++++++ .../gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gk208.fuc5 | 70 + .../drm/nouveau/nvkm/subdev/pmu/fuc/gk208.fuc5.h | 1731 ++++++++++ .../gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gt215.fuc3 | 70 + .../drm/nouveau/nvkm/subdev/pmu/fuc/gt215.fuc3.h | 1868 +++++++++++ .../gpu/drm/nouveau/nvkm/subdev/pmu/fuc/host.fuc | 151 + .../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 | 556 ++++ .../gpu/drm/nouveau/nvkm/subdev/pmu/fuc/macros.fuc | 272 ++ .../gpu/drm/nouveau/nvkm/subdev/pmu/fuc/memx.fuc | 447 +++ .../gpu/drm/nouveau/nvkm/subdev/pmu/fuc/os.h | 52 + .../gpu/drm/nouveau/nvkm/subdev/pmu/fuc/perf.fuc | 57 + .../gpu/drm/nouveau/nvkm/subdev/pmu/fuc/test.fuc | 64 + .../gpu/drm/nouveau/nvkm/subdev/pmu/gf100.c | 40 + .../gpu/drm/nouveau/nvkm/subdev/pmu/gf110.c | 40 + .../gpu/drm/nouveau/nvkm/subdev/pmu/gk104.c | 67 + .../gpu/drm/nouveau/nvkm/subdev/pmu/gk110.c | 95 + .../gpu/drm/nouveau/nvkm/subdev/pmu/gk208.c | 41 + .../gpu/drm/nouveau/nvkm/subdev/pmu/gk20a.c | 230 ++ .../gpu/drm/nouveau/nvkm/subdev/pmu/gt215.c | 49 + .../drivers/gpu/drm/nouveau/nvkm/subdev/pmu/memx.c | 200 ++ .../drivers/gpu/drm/nouveau/nvkm/subdev/pmu/priv.h | 45 + .../gpu/drm/nouveau/nvkm/subdev/therm/Kbuild | 13 + .../gpu/drm/nouveau/nvkm/subdev/therm/base.c | 367 +++ .../gpu/drm/nouveau/nvkm/subdev/therm/fan.c | 282 ++ .../gpu/drm/nouveau/nvkm/subdev/therm/fannil.c | 53 + .../gpu/drm/nouveau/nvkm/subdev/therm/fanpwm.c | 113 + .../gpu/drm/nouveau/nvkm/subdev/therm/fantog.c | 118 + .../gpu/drm/nouveau/nvkm/subdev/therm/g84.c | 266 ++ .../gpu/drm/nouveau/nvkm/subdev/therm/gf110.c | 174 + .../gpu/drm/nouveau/nvkm/subdev/therm/gm107.c | 93 + .../gpu/drm/nouveau/nvkm/subdev/therm/gt215.c | 100 + .../drivers/gpu/drm/nouveau/nvkm/subdev/therm/ic.c | 119 + .../gpu/drm/nouveau/nvkm/subdev/therm/nv40.c | 225 ++ .../gpu/drm/nouveau/nvkm/subdev/therm/nv50.c | 198 ++ .../gpu/drm/nouveau/nvkm/subdev/therm/priv.h | 153 + .../gpu/drm/nouveau/nvkm/subdev/therm/temp.c | 259 ++ .../gpu/drm/nouveau/nvkm/subdev/timer/Kbuild | 3 + .../gpu/drm/nouveau/nvkm/subdev/timer/base.c | 93 + .../gpu/drm/nouveau/nvkm/subdev/timer/gk20a.c | 56 + .../gpu/drm/nouveau/nvkm/subdev/timer/nv04.c | 262 ++ .../gpu/drm/nouveau/nvkm/subdev/timer/nv04.h | 25 + .../gpu/drm/nouveau/nvkm/subdev/timer/priv.h | 4 + .../gpu/drm/nouveau/nvkm/subdev/volt/Kbuild | 4 + .../gpu/drm/nouveau/nvkm/subdev/volt/base.c | 204 ++ .../gpu/drm/nouveau/nvkm/subdev/volt/gk20a.c | 197 ++ .../gpu/drm/nouveau/nvkm/subdev/volt/gpio.c | 96 + .../gpu/drm/nouveau/nvkm/subdev/volt/nv40.c | 55 + 695 files changed, 145050 insertions(+) create mode 100644 kernel/drivers/gpu/drm/nouveau/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/Kconfig create mode 100644 kernel/drivers/gpu/drm/nouveau/dispnv04/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/dispnv04/arb.c create mode 100644 kernel/drivers/gpu/drm/nouveau/dispnv04/crtc.c create mode 100644 kernel/drivers/gpu/drm/nouveau/dispnv04/cursor.c create mode 100644 kernel/drivers/gpu/drm/nouveau/dispnv04/dac.c create mode 100644 kernel/drivers/gpu/drm/nouveau/dispnv04/dfp.c create mode 100644 kernel/drivers/gpu/drm/nouveau/dispnv04/disp.c create mode 100644 kernel/drivers/gpu/drm/nouveau/dispnv04/disp.h create mode 100644 kernel/drivers/gpu/drm/nouveau/dispnv04/hw.c create mode 100644 kernel/drivers/gpu/drm/nouveau/dispnv04/hw.h create mode 100644 kernel/drivers/gpu/drm/nouveau/dispnv04/nvreg.h create mode 100644 kernel/drivers/gpu/drm/nouveau/dispnv04/overlay.c create mode 100644 kernel/drivers/gpu/drm/nouveau/dispnv04/tvmodesnv17.c create mode 100644 kernel/drivers/gpu/drm/nouveau/dispnv04/tvnv04.c create mode 100644 kernel/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c create mode 100644 kernel/drivers/gpu/drm/nouveau/dispnv04/tvnv17.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvif/class.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvif/client.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvif/device.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvif/driver.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvif/event.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvif/ioctl.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvif/list.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvif/notify.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvif/object.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvif/os.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvif/unpack.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/core/client.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/core/debug.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/core/device.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/core/devidx.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/core/engctx.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/core/engine.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/core/enum.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/core/event.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/core/gpuobj.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/core/handle.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/core/ioctl.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/core/mm.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/core/namedb.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/core/notify.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/core/object.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/core/option.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/core/os.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/core/parent.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/core/printk.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/core/ramht.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/core/subdev.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/bsp.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/ce.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/cipher.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/device.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/disp.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/dmaobj.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/falcon.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/fifo.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/gr.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/mpeg.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/mspdec.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/msppp.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/msvld.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/pm.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/sec.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/sw.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/vp.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/xtensa.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bar.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/M0203.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/M0205.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/M0209.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/P0260.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/bit.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/bmp.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/boost.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/conn.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/cstep.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/dcb.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/disp.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/dp.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/extdev.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/fan.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/gpio.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/i2c.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/image.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/init.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/mxm.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/npde.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/pcir.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/perf.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/pll.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/pmu.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/ramcfg.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/rammap.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/therm.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/timing.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/vmap.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/volt.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/xpio.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bus.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/clk.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/devinit.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/fb.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/fuse.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/gpio.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/i2c.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/ibus.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/instmem.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/ltc.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/mc.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/mmu.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/mxm.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/pmu.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/therm.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/timer.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/vga.h create mode 100644 kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/volt.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_abi16.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_abi16.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_acpi.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_acpi.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_agp.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_agp.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_backlight.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_bios.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_bios.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_bo.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_bo.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_chan.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_chan.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_connector.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_connector.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_crtc.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_debugfs.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_debugfs.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_display.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_display.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_dma.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_dma.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_dp.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_drm.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_drm.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_encoder.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_fbcon.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_fbcon.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_fence.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_fence.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_gem.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_gem.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_hwmon.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_hwmon.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_ioc32.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_ioctl.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_nvif.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_platform.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_platform.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_prime.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_reg.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_sgdma.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_sysfs.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_sysfs.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_ttm.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_ttm.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_usif.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_usif.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_vga.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nouveau_vga.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nv04_fbcon.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nv04_fence.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nv10_fence.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nv10_fence.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nv17_fence.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nv50_display.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nv50_display.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nv50_fbcon.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nv50_fence.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nv84_fence.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvc0_fbcon.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvc0_fence.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvif/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvif/client.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvif/device.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvif/notify.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvif/object.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/core/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/core/client.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/core/engctx.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/core/engine.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/core/enum.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/core/event.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/core/gpuobj.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/core/handle.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/core/ioctl.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/core/mm.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/core/namedb.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/core/notify.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/core/object.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/core/option.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/core/parent.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/core/printk.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/core/ramht.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/core/subdev.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/bsp/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/bsp/g84.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/com.fuc create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/gf100.fuc3 create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/gf100.fuc3.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/gt215.fuc3 create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/gt215.fuc3.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/gf100.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/gk104.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/gm204.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/gt215.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/cipher/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/cipher/g84.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/acpi.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/acpi.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/ctrl.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/gf100.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/gk104.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/gm100.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/nv04.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/nv10.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/nv20.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/nv30.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/nv40.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/nv50.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/priv.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/dacnv50.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/dport.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/dport.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/g84.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/g94.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf110.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk104.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk110.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm107.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm204.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt200.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt215.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagf110.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf110.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv04.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/outpdp.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/outpdp.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/piornv50.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf110.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm204.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/sornv50.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/vga.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/dmaobj/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/dmaobj/base.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/dmaobj/gf100.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/dmaobj/gf110.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/dmaobj/nv04.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/dmaobj/nv50.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/dmaobj/priv.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/falcon.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/base.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/g84.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gf100.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk208.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk20a.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gm204.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv04.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv04.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv10.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv17.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv40.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv50.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv50.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf100.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf100.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf104.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf108.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf110.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf117.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf119.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk104.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk110.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk110b.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk208.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk20a.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgm107.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgm204.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgm206.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxnv40.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxnv40.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxnv50.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/com.fuc create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpc.fuc create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgf100.fuc3 create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgf100.fuc3.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgf117.fuc3 create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgf117.fuc3.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk104.fuc3 create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk104.fuc3.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk110.fuc3 create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk110.fuc3.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk208.fuc5 create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk208.fuc5.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgm107.fuc5 create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgm107.fuc5.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hub.fuc create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgf100.fuc3 create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgf100.fuc3.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgf117.fuc3 create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgf117.fuc3.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk104.fuc3 create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk104.fuc3.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk110.fuc3 create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk110.fuc3.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk208.fuc5 create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk208.fuc5.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgm107.fuc5 create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgm107.fuc5.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/macros.fuc create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/os.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf104.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf108.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf110.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf117.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf119.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk104.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk110.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk110b.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk208.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk20a.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gm107.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gm204.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gm206.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv04.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv10.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv20.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv20.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv25.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv2a.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv30.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv34.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv35.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv40.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv40.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv50.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv50.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/regs.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/g84.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv31.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv31.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv40.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv44.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv50.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/mspdec/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/mspdec/g98.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/mspdec/gf100.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/mspdec/gk104.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/msppp/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/msppp/g98.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/msppp/gf100.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/msvld/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/msvld/g98.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/msvld/gf100.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/msvld/gk104.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/base.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/daemon.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/g84.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/gf100.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/gf100.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/gk104.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/gk110.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/gt215.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/nv40.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/nv40.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/nv50.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/priv.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/sec/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/sec/fuc/g98.fuc0s create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/sec/fuc/g98.fuc0s.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/sec/g98.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/sw/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/sw/gf100.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv04.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv10.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv50.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv50.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/vp/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/vp/g84.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/engine/xtensa.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bar/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bar/base.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gk20a.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bar/nv50.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bar/priv.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/M0203.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/M0205.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/M0209.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/P0260.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/base.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/bit.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/boost.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/conn.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/cstep.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/dcb.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/disp.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/dp.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/extdev.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/fan.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/gpio.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/i2c.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/image.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/mxm.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/npde.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/pcir.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/perf.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/pll.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/pmu.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/ramcfg.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/rammap.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowacpi.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowof.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowpci.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowrom.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/therm.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/timing.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/vmap.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/volt.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/xpio.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bus/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bus/g94.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bus/gf100.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bus/hwsq.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bus/hwsq.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv04.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv04.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv31.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv50.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/g84.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gf100.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk104.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gt215.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gt215.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/mcp77.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv04.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv40.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv50.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv50.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/pll.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/pllgt215.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/pllnv04.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/seq.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/base.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/fbmem.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/g84.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/g98.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gf100.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm107.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm204.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gt215.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/mcp89.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv05.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv10.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv1a.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv20.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/priv.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/base.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/g84.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr3.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr5.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk104.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk20a.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gm107.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gt215.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/mcp77.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/mcp89.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv04.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv04.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv10.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv1a.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv20.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv25.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv30.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv35.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv36.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv40.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv40.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv41.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv44.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv46.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv47.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv49.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv4e.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/priv.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramfuc.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgf100.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgk104.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgm107.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgt215.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/rammcp77.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv04.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv10.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv1a.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv20.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv40.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv41.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv44.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv49.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv4e.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv50.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramseq.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/regsnv04.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/sddr2.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/sddr3.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/base.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/gf100.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/gm107.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/nv50.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/priv.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/base.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/g94.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/gf110.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/gk104.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/nv10.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/nv50.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/priv.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/anx9805.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/base.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bit.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/g94.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gf110.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gf117.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gk104.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gm204.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv04.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv4e.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv50.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv50.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/pad.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/pad.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padg94.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padgm204.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padnv04.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/port.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/priv.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ibus/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ibus/gf100.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ibus/gk104.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ibus/gk20a.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/base.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv04.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv04.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv40.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/priv.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gf100.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gk104.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gm107.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/priv.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/base.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/g94.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/g98.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gf100.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gf106.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gk20a.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv04.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv04.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv40.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv44.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv4c.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv50.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/priv.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/base.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gf100.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv04.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv04.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv41.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv44.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv50.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/base.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/mxms.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/mxms.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/nv50.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/base.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/arith.fuc create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf100.fuc3 create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf100.fuc3.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf110.fuc4 create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf110.fuc4.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gk208.fuc5 create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gk208.fuc5.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gt215.fuc3 create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gt215.fuc3.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/host.fuc create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/i2c_.fuc create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/idle.fuc create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/kernel.fuc create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/macros.fuc create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/memx.fuc create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/os.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/perf.fuc create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/test.fuc create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gf100.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gf110.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk104.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk110.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk208.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk20a.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gt215.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/memx.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/priv.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fannil.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fanpwm.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/g84.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gf110.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gm107.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gt215.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/ic.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv40.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv50.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/priv.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/timer/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/timer/gk20a.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/timer/priv.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/volt/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/volt/base.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gk20a.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gpio.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/volt/nv40.c (limited to 'kernel/drivers/gpu/drm/nouveau') diff --git a/kernel/drivers/gpu/drm/nouveau/Kbuild b/kernel/drivers/gpu/drm/nouveau/Kbuild new file mode 100644 index 000000000..2b765663c --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/Kbuild @@ -0,0 +1,66 @@ +ccflags-y := -Iinclude/drm +ccflags-y += -I$(src)/include +ccflags-y += -I$(src)/include/nvkm +ccflags-y += -I$(src)/nvkm +ccflags-y += -I$(src) + +# NVKM - HW resource manager +#- code also used by various userspace tools/tests +include $(src)/nvif/Kbuild +nouveau-y := $(nvif-y) + +# NVIF - NVKM interface library (NVKM user interface also defined here) +#- code also used by various userspace tools/tests +include $(src)/nvkm/Kbuild +nouveau-y += $(nvkm-y) + +# DRM - general +ifdef CONFIG_X86 +nouveau-$(CONFIG_ACPI) += nouveau_acpi.o +endif +nouveau-y += nouveau_agp.o +nouveau-$(CONFIG_DEBUG_FS) += nouveau_debugfs.o +nouveau-y += nouveau_drm.o +nouveau-y += nouveau_hwmon.o +nouveau-$(CONFIG_COMPAT) += nouveau_ioc32.o +nouveau-y += nouveau_nvif.o +nouveau-$(CONFIG_NOUVEAU_PLATFORM_DRIVER) += nouveau_platform.o +nouveau-y += nouveau_sysfs.o +nouveau-y += nouveau_usif.o # userspace <-> nvif +nouveau-y += nouveau_vga.o + +# DRM - memory management +nouveau-y += nouveau_bo.o +nouveau-y += nouveau_gem.o +nouveau-y += nouveau_prime.o +nouveau-y += nouveau_sgdma.o +nouveau-y += nouveau_ttm.o + +# DRM - modesetting +nouveau-$(CONFIG_DRM_NOUVEAU_BACKLIGHT) += nouveau_backlight.o +nouveau-y += nouveau_connector.o +nouveau-y += nouveau_display.o +nouveau-y += nv50_display.o +nouveau-y += nouveau_dp.o +nouveau-y += nouveau_fbcon.o +nouveau-y += nv04_fbcon.o +nouveau-y += nv50_fbcon.o +nouveau-y += nvc0_fbcon.o + +# DRM - command submission +nouveau-y += nouveau_abi16.o +nouveau-y += nouveau_chan.o +nouveau-y += nouveau_dma.o +nouveau-y += nouveau_fence.o +nouveau-y += nv04_fence.o +nouveau-y += nv10_fence.o +nouveau-y += nv17_fence.o +nouveau-y += nv50_fence.o +nouveau-y += nv84_fence.o +nouveau-y += nvc0_fence.o + +# DRM - prehistoric modesetting (NV04-G7x) +nouveau-y += nouveau_bios.o +include $(src)/dispnv04/Kbuild + +obj-$(CONFIG_DRM_NOUVEAU) += nouveau.o diff --git a/kernel/drivers/gpu/drm/nouveau/Kconfig b/kernel/drivers/gpu/drm/nouveau/Kconfig new file mode 100644 index 000000000..5ab13e793 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/Kconfig @@ -0,0 +1,70 @@ +config DRM_NOUVEAU + tristate "Nouveau (NVIDIA) cards" + depends on DRM && PCI + select FW_LOADER + select DRM_KMS_HELPER + select DRM_KMS_FB_HELPER + select DRM_TTM + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + select FB + select FRAMEBUFFER_CONSOLE if !EXPERT + select FB_BACKLIGHT if DRM_NOUVEAU_BACKLIGHT + select ACPI_VIDEO if ACPI && X86 && BACKLIGHT_CLASS_DEVICE && INPUT + select X86_PLATFORM_DEVICES if ACPI && X86 + select ACPI_WMI if ACPI && X86 + select MXM_WMI if ACPI && X86 + select POWER_SUPPLY + # Similar to i915, we need to select ACPI_VIDEO and it's dependencies + select BACKLIGHT_LCD_SUPPORT if ACPI && X86 + select BACKLIGHT_CLASS_DEVICE if ACPI && X86 + select INPUT if ACPI && X86 + select THERMAL if ACPI && X86 + select ACPI_VIDEO if ACPI && X86 + help + Choose this option for open-source NVIDIA support. + +config NOUVEAU_PLATFORM_DRIVER + bool "Nouveau (NVIDIA) SoC GPUs" + depends on DRM_NOUVEAU && ARCH_TEGRA + default y + help + Support for Nouveau platform driver, used for SoC GPUs as found + on NVIDIA Tegra K1. + +config NOUVEAU_DEBUG + int "Maximum debug level" + depends on DRM_NOUVEAU + range 0 7 + default 5 + help + Selects the maximum debug level to compile support for. + + 0 - fatal + 1 - error + 2 - warning + 3 - info + 4 - debug + 5 - trace (recommended) + 6 - paranoia + 7 - spam + + The paranoia and spam levels will add a lot of extra checks which + may potentially slow down driver operation. + +config NOUVEAU_DEBUG_DEFAULT + int "Default debug level" + depends on DRM_NOUVEAU + range 0 7 + default 3 + help + Selects the default debug level + +config DRM_NOUVEAU_BACKLIGHT + bool "Support for backlight control" + depends on DRM_NOUVEAU + default y + help + Say Y here if you want to control the backlight of your display + (e.g. a laptop panel). diff --git a/kernel/drivers/gpu/drm/nouveau/dispnv04/Kbuild b/kernel/drivers/gpu/drm/nouveau/dispnv04/Kbuild new file mode 100644 index 000000000..424a489d0 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/dispnv04/Kbuild @@ -0,0 +1,11 @@ +nouveau-y += dispnv04/arb.o +nouveau-y += dispnv04/crtc.o +nouveau-y += dispnv04/cursor.o +nouveau-y += dispnv04/dac.o +nouveau-y += dispnv04/dfp.o +nouveau-y += dispnv04/disp.o +nouveau-y += dispnv04/hw.o +nouveau-y += dispnv04/overlay.o +nouveau-y += dispnv04/tvmodesnv17.o +nouveau-y += dispnv04/tvnv04.o +nouveau-y += dispnv04/tvnv17.o diff --git a/kernel/drivers/gpu/drm/nouveau/dispnv04/arb.c b/kernel/drivers/gpu/drm/nouveau/dispnv04/arb.c new file mode 100644 index 000000000..c6361422a --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/dispnv04/arb.c @@ -0,0 +1,265 @@ +/* + * 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 + +#include "nouveau_drm.h" +#include "nouveau_reg.h" +#include "hw.h" + +/****************************************************************************\ +* * +* The video arbitration routines calculate some "magic" numbers. Fixes * +* the snow seen when accessing the framebuffer without it. * +* It just works (I hope). * +* * +\****************************************************************************/ + +struct nv_fifo_info { + int lwm; + int burst; +}; + +struct nv_sim_state { + int pclk_khz; + int mclk_khz; + int nvclk_khz; + int bpp; + int mem_page_miss; + int mem_latency; + int memory_type; + int memory_width; + int two_heads; +}; + +static void +nv04_calc_arb(struct nv_fifo_info *fifo, struct nv_sim_state *arb) +{ + int pagemiss, cas, width, bpp; + int nvclks, mclks, pclks, crtpagemiss; + int found, mclk_extra, mclk_loop, cbs, m1, p1; + int mclk_freq, pclk_freq, nvclk_freq; + int us_m, us_n, us_p, crtc_drain_rate; + int cpm_us, us_crt, clwm; + + pclk_freq = arb->pclk_khz; + mclk_freq = arb->mclk_khz; + nvclk_freq = arb->nvclk_khz; + pagemiss = arb->mem_page_miss; + cas = arb->mem_latency; + width = arb->memory_width >> 6; + bpp = arb->bpp; + cbs = 128; + + pclks = 2; + nvclks = 10; + mclks = 13 + cas; + mclk_extra = 3; + found = 0; + + while (!found) { + found = 1; + + mclk_loop = mclks + mclk_extra; + us_m = mclk_loop * 1000 * 1000 / mclk_freq; + us_n = nvclks * 1000 * 1000 / nvclk_freq; + us_p = nvclks * 1000 * 1000 / pclk_freq; + + crtc_drain_rate = pclk_freq * bpp / 8; + crtpagemiss = 2; + crtpagemiss += 1; + cpm_us = crtpagemiss * pagemiss * 1000 * 1000 / mclk_freq; + us_crt = cpm_us + us_m + us_n + us_p; + clwm = us_crt * crtc_drain_rate / (1000 * 1000); + clwm++; + + m1 = clwm + cbs - 512; + p1 = m1 * pclk_freq / mclk_freq; + p1 = p1 * bpp / 8; + if ((p1 < m1 && m1 > 0) || clwm > 519) { + found = !mclk_extra; + mclk_extra--; + } + if (clwm < 384) + clwm = 384; + + fifo->lwm = clwm; + fifo->burst = cbs; + } +} + +static void +nv10_calc_arb(struct nv_fifo_info *fifo, struct nv_sim_state *arb) +{ + int fill_rate, drain_rate; + int pclks, nvclks, mclks, xclks; + int pclk_freq, nvclk_freq, mclk_freq; + int fill_lat, extra_lat; + int max_burst_o, max_burst_l; + int fifo_len, min_lwm, max_lwm; + const int burst_lat = 80; /* Maximum allowable latency due + * to the CRTC FIFO burst. (ns) */ + + pclk_freq = arb->pclk_khz; + nvclk_freq = arb->nvclk_khz; + mclk_freq = arb->mclk_khz; + + fill_rate = mclk_freq * arb->memory_width / 8; /* kB/s */ + drain_rate = pclk_freq * arb->bpp / 8; /* kB/s */ + + fifo_len = arb->two_heads ? 1536 : 1024; /* B */ + + /* Fixed FIFO refill latency. */ + + pclks = 4; /* lwm detect. */ + + nvclks = 3 /* lwm -> sync. */ + + 2 /* fbi bus cycles (1 req + 1 busy) */ + + 1 /* 2 edge sync. may be very close to edge so + * just put one. */ + + 1 /* fbi_d_rdv_n */ + + 1 /* Fbi_d_rdata */ + + 1; /* crtfifo load */ + + mclks = 1 /* 2 edge sync. may be very close to edge so + * just put one. */ + + 1 /* arb_hp_req */ + + 5 /* tiling pipeline */ + + 2 /* latency fifo */ + + 2 /* memory request to fbio block */ + + 7; /* data returned from fbio block */ + + /* Need to accumulate 256 bits for read */ + mclks += (arb->memory_type == 0 ? 2 : 1) + * arb->memory_width / 32; + + fill_lat = mclks * 1000 * 1000 / mclk_freq /* minimum mclk latency */ + + nvclks * 1000 * 1000 / nvclk_freq /* nvclk latency */ + + pclks * 1000 * 1000 / pclk_freq; /* pclk latency */ + + /* Conditional FIFO refill latency. */ + + xclks = 2 * arb->mem_page_miss + mclks /* Extra latency due to + * the overlay. */ + + 2 * arb->mem_page_miss /* Extra pagemiss latency. */ + + (arb->bpp == 32 ? 8 : 4); /* Margin of error. */ + + extra_lat = xclks * 1000 * 1000 / mclk_freq; + + if (arb->two_heads) + /* Account for another CRTC. */ + extra_lat += fill_lat + extra_lat + burst_lat; + + /* FIFO burst */ + + /* Max burst not leading to overflows. */ + max_burst_o = (1 + fifo_len - extra_lat * drain_rate / (1000 * 1000)) + * (fill_rate / 1000) / ((fill_rate - drain_rate) / 1000); + fifo->burst = min(max_burst_o, 1024); + + /* Max burst value with an acceptable latency. */ + max_burst_l = burst_lat * fill_rate / (1000 * 1000); + fifo->burst = min(max_burst_l, fifo->burst); + + fifo->burst = rounddown_pow_of_two(fifo->burst); + + /* FIFO low watermark */ + + min_lwm = (fill_lat + extra_lat) * drain_rate / (1000 * 1000) + 1; + max_lwm = fifo_len - fifo->burst + + fill_lat * drain_rate / (1000 * 1000) + + fifo->burst * drain_rate / fill_rate; + + fifo->lwm = min_lwm + 10 * (max_lwm - min_lwm) / 100; /* Empirical. */ +} + +static void +nv04_update_arb(struct drm_device *dev, int VClk, int bpp, + int *burst, int *lwm) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvif_device *device = &nouveau_drm(dev)->device; + struct nv_fifo_info fifo_data; + struct nv_sim_state sim_data; + int MClk = nouveau_hw_get_clock(dev, PLL_MEMORY); + int NVClk = nouveau_hw_get_clock(dev, PLL_CORE); + uint32_t cfg1 = nvif_rd32(device, NV04_PFB_CFG1); + + sim_data.pclk_khz = VClk; + sim_data.mclk_khz = MClk; + sim_data.nvclk_khz = NVClk; + sim_data.bpp = bpp; + sim_data.two_heads = nv_two_heads(dev); + if ((dev->pdev->device & 0xffff) == 0x01a0 /*CHIPSET_NFORCE*/ || + (dev->pdev->device & 0xffff) == 0x01f0 /*CHIPSET_NFORCE2*/) { + uint32_t type; + + pci_read_config_dword(pci_get_bus_and_slot(0, 1), 0x7c, &type); + + sim_data.memory_type = (type >> 12) & 1; + sim_data.memory_width = 64; + sim_data.mem_latency = 3; + sim_data.mem_page_miss = 10; + } else { + sim_data.memory_type = nvif_rd32(device, NV04_PFB_CFG0) & 0x1; + sim_data.memory_width = (nvif_rd32(device, NV_PEXTDEV_BOOT_0) & 0x10) ? 128 : 64; + sim_data.mem_latency = cfg1 & 0xf; + sim_data.mem_page_miss = ((cfg1 >> 4) & 0xf) + ((cfg1 >> 31) & 0x1); + } + + if (drm->device.info.family == NV_DEVICE_INFO_V0_TNT) + nv04_calc_arb(&fifo_data, &sim_data); + else + nv10_calc_arb(&fifo_data, &sim_data); + + *burst = ilog2(fifo_data.burst >> 4); + *lwm = fifo_data.lwm >> 3; +} + +static void +nv20_update_arb(int *burst, int *lwm) +{ + unsigned int fifo_size, burst_size, graphics_lwm; + + fifo_size = 2048; + burst_size = 512; + graphics_lwm = fifo_size - burst_size; + + *burst = ilog2(burst_size >> 5); + *lwm = graphics_lwm >> 3; +} + +void +nouveau_calc_arb(struct drm_device *dev, int vclk, int bpp, int *burst, int *lwm) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + + if (drm->device.info.family < NV_DEVICE_INFO_V0_KELVIN) + nv04_update_arb(dev, vclk, bpp, burst, lwm); + else if ((dev->pdev->device & 0xfff0) == 0x0240 /*CHIPSET_C51*/ || + (dev->pdev->device & 0xfff0) == 0x03d0 /*CHIPSET_C512*/) { + *burst = 128; + *lwm = 0x0480; + } else + nv20_update_arb(burst, lwm); +} diff --git a/kernel/drivers/gpu/drm/nouveau/dispnv04/crtc.c b/kernel/drivers/gpu/drm/nouveau/dispnv04/crtc.c new file mode 100644 index 000000000..3d96b49fe --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/dispnv04/crtc.c @@ -0,0 +1,1146 @@ +/* + * Copyright 1993-2003 NVIDIA, Corporation + * Copyright 2006 Dave Airlie + * Copyright 2007 Maarten Maathuis + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to 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 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 "nouveau_drm.h" +#include "nouveau_reg.h" +#include "nouveau_bo.h" +#include "nouveau_gem.h" +#include "nouveau_encoder.h" +#include "nouveau_connector.h" +#include "nouveau_crtc.h" +#include "hw.h" +#include "nvreg.h" +#include "nouveau_fbcon.h" +#include "disp.h" + +#include +#include + +static int +nv04_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, + struct drm_framebuffer *old_fb); + +static void +crtc_wr_cio_state(struct drm_crtc *crtc, struct nv04_crtc_reg *crtcstate, int index) +{ + NVWriteVgaCrtc(crtc->dev, nouveau_crtc(crtc)->index, index, + crtcstate->CRTC[index]); +} + +static void nv_crtc_set_digital_vibrance(struct drm_crtc *crtc, int level) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index]; + + regp->CRTC[NV_CIO_CRE_CSB] = nv_crtc->saturation = level; + if (nv_crtc->saturation && nv_gf4_disp_arch(crtc->dev)) { + regp->CRTC[NV_CIO_CRE_CSB] = 0x80; + regp->CRTC[NV_CIO_CRE_5B] = nv_crtc->saturation << 2; + crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_5B); + } + crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_CSB); +} + +static void nv_crtc_set_image_sharpening(struct drm_crtc *crtc, int level) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index]; + + nv_crtc->sharpness = level; + if (level < 0) /* blur is in hw range 0x3f -> 0x20 */ + level += 0x40; + regp->ramdac_634 = level; + NVWriteRAMDAC(crtc->dev, nv_crtc->index, NV_PRAMDAC_634, regp->ramdac_634); +} + +#define PLLSEL_VPLL1_MASK \ + (NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_VPLL \ + | NV_PRAMDAC_PLL_COEFF_SELECT_VCLK_RATIO_DB2) +#define PLLSEL_VPLL2_MASK \ + (NV_PRAMDAC_PLL_COEFF_SELECT_PLL_SOURCE_VPLL2 \ + | NV_PRAMDAC_PLL_COEFF_SELECT_VCLK2_RATIO_DB2) +#define PLLSEL_TV_MASK \ + (NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1 \ + | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1 \ + | NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK2 \ + | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK2) + +/* NV4x 0x40.. pll notes: + * gpu pll: 0x4000 + 0x4004 + * ?gpu? pll: 0x4008 + 0x400c + * vpll1: 0x4010 + 0x4014 + * vpll2: 0x4018 + 0x401c + * mpll: 0x4020 + 0x4024 + * mpll: 0x4038 + 0x403c + * + * the first register of each pair has some unknown details: + * bits 0-7: redirected values from elsewhere? (similar to PLL_SETUP_CONTROL?) + * bits 20-23: (mpll) something to do with post divider? + * bits 28-31: related to single stage mode? (bit 8/12) + */ + +static void nv_crtc_calc_state_ext(struct drm_crtc *crtc, struct drm_display_mode * mode, int dot_clock) +{ + struct drm_device *dev = crtc->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_bios *bios = nvxx_bios(&drm->device); + struct nvkm_clk *clk = nvxx_clk(&drm->device); + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct nv04_mode_state *state = &nv04_display(dev)->mode_reg; + struct nv04_crtc_reg *regp = &state->crtc_reg[nv_crtc->index]; + struct nvkm_pll_vals *pv = ®p->pllvals; + struct nvbios_pll pll_lim; + + if (nvbios_pll_parse(bios, nv_crtc->index ? PLL_VPLL1 : PLL_VPLL0, + &pll_lim)) + return; + + /* NM2 == 0 is used to determine single stage mode on two stage plls */ + pv->NM2 = 0; + + /* for newer nv4x the blob uses only the first stage of the vpll below a + * certain clock. for a certain nv4b this is 150MHz. since the max + * output frequency of the first stage for this card is 300MHz, it is + * assumed the threshold is given by vco1 maxfreq/2 + */ + /* for early nv4x, specifically nv40 and *some* nv43 (devids 0 and 6, + * not 8, others unknown), the blob always uses both plls. no problem + * has yet been observed in allowing the use a single stage pll on all + * nv43 however. the behaviour of single stage use is untested on nv40 + */ + if (drm->device.info.chipset > 0x40 && dot_clock <= (pll_lim.vco1.max_freq / 2)) + memset(&pll_lim.vco2, 0, sizeof(pll_lim.vco2)); + + + if (!clk->pll_calc(clk, &pll_lim, dot_clock, pv)) + return; + + state->pllsel &= PLLSEL_VPLL1_MASK | PLLSEL_VPLL2_MASK | PLLSEL_TV_MASK; + + /* The blob uses this always, so let's do the same */ + if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE) + state->pllsel |= NV_PRAMDAC_PLL_COEFF_SELECT_USE_VPLL2_TRUE; + /* again nv40 and some nv43 act more like nv3x as described above */ + if (drm->device.info.chipset < 0x41) + state->pllsel |= NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_MPLL | + NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_NVPLL; + state->pllsel |= nv_crtc->index ? PLLSEL_VPLL2_MASK : PLLSEL_VPLL1_MASK; + + if (pv->NM2) + NV_DEBUG(drm, "vpll: n1 %d n2 %d m1 %d m2 %d log2p %d\n", + pv->N1, pv->N2, pv->M1, pv->M2, pv->log2P); + else + NV_DEBUG(drm, "vpll: n %d m %d log2p %d\n", + pv->N1, pv->M1, pv->log2P); + + nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.offset); +} + +static void +nv_crtc_dpms(struct drm_crtc *crtc, int mode) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + unsigned char seq1 = 0, crtc17 = 0; + unsigned char crtc1A; + + NV_DEBUG(drm, "Setting dpms mode %d on CRTC %d\n", mode, + nv_crtc->index); + + if (nv_crtc->last_dpms == mode) /* Don't do unnecessary mode changes. */ + return; + + nv_crtc->last_dpms = mode; + + if (nv_two_heads(dev)) + NVSetOwner(dev, nv_crtc->index); + + /* nv4ref indicates these two RPC1 bits inhibit h/v sync */ + crtc1A = NVReadVgaCrtc(dev, nv_crtc->index, + NV_CIO_CRE_RPC1_INDEX) & ~0xC0; + switch (mode) { + case DRM_MODE_DPMS_STANDBY: + /* Screen: Off; HSync: Off, VSync: On -- Not Supported */ + seq1 = 0x20; + crtc17 = 0x80; + crtc1A |= 0x80; + break; + case DRM_MODE_DPMS_SUSPEND: + /* Screen: Off; HSync: On, VSync: Off -- Not Supported */ + seq1 = 0x20; + crtc17 = 0x80; + crtc1A |= 0x40; + break; + case DRM_MODE_DPMS_OFF: + /* Screen: Off; HSync: Off, VSync: Off */ + seq1 = 0x20; + crtc17 = 0x00; + crtc1A |= 0xC0; + break; + case DRM_MODE_DPMS_ON: + default: + /* Screen: On; HSync: On, VSync: On */ + seq1 = 0x00; + crtc17 = 0x80; + break; + } + + NVVgaSeqReset(dev, nv_crtc->index, true); + /* Each head has it's own sequencer, so we can turn it off when we want */ + seq1 |= (NVReadVgaSeq(dev, nv_crtc->index, NV_VIO_SR_CLOCK_INDEX) & ~0x20); + NVWriteVgaSeq(dev, nv_crtc->index, NV_VIO_SR_CLOCK_INDEX, seq1); + crtc17 |= (NVReadVgaCrtc(dev, nv_crtc->index, NV_CIO_CR_MODE_INDEX) & ~0x80); + mdelay(10); + NVWriteVgaCrtc(dev, nv_crtc->index, NV_CIO_CR_MODE_INDEX, crtc17); + NVVgaSeqReset(dev, nv_crtc->index, false); + + NVWriteVgaCrtc(dev, nv_crtc->index, NV_CIO_CRE_RPC1_INDEX, crtc1A); +} + +static bool +nv_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static void +nv_crtc_mode_set_vga(struct drm_crtc *crtc, struct drm_display_mode *mode) +{ + struct drm_device *dev = crtc->dev; + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index]; + struct drm_framebuffer *fb = crtc->primary->fb; + + /* Calculate our timings */ + int horizDisplay = (mode->crtc_hdisplay >> 3) - 1; + int horizStart = (mode->crtc_hsync_start >> 3) + 1; + int horizEnd = (mode->crtc_hsync_end >> 3) + 1; + int horizTotal = (mode->crtc_htotal >> 3) - 5; + int horizBlankStart = (mode->crtc_hdisplay >> 3) - 1; + int horizBlankEnd = (mode->crtc_htotal >> 3) - 1; + int vertDisplay = mode->crtc_vdisplay - 1; + int vertStart = mode->crtc_vsync_start - 1; + int vertEnd = mode->crtc_vsync_end - 1; + int vertTotal = mode->crtc_vtotal - 2; + int vertBlankStart = mode->crtc_vdisplay - 1; + int vertBlankEnd = mode->crtc_vtotal - 1; + + struct drm_encoder *encoder; + bool fp_output = false; + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + + if (encoder->crtc == crtc && + (nv_encoder->dcb->type == DCB_OUTPUT_LVDS || + nv_encoder->dcb->type == DCB_OUTPUT_TMDS)) + fp_output = true; + } + + if (fp_output) { + vertStart = vertTotal - 3; + vertEnd = vertTotal - 2; + vertBlankStart = vertStart; + horizStart = horizTotal - 5; + horizEnd = horizTotal - 2; + horizBlankEnd = horizTotal + 4; +#if 0 + if (dev->overlayAdaptor && drm->device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) + /* This reportedly works around some video overlay bandwidth problems */ + horizTotal += 2; +#endif + } + + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + vertTotal |= 1; + +#if 0 + ErrorF("horizDisplay: 0x%X \n", horizDisplay); + ErrorF("horizStart: 0x%X \n", horizStart); + ErrorF("horizEnd: 0x%X \n", horizEnd); + ErrorF("horizTotal: 0x%X \n", horizTotal); + ErrorF("horizBlankStart: 0x%X \n", horizBlankStart); + ErrorF("horizBlankEnd: 0x%X \n", horizBlankEnd); + ErrorF("vertDisplay: 0x%X \n", vertDisplay); + ErrorF("vertStart: 0x%X \n", vertStart); + ErrorF("vertEnd: 0x%X \n", vertEnd); + ErrorF("vertTotal: 0x%X \n", vertTotal); + ErrorF("vertBlankStart: 0x%X \n", vertBlankStart); + ErrorF("vertBlankEnd: 0x%X \n", vertBlankEnd); +#endif + + /* + * compute correct Hsync & Vsync polarity + */ + if ((mode->flags & (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NHSYNC)) + && (mode->flags & (DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC))) { + + regp->MiscOutReg = 0x23; + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + regp->MiscOutReg |= 0x40; + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + regp->MiscOutReg |= 0x80; + } else { + int vdisplay = mode->vdisplay; + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + vdisplay *= 2; + if (mode->vscan > 1) + vdisplay *= mode->vscan; + if (vdisplay < 400) + regp->MiscOutReg = 0xA3; /* +hsync -vsync */ + else if (vdisplay < 480) + regp->MiscOutReg = 0x63; /* -hsync +vsync */ + else if (vdisplay < 768) + regp->MiscOutReg = 0xE3; /* -hsync -vsync */ + else + regp->MiscOutReg = 0x23; /* +hsync +vsync */ + } + + /* + * Time Sequencer + */ + regp->Sequencer[NV_VIO_SR_RESET_INDEX] = 0x00; + /* 0x20 disables the sequencer */ + if (mode->flags & DRM_MODE_FLAG_CLKDIV2) + regp->Sequencer[NV_VIO_SR_CLOCK_INDEX] = 0x29; + else + regp->Sequencer[NV_VIO_SR_CLOCK_INDEX] = 0x21; + regp->Sequencer[NV_VIO_SR_PLANE_MASK_INDEX] = 0x0F; + regp->Sequencer[NV_VIO_SR_CHAR_MAP_INDEX] = 0x00; + regp->Sequencer[NV_VIO_SR_MEM_MODE_INDEX] = 0x0E; + + /* + * CRTC + */ + regp->CRTC[NV_CIO_CR_HDT_INDEX] = horizTotal; + regp->CRTC[NV_CIO_CR_HDE_INDEX] = horizDisplay; + regp->CRTC[NV_CIO_CR_HBS_INDEX] = horizBlankStart; + regp->CRTC[NV_CIO_CR_HBE_INDEX] = (1 << 7) | + XLATE(horizBlankEnd, 0, NV_CIO_CR_HBE_4_0); + regp->CRTC[NV_CIO_CR_HRS_INDEX] = horizStart; + regp->CRTC[NV_CIO_CR_HRE_INDEX] = XLATE(horizBlankEnd, 5, NV_CIO_CR_HRE_HBE_5) | + XLATE(horizEnd, 0, NV_CIO_CR_HRE_4_0); + regp->CRTC[NV_CIO_CR_VDT_INDEX] = vertTotal; + regp->CRTC[NV_CIO_CR_OVL_INDEX] = XLATE(vertStart, 9, NV_CIO_CR_OVL_VRS_9) | + XLATE(vertDisplay, 9, NV_CIO_CR_OVL_VDE_9) | + XLATE(vertTotal, 9, NV_CIO_CR_OVL_VDT_9) | + (1 << 4) | + XLATE(vertBlankStart, 8, NV_CIO_CR_OVL_VBS_8) | + XLATE(vertStart, 8, NV_CIO_CR_OVL_VRS_8) | + XLATE(vertDisplay, 8, NV_CIO_CR_OVL_VDE_8) | + XLATE(vertTotal, 8, NV_CIO_CR_OVL_VDT_8); + regp->CRTC[NV_CIO_CR_RSAL_INDEX] = 0x00; + regp->CRTC[NV_CIO_CR_CELL_HT_INDEX] = ((mode->flags & DRM_MODE_FLAG_DBLSCAN) ? MASK(NV_CIO_CR_CELL_HT_SCANDBL) : 0) | + 1 << 6 | + XLATE(vertBlankStart, 9, NV_CIO_CR_CELL_HT_VBS_9); + regp->CRTC[NV_CIO_CR_CURS_ST_INDEX] = 0x00; + regp->CRTC[NV_CIO_CR_CURS_END_INDEX] = 0x00; + regp->CRTC[NV_CIO_CR_SA_HI_INDEX] = 0x00; + regp->CRTC[NV_CIO_CR_SA_LO_INDEX] = 0x00; + regp->CRTC[NV_CIO_CR_TCOFF_HI_INDEX] = 0x00; + regp->CRTC[NV_CIO_CR_TCOFF_LO_INDEX] = 0x00; + regp->CRTC[NV_CIO_CR_VRS_INDEX] = vertStart; + regp->CRTC[NV_CIO_CR_VRE_INDEX] = 1 << 5 | XLATE(vertEnd, 0, NV_CIO_CR_VRE_3_0); + regp->CRTC[NV_CIO_CR_VDE_INDEX] = vertDisplay; + /* framebuffer can be larger than crtc scanout area. */ + regp->CRTC[NV_CIO_CR_OFFSET_INDEX] = fb->pitches[0] / 8; + regp->CRTC[NV_CIO_CR_ULINE_INDEX] = 0x00; + regp->CRTC[NV_CIO_CR_VBS_INDEX] = vertBlankStart; + regp->CRTC[NV_CIO_CR_VBE_INDEX] = vertBlankEnd; + regp->CRTC[NV_CIO_CR_MODE_INDEX] = 0x43; + regp->CRTC[NV_CIO_CR_LCOMP_INDEX] = 0xff; + + /* + * Some extended CRTC registers (they are not saved with the rest of the vga regs). + */ + + /* framebuffer can be larger than crtc scanout area. */ + regp->CRTC[NV_CIO_CRE_RPC0_INDEX] = + XLATE(fb->pitches[0] / 8, 8, NV_CIO_CRE_RPC0_OFFSET_10_8); + regp->CRTC[NV_CIO_CRE_42] = + XLATE(fb->pitches[0] / 8, 11, NV_CIO_CRE_42_OFFSET_11); + regp->CRTC[NV_CIO_CRE_RPC1_INDEX] = mode->crtc_hdisplay < 1280 ? + MASK(NV_CIO_CRE_RPC1_LARGE) : 0x00; + regp->CRTC[NV_CIO_CRE_LSR_INDEX] = XLATE(horizBlankEnd, 6, NV_CIO_CRE_LSR_HBE_6) | + XLATE(vertBlankStart, 10, NV_CIO_CRE_LSR_VBS_10) | + XLATE(vertStart, 10, NV_CIO_CRE_LSR_VRS_10) | + XLATE(vertDisplay, 10, NV_CIO_CRE_LSR_VDE_10) | + XLATE(vertTotal, 10, NV_CIO_CRE_LSR_VDT_10); + regp->CRTC[NV_CIO_CRE_HEB__INDEX] = XLATE(horizStart, 8, NV_CIO_CRE_HEB_HRS_8) | + XLATE(horizBlankStart, 8, NV_CIO_CRE_HEB_HBS_8) | + XLATE(horizDisplay, 8, NV_CIO_CRE_HEB_HDE_8) | + XLATE(horizTotal, 8, NV_CIO_CRE_HEB_HDT_8); + regp->CRTC[NV_CIO_CRE_EBR_INDEX] = XLATE(vertBlankStart, 11, NV_CIO_CRE_EBR_VBS_11) | + XLATE(vertStart, 11, NV_CIO_CRE_EBR_VRS_11) | + XLATE(vertDisplay, 11, NV_CIO_CRE_EBR_VDE_11) | + XLATE(vertTotal, 11, NV_CIO_CRE_EBR_VDT_11); + + if (mode->flags & DRM_MODE_FLAG_INTERLACE) { + horizTotal = (horizTotal >> 1) & ~1; + regp->CRTC[NV_CIO_CRE_ILACE__INDEX] = horizTotal; + regp->CRTC[NV_CIO_CRE_HEB__INDEX] |= XLATE(horizTotal, 8, NV_CIO_CRE_HEB_ILC_8); + } else + regp->CRTC[NV_CIO_CRE_ILACE__INDEX] = 0xff; /* interlace off */ + + /* + * Graphics Display Controller + */ + regp->Graphics[NV_VIO_GX_SR_INDEX] = 0x00; + regp->Graphics[NV_VIO_GX_SREN_INDEX] = 0x00; + regp->Graphics[NV_VIO_GX_CCOMP_INDEX] = 0x00; + regp->Graphics[NV_VIO_GX_ROP_INDEX] = 0x00; + regp->Graphics[NV_VIO_GX_READ_MAP_INDEX] = 0x00; + regp->Graphics[NV_VIO_GX_MODE_INDEX] = 0x40; /* 256 color mode */ + regp->Graphics[NV_VIO_GX_MISC_INDEX] = 0x05; /* map 64k mem + graphic mode */ + regp->Graphics[NV_VIO_GX_DONT_CARE_INDEX] = 0x0F; + regp->Graphics[NV_VIO_GX_BIT_MASK_INDEX] = 0xFF; + + regp->Attribute[0] = 0x00; /* standard colormap translation */ + regp->Attribute[1] = 0x01; + regp->Attribute[2] = 0x02; + regp->Attribute[3] = 0x03; + regp->Attribute[4] = 0x04; + regp->Attribute[5] = 0x05; + regp->Attribute[6] = 0x06; + regp->Attribute[7] = 0x07; + regp->Attribute[8] = 0x08; + regp->Attribute[9] = 0x09; + regp->Attribute[10] = 0x0A; + regp->Attribute[11] = 0x0B; + regp->Attribute[12] = 0x0C; + regp->Attribute[13] = 0x0D; + regp->Attribute[14] = 0x0E; + regp->Attribute[15] = 0x0F; + regp->Attribute[NV_CIO_AR_MODE_INDEX] = 0x01; /* Enable graphic mode */ + /* Non-vga */ + regp->Attribute[NV_CIO_AR_OSCAN_INDEX] = 0x00; + regp->Attribute[NV_CIO_AR_PLANE_INDEX] = 0x0F; /* enable all color planes */ + regp->Attribute[NV_CIO_AR_HPP_INDEX] = 0x00; + regp->Attribute[NV_CIO_AR_CSEL_INDEX] = 0x00; +} + +/** + * Sets up registers for the given mode/adjusted_mode pair. + * + * The clocks, CRTCs and outputs attached to this CRTC must be off. + * + * This shouldn't enable any clocks, CRTCs, or outputs, but they should + * be easily turned on/off after this. + */ +static void +nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct drm_display_mode * mode) +{ + struct drm_device *dev = crtc->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index]; + struct nv04_crtc_reg *savep = &nv04_display(dev)->saved_reg.crtc_reg[nv_crtc->index]; + struct drm_encoder *encoder; + bool lvds_output = false, tmds_output = false, tv_output = false, + off_chip_digital = false; + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + bool digital = false; + + if (encoder->crtc != crtc) + continue; + + if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS) + digital = lvds_output = true; + if (nv_encoder->dcb->type == DCB_OUTPUT_TV) + tv_output = true; + if (nv_encoder->dcb->type == DCB_OUTPUT_TMDS) + digital = tmds_output = true; + if (nv_encoder->dcb->location != DCB_LOC_ON_CHIP && digital) + off_chip_digital = true; + } + + /* Registers not directly related to the (s)vga mode */ + + /* What is the meaning of this register? */ + /* A few popular values are 0x18, 0x1c, 0x38, 0x3c */ + regp->CRTC[NV_CIO_CRE_ENH_INDEX] = savep->CRTC[NV_CIO_CRE_ENH_INDEX] & ~(1<<5); + + regp->crtc_eng_ctrl = 0; + /* Except for rare conditions I2C is enabled on the primary crtc */ + if (nv_crtc->index == 0) + regp->crtc_eng_ctrl |= NV_CRTC_FSEL_I2C; +#if 0 + /* Set overlay to desired crtc. */ + if (dev->overlayAdaptor) { + NVPortPrivPtr pPriv = GET_OVERLAY_PRIVATE(dev); + if (pPriv->overlayCRTC == nv_crtc->index) + regp->crtc_eng_ctrl |= NV_CRTC_FSEL_OVERLAY; + } +#endif + + /* ADDRESS_SPACE_PNVM is the same as setting HCUR_ASI */ + regp->cursor_cfg = NV_PCRTC_CURSOR_CONFIG_CUR_LINES_64 | + NV_PCRTC_CURSOR_CONFIG_CUR_PIXELS_64 | + NV_PCRTC_CURSOR_CONFIG_ADDRESS_SPACE_PNVM; + if (drm->device.info.chipset >= 0x11) + regp->cursor_cfg |= NV_PCRTC_CURSOR_CONFIG_CUR_BPP_32; + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + regp->cursor_cfg |= NV_PCRTC_CURSOR_CONFIG_DOUBLE_SCAN_ENABLE; + + /* Unblock some timings */ + regp->CRTC[NV_CIO_CRE_53] = 0; + regp->CRTC[NV_CIO_CRE_54] = 0; + + /* 0x00 is disabled, 0x11 is lvds, 0x22 crt and 0x88 tmds */ + if (lvds_output) + regp->CRTC[NV_CIO_CRE_SCRATCH3__INDEX] = 0x11; + else if (tmds_output) + regp->CRTC[NV_CIO_CRE_SCRATCH3__INDEX] = 0x88; + else + regp->CRTC[NV_CIO_CRE_SCRATCH3__INDEX] = 0x22; + + /* These values seem to vary */ + /* This register seems to be used by the bios to make certain decisions on some G70 cards? */ + regp->CRTC[NV_CIO_CRE_SCRATCH4__INDEX] = savep->CRTC[NV_CIO_CRE_SCRATCH4__INDEX]; + + nv_crtc_set_digital_vibrance(crtc, nv_crtc->saturation); + + /* probably a scratch reg, but kept for cargo-cult purposes: + * bit0: crtc0?, head A + * bit6: lvds, head A + * bit7: (only in X), head A + */ + if (nv_crtc->index == 0) + regp->CRTC[NV_CIO_CRE_4B] = savep->CRTC[NV_CIO_CRE_4B] | 0x80; + + /* The blob seems to take the current value from crtc 0, add 4 to that + * and reuse the old value for crtc 1 */ + regp->CRTC[NV_CIO_CRE_TVOUT_LATENCY] = nv04_display(dev)->saved_reg.crtc_reg[0].CRTC[NV_CIO_CRE_TVOUT_LATENCY]; + if (!nv_crtc->index) + regp->CRTC[NV_CIO_CRE_TVOUT_LATENCY] += 4; + + /* the blob sometimes sets |= 0x10 (which is the same as setting |= + * 1 << 30 on 0x60.830), for no apparent reason */ + regp->CRTC[NV_CIO_CRE_59] = off_chip_digital; + + if (drm->device.info.family >= NV_DEVICE_INFO_V0_RANKINE) + regp->CRTC[0x9f] = off_chip_digital ? 0x11 : 0x1; + + regp->crtc_830 = mode->crtc_vdisplay - 3; + regp->crtc_834 = mode->crtc_vdisplay - 1; + + if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE) + /* This is what the blob does */ + regp->crtc_850 = NVReadCRTC(dev, 0, NV_PCRTC_850); + + if (drm->device.info.family >= NV_DEVICE_INFO_V0_RANKINE) + regp->gpio_ext = NVReadCRTC(dev, 0, NV_PCRTC_GPIO_EXT); + + if (drm->device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) + regp->crtc_cfg = NV10_PCRTC_CONFIG_START_ADDRESS_HSYNC; + else + regp->crtc_cfg = NV04_PCRTC_CONFIG_START_ADDRESS_HSYNC; + + /* Some misc regs */ + if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE) { + regp->CRTC[NV_CIO_CRE_85] = 0xFF; + regp->CRTC[NV_CIO_CRE_86] = 0x1; + } + + regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] = (crtc->primary->fb->depth + 1) / 8; + /* Enable slaved mode (called MODE_TV in nv4ref.h) */ + if (lvds_output || tmds_output || tv_output) + regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] |= (1 << 7); + + /* Generic PRAMDAC regs */ + + if (drm->device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) + /* Only bit that bios and blob set. */ + regp->nv10_cursync = (1 << 25); + + regp->ramdac_gen_ctrl = NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS | + NV_PRAMDAC_GENERAL_CONTROL_VGA_STATE_SEL | + NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON; + if (crtc->primary->fb->depth == 16) + regp->ramdac_gen_ctrl |= NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL; + if (drm->device.info.chipset >= 0x11) + regp->ramdac_gen_ctrl |= NV_PRAMDAC_GENERAL_CONTROL_PIPE_LONG; + + regp->ramdac_630 = 0; /* turn off green mode (tv test pattern?) */ + regp->tv_setup = 0; + + nv_crtc_set_image_sharpening(crtc, nv_crtc->sharpness); + + /* Some values the blob sets */ + regp->ramdac_8c0 = 0x100; + regp->ramdac_a20 = 0x0; + regp->ramdac_a24 = 0xfffff; + regp->ramdac_a34 = 0x1; +} + +static int +nv_crtc_swap_fbs(struct drm_crtc *crtc, struct drm_framebuffer *old_fb) +{ + struct nv04_display *disp = nv04_display(crtc->dev); + struct nouveau_framebuffer *nvfb = nouveau_framebuffer(crtc->primary->fb); + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + int ret; + + ret = nouveau_bo_pin(nvfb->nvbo, TTM_PL_FLAG_VRAM, false); + if (ret == 0) { + if (disp->image[nv_crtc->index]) + nouveau_bo_unpin(disp->image[nv_crtc->index]); + nouveau_bo_ref(nvfb->nvbo, &disp->image[nv_crtc->index]); + } + + return ret; +} + +/** + * Sets up registers for the given mode/adjusted_mode pair. + * + * The clocks, CRTCs and outputs attached to this CRTC must be off. + * + * This shouldn't enable any clocks, CRTCs, or outputs, but they should + * be easily turned on/off after this. + */ +static int +nv_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, + int x, int y, struct drm_framebuffer *old_fb) +{ + struct drm_device *dev = crtc->dev; + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct nouveau_drm *drm = nouveau_drm(dev); + int ret; + + NV_DEBUG(drm, "CTRC mode on CRTC %d:\n", nv_crtc->index); + drm_mode_debug_printmodeline(adjusted_mode); + + ret = nv_crtc_swap_fbs(crtc, old_fb); + if (ret) + return ret; + + /* unlock must come after turning off FP_TG_CONTROL in output_prepare */ + nv_lock_vga_crtc_shadow(dev, nv_crtc->index, -1); + + nv_crtc_mode_set_vga(crtc, adjusted_mode); + /* calculated in nv04_dfp_prepare, nv40 needs it written before calculating PLLs */ + if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE) + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, nv04_display(dev)->mode_reg.sel_clk); + nv_crtc_mode_set_regs(crtc, adjusted_mode); + nv_crtc_calc_state_ext(crtc, mode, adjusted_mode->clock); + return 0; +} + +static void nv_crtc_save(struct drm_crtc *crtc) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct nv04_mode_state *state = &nv04_display(dev)->mode_reg; + struct nv04_crtc_reg *crtc_state = &state->crtc_reg[nv_crtc->index]; + struct nv04_mode_state *saved = &nv04_display(dev)->saved_reg; + struct nv04_crtc_reg *crtc_saved = &saved->crtc_reg[nv_crtc->index]; + + if (nv_two_heads(crtc->dev)) + NVSetOwner(crtc->dev, nv_crtc->index); + + nouveau_hw_save_state(crtc->dev, nv_crtc->index, saved); + + /* init some state to saved value */ + state->sel_clk = saved->sel_clk & ~(0x5 << 16); + crtc_state->CRTC[NV_CIO_CRE_LCD__INDEX] = crtc_saved->CRTC[NV_CIO_CRE_LCD__INDEX]; + state->pllsel = saved->pllsel & ~(PLLSEL_VPLL1_MASK | PLLSEL_VPLL2_MASK | PLLSEL_TV_MASK); + crtc_state->gpio_ext = crtc_saved->gpio_ext; +} + +static void nv_crtc_restore(struct drm_crtc *crtc) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct drm_device *dev = crtc->dev; + int head = nv_crtc->index; + uint8_t saved_cr21 = nv04_display(dev)->saved_reg.crtc_reg[head].CRTC[NV_CIO_CRE_21]; + + if (nv_two_heads(crtc->dev)) + NVSetOwner(crtc->dev, head); + + nouveau_hw_load_state(crtc->dev, head, &nv04_display(dev)->saved_reg); + nv_lock_vga_crtc_shadow(crtc->dev, head, saved_cr21); + + nv_crtc->last_dpms = NV_DPMS_CLEARED; +} + +static void nv_crtc_prepare(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + const struct drm_crtc_helper_funcs *funcs = crtc->helper_private; + + if (nv_two_heads(dev)) + NVSetOwner(dev, nv_crtc->index); + + drm_vblank_pre_modeset(dev, nv_crtc->index); + funcs->dpms(crtc, DRM_MODE_DPMS_OFF); + + NVBlankScreen(dev, nv_crtc->index, true); + + /* Some more preparation. */ + NVWriteCRTC(dev, nv_crtc->index, NV_PCRTC_CONFIG, NV_PCRTC_CONFIG_START_ADDRESS_NON_VGA); + if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE) { + uint32_t reg900 = NVReadRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_900); + NVWriteRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_900, reg900 & ~0x10000); + } +} + +static void nv_crtc_commit(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + const struct drm_crtc_helper_funcs *funcs = crtc->helper_private; + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + + nouveau_hw_load_state(dev, nv_crtc->index, &nv04_display(dev)->mode_reg); + nv04_crtc_mode_set_base(crtc, crtc->x, crtc->y, NULL); + +#ifdef __BIG_ENDIAN + /* turn on LFB swapping */ + { + uint8_t tmp = NVReadVgaCrtc(dev, nv_crtc->index, NV_CIO_CRE_RCR); + tmp |= MASK(NV_CIO_CRE_RCR_ENDIAN_BIG); + NVWriteVgaCrtc(dev, nv_crtc->index, NV_CIO_CRE_RCR, tmp); + } +#endif + + funcs->dpms(crtc, DRM_MODE_DPMS_ON); + drm_vblank_post_modeset(dev, nv_crtc->index); +} + +static void nv_crtc_destroy(struct drm_crtc *crtc) +{ + struct nv04_display *disp = nv04_display(crtc->dev); + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + + if (!nv_crtc) + return; + + drm_crtc_cleanup(crtc); + + if (disp->image[nv_crtc->index]) + nouveau_bo_unpin(disp->image[nv_crtc->index]); + nouveau_bo_ref(NULL, &disp->image[nv_crtc->index]); + + nouveau_bo_unmap(nv_crtc->cursor.nvbo); + nouveau_bo_unpin(nv_crtc->cursor.nvbo); + nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo); + kfree(nv_crtc); +} + +static void +nv_crtc_gamma_load(struct drm_crtc *crtc) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct drm_device *dev = nv_crtc->base.dev; + struct rgb { uint8_t r, g, b; } __attribute__((packed)) *rgbs; + int i; + + rgbs = (struct rgb *)nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index].DAC; + for (i = 0; i < 256; i++) { + rgbs[i].r = nv_crtc->lut.r[i] >> 8; + rgbs[i].g = nv_crtc->lut.g[i] >> 8; + rgbs[i].b = nv_crtc->lut.b[i] >> 8; + } + + nouveau_hw_load_state_palette(dev, nv_crtc->index, &nv04_display(dev)->mode_reg); +} + +static void +nv_crtc_disable(struct drm_crtc *crtc) +{ + struct nv04_display *disp = nv04_display(crtc->dev); + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + if (disp->image[nv_crtc->index]) + nouveau_bo_unpin(disp->image[nv_crtc->index]); + nouveau_bo_ref(NULL, &disp->image[nv_crtc->index]); +} + +static void +nv_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, uint32_t start, + uint32_t size) +{ + int end = (start + size > 256) ? 256 : start + size, i; + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + + for (i = start; i < end; i++) { + nv_crtc->lut.r[i] = r[i]; + nv_crtc->lut.g[i] = g[i]; + nv_crtc->lut.b[i] = b[i]; + } + + /* We need to know the depth before we upload, but it's possible to + * get called before a framebuffer is bound. If this is the case, + * mark the lut values as dirty by setting depth==0, and it'll be + * uploaded on the first mode_set_base() + */ + if (!nv_crtc->base.primary->fb) { + nv_crtc->lut.depth = 0; + return; + } + + nv_crtc_gamma_load(crtc); +} + +static int +nv04_crtc_do_mode_set_base(struct drm_crtc *crtc, + struct drm_framebuffer *passed_fb, + int x, int y, bool atomic) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index]; + struct drm_framebuffer *drm_fb; + struct nouveau_framebuffer *fb; + int arb_burst, arb_lwm; + + NV_DEBUG(drm, "index %d\n", nv_crtc->index); + + /* no fb bound */ + if (!atomic && !crtc->primary->fb) { + NV_DEBUG(drm, "No FB bound\n"); + return 0; + } + + /* If atomic, we want to switch to the fb we were passed, so + * now we update pointers to do that. + */ + if (atomic) { + drm_fb = passed_fb; + fb = nouveau_framebuffer(passed_fb); + } else { + drm_fb = crtc->primary->fb; + fb = nouveau_framebuffer(crtc->primary->fb); + } + + nv_crtc->fb.offset = fb->nvbo->bo.offset; + + if (nv_crtc->lut.depth != drm_fb->depth) { + nv_crtc->lut.depth = drm_fb->depth; + nv_crtc_gamma_load(crtc); + } + + /* Update the framebuffer format. */ + regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] &= ~3; + regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] |= (crtc->primary->fb->depth + 1) / 8; + regp->ramdac_gen_ctrl &= ~NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL; + if (crtc->primary->fb->depth == 16) + regp->ramdac_gen_ctrl |= NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL; + crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_PIXEL_INDEX); + NVWriteRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_GENERAL_CONTROL, + regp->ramdac_gen_ctrl); + + regp->CRTC[NV_CIO_CR_OFFSET_INDEX] = drm_fb->pitches[0] >> 3; + regp->CRTC[NV_CIO_CRE_RPC0_INDEX] = + XLATE(drm_fb->pitches[0] >> 3, 8, NV_CIO_CRE_RPC0_OFFSET_10_8); + regp->CRTC[NV_CIO_CRE_42] = + XLATE(drm_fb->pitches[0] / 8, 11, NV_CIO_CRE_42_OFFSET_11); + crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_RPC0_INDEX); + crtc_wr_cio_state(crtc, regp, NV_CIO_CR_OFFSET_INDEX); + crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_42); + + /* Update the framebuffer location. */ + regp->fb_start = nv_crtc->fb.offset & ~3; + regp->fb_start += (y * drm_fb->pitches[0]) + (x * drm_fb->bits_per_pixel / 8); + nv_set_crtc_base(dev, nv_crtc->index, regp->fb_start); + + /* Update the arbitration parameters. */ + nouveau_calc_arb(dev, crtc->mode.clock, drm_fb->bits_per_pixel, + &arb_burst, &arb_lwm); + + regp->CRTC[NV_CIO_CRE_FF_INDEX] = arb_burst; + regp->CRTC[NV_CIO_CRE_FFLWM__INDEX] = arb_lwm & 0xff; + crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_FF_INDEX); + crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_FFLWM__INDEX); + + if (drm->device.info.family >= NV_DEVICE_INFO_V0_KELVIN) { + regp->CRTC[NV_CIO_CRE_47] = arb_lwm >> 8; + crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_47); + } + + return 0; +} + +static int +nv04_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, + struct drm_framebuffer *old_fb) +{ + int ret = nv_crtc_swap_fbs(crtc, old_fb); + if (ret) + return ret; + return nv04_crtc_do_mode_set_base(crtc, old_fb, x, y, false); +} + +static int +nv04_crtc_mode_set_base_atomic(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + int x, int y, enum mode_set_atomic state) +{ + struct nouveau_drm *drm = nouveau_drm(crtc->dev); + struct drm_device *dev = drm->dev; + + if (state == ENTER_ATOMIC_MODE_SET) + nouveau_fbcon_accel_save_disable(dev); + else + nouveau_fbcon_accel_restore(dev); + + return nv04_crtc_do_mode_set_base(crtc, fb, x, y, true); +} + +static void nv04_cursor_upload(struct drm_device *dev, struct nouveau_bo *src, + struct nouveau_bo *dst) +{ + int width = nv_cursor_width(dev); + uint32_t pixel; + int i, j; + + for (i = 0; i < width; i++) { + for (j = 0; j < width; j++) { + pixel = nouveau_bo_rd32(src, i*64 + j); + + nouveau_bo_wr16(dst, i*width + j, (pixel & 0x80000000) >> 16 + | (pixel & 0xf80000) >> 9 + | (pixel & 0xf800) >> 6 + | (pixel & 0xf8) >> 3); + } + } +} + +static void nv11_cursor_upload(struct drm_device *dev, struct nouveau_bo *src, + struct nouveau_bo *dst) +{ + uint32_t pixel; + int alpha, i; + + /* nv11+ supports premultiplied (PM), or non-premultiplied (NPM) alpha + * cursors (though NPM in combination with fp dithering may not work on + * nv11, from "nv" driver history) + * NPM mode needs NV_PCRTC_CURSOR_CONFIG_ALPHA_BLEND set and is what the + * blob uses, however we get given PM cursors so we use PM mode + */ + for (i = 0; i < 64 * 64; i++) { + pixel = nouveau_bo_rd32(src, i); + + /* hw gets unhappy if alpha <= rgb values. for a PM image "less + * than" shouldn't happen; fix "equal to" case by adding one to + * alpha channel (slightly inaccurate, but so is attempting to + * get back to NPM images, due to limits of integer precision) + */ + alpha = pixel >> 24; + if (alpha > 0 && alpha < 255) + pixel = (pixel & 0x00ffffff) | ((alpha + 1) << 24); + +#ifdef __BIG_ENDIAN + { + struct nouveau_drm *drm = nouveau_drm(dev); + + if (drm->device.info.chipset == 0x11) { + pixel = ((pixel & 0x000000ff) << 24) | + ((pixel & 0x0000ff00) << 8) | + ((pixel & 0x00ff0000) >> 8) | + ((pixel & 0xff000000) >> 24); + } + } +#endif + + nouveau_bo_wr32(dst, i, pixel); + } +} + +static int +nv04_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, + uint32_t buffer_handle, uint32_t width, uint32_t height) +{ + struct nouveau_drm *drm = nouveau_drm(crtc->dev); + struct drm_device *dev = drm->dev; + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct nouveau_bo *cursor = NULL; + struct drm_gem_object *gem; + int ret = 0; + + if (!buffer_handle) { + nv_crtc->cursor.hide(nv_crtc, true); + return 0; + } + + if (width != 64 || height != 64) + return -EINVAL; + + gem = drm_gem_object_lookup(dev, file_priv, buffer_handle); + if (!gem) + return -ENOENT; + cursor = nouveau_gem_object(gem); + + ret = nouveau_bo_map(cursor); + if (ret) + goto out; + + if (drm->device.info.chipset >= 0x11) + nv11_cursor_upload(dev, cursor, nv_crtc->cursor.nvbo); + else + nv04_cursor_upload(dev, cursor, nv_crtc->cursor.nvbo); + + nouveau_bo_unmap(cursor); + nv_crtc->cursor.offset = nv_crtc->cursor.nvbo->bo.offset; + nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.offset); + nv_crtc->cursor.show(nv_crtc, true); +out: + drm_gem_object_unreference_unlocked(gem); + return ret; +} + +static int +nv04_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + + nv_crtc->cursor.set_pos(nv_crtc, x, y); + return 0; +} + +int +nouveau_crtc_set_config(struct drm_mode_set *set) +{ + struct drm_device *dev; + struct nouveau_drm *drm; + int ret; + struct drm_crtc *crtc; + bool active = false; + if (!set || !set->crtc) + return -EINVAL; + + dev = set->crtc->dev; + + /* get a pm reference here */ + ret = pm_runtime_get_sync(dev->dev); + if (ret < 0 && ret != -EACCES) + return ret; + + ret = drm_crtc_helper_set_config(set); + + drm = nouveau_drm(dev); + + /* if we get here with no crtcs active then we can drop a reference */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + if (crtc->enabled) + active = true; + } + + pm_runtime_mark_last_busy(dev->dev); + /* if we have active crtcs and we don't have a power ref, + take the current one */ + if (active && !drm->have_disp_power_ref) { + drm->have_disp_power_ref = true; + return ret; + } + /* if we have no active crtcs, then drop the power ref + we got before */ + if (!active && drm->have_disp_power_ref) { + pm_runtime_put_autosuspend(dev->dev); + drm->have_disp_power_ref = false; + } + /* drop the power reference we got coming in here */ + pm_runtime_put_autosuspend(dev->dev); + return ret; +} + +static const struct drm_crtc_funcs nv04_crtc_funcs = { + .save = nv_crtc_save, + .restore = nv_crtc_restore, + .cursor_set = nv04_crtc_cursor_set, + .cursor_move = nv04_crtc_cursor_move, + .gamma_set = nv_crtc_gamma_set, + .set_config = nouveau_crtc_set_config, + .page_flip = nouveau_crtc_page_flip, + .destroy = nv_crtc_destroy, +}; + +static const struct drm_crtc_helper_funcs nv04_crtc_helper_funcs = { + .dpms = nv_crtc_dpms, + .prepare = nv_crtc_prepare, + .commit = nv_crtc_commit, + .mode_fixup = nv_crtc_mode_fixup, + .mode_set = nv_crtc_mode_set, + .mode_set_base = nv04_crtc_mode_set_base, + .mode_set_base_atomic = nv04_crtc_mode_set_base_atomic, + .load_lut = nv_crtc_gamma_load, + .disable = nv_crtc_disable, +}; + +int +nv04_crtc_create(struct drm_device *dev, int crtc_num) +{ + struct nouveau_crtc *nv_crtc; + int ret, i; + + nv_crtc = kzalloc(sizeof(*nv_crtc), GFP_KERNEL); + if (!nv_crtc) + return -ENOMEM; + + for (i = 0; i < 256; i++) { + nv_crtc->lut.r[i] = i << 8; + nv_crtc->lut.g[i] = i << 8; + nv_crtc->lut.b[i] = i << 8; + } + nv_crtc->lut.depth = 0; + + nv_crtc->index = crtc_num; + nv_crtc->last_dpms = NV_DPMS_CLEARED; + + drm_crtc_init(dev, &nv_crtc->base, &nv04_crtc_funcs); + drm_crtc_helper_add(&nv_crtc->base, &nv04_crtc_helper_funcs); + drm_mode_crtc_set_gamma_size(&nv_crtc->base, 256); + + ret = nouveau_bo_new(dev, 64*64*4, 0x100, TTM_PL_FLAG_VRAM, + 0, 0x0000, NULL, NULL, &nv_crtc->cursor.nvbo); + if (!ret) { + ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM, false); + if (!ret) { + ret = nouveau_bo_map(nv_crtc->cursor.nvbo); + if (ret) + nouveau_bo_unpin(nv_crtc->cursor.nvbo); + } + if (ret) + nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo); + } + + nv04_cursor_init(nv_crtc); + + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/dispnv04/cursor.c b/kernel/drivers/gpu/drm/nouveau/dispnv04/cursor.c new file mode 100644 index 000000000..4e61173c3 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/dispnv04/cursor.c @@ -0,0 +1,70 @@ +#include +#include +#include "nouveau_drm.h" +#include "nouveau_reg.h" +#include "nouveau_crtc.h" +#include "hw.h" + +static void +nv04_cursor_show(struct nouveau_crtc *nv_crtc, bool update) +{ + nv_show_cursor(nv_crtc->base.dev, nv_crtc->index, true); +} + +static void +nv04_cursor_hide(struct nouveau_crtc *nv_crtc, bool update) +{ + nv_show_cursor(nv_crtc->base.dev, nv_crtc->index, false); +} + +static void +nv04_cursor_set_pos(struct nouveau_crtc *nv_crtc, int x, int y) +{ + nv_crtc->cursor_saved_x = x; nv_crtc->cursor_saved_y = y; + NVWriteRAMDAC(nv_crtc->base.dev, nv_crtc->index, + NV_PRAMDAC_CU_START_POS, + XLATE(y, 0, NV_PRAMDAC_CU_START_POS_Y) | + XLATE(x, 0, NV_PRAMDAC_CU_START_POS_X)); +} + +static void +crtc_wr_cio_state(struct drm_crtc *crtc, struct nv04_crtc_reg *crtcstate, int index) +{ + NVWriteVgaCrtc(crtc->dev, nouveau_crtc(crtc)->index, index, + crtcstate->CRTC[index]); +} + +static void +nv04_cursor_set_offset(struct nouveau_crtc *nv_crtc, uint32_t offset) +{ + struct drm_device *dev = nv_crtc->base.dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index]; + struct drm_crtc *crtc = &nv_crtc->base; + + regp->CRTC[NV_CIO_CRE_HCUR_ADDR0_INDEX] = + MASK(NV_CIO_CRE_HCUR_ASI) | + XLATE(offset, 17, NV_CIO_CRE_HCUR_ADDR0_ADR); + regp->CRTC[NV_CIO_CRE_HCUR_ADDR1_INDEX] = + XLATE(offset, 11, NV_CIO_CRE_HCUR_ADDR1_ADR); + if (crtc->mode.flags & DRM_MODE_FLAG_DBLSCAN) + regp->CRTC[NV_CIO_CRE_HCUR_ADDR1_INDEX] |= + MASK(NV_CIO_CRE_HCUR_ADDR1_CUR_DBL); + regp->CRTC[NV_CIO_CRE_HCUR_ADDR2_INDEX] = offset >> 24; + + crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX); + crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_HCUR_ADDR1_INDEX); + crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_HCUR_ADDR2_INDEX); + if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE) + nv_fix_nv40_hw_cursor(dev, nv_crtc->index); +} + +int +nv04_cursor_init(struct nouveau_crtc *crtc) +{ + crtc->cursor.set_offset = nv04_cursor_set_offset; + crtc->cursor.set_pos = nv04_cursor_set_pos; + crtc->cursor.hide = nv04_cursor_hide; + crtc->cursor.show = nv04_cursor_show; + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/dispnv04/dac.c b/kernel/drivers/gpu/drm/nouveau/dispnv04/dac.c new file mode 100644 index 000000000..af7249ca0 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/dispnv04/dac.c @@ -0,0 +1,556 @@ +/* + * Copyright 2003 NVIDIA, Corporation + * Copyright 2006 Dave Airlie + * Copyright 2007 Maarten Maathuis + * 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 (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 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 "nouveau_drm.h" +#include "nouveau_encoder.h" +#include "nouveau_connector.h" +#include "nouveau_crtc.h" +#include "hw.h" +#include "nvreg.h" + +#include +#include +#include + +int nv04_dac_output_offset(struct drm_encoder *encoder) +{ + struct dcb_output *dcb = nouveau_encoder(encoder)->dcb; + int offset = 0; + + if (dcb->or & (8 | DCB_OUTPUT_C)) + offset += 0x68; + if (dcb->or & (8 | DCB_OUTPUT_B)) + offset += 0x2000; + + return offset; +} + +/* + * arbitrary limit to number of sense oscillations tolerated in one sample + * period (observed to be at least 13 in "nvidia") + */ +#define MAX_HBLANK_OSC 20 + +/* + * arbitrary limit to number of conflicting sample pairs to tolerate at a + * voltage step (observed to be at least 5 in "nvidia") + */ +#define MAX_SAMPLE_PAIRS 10 + +static int sample_load_twice(struct drm_device *dev, bool sense[2]) +{ + struct nvif_device *device = &nouveau_drm(dev)->device; + struct nvkm_timer *ptimer = nvxx_timer(device); + int i; + + for (i = 0; i < 2; i++) { + bool sense_a, sense_b, sense_b_prime; + int j = 0; + + /* + * wait for bit 0 clear -- out of hblank -- (say reg value 0x4), + * then wait for transition 0x4->0x5->0x4: enter hblank, leave + * hblank again + * use a 10ms timeout (guards against crtc being inactive, in + * which case blank state would never change) + */ + if (!nvkm_timer_wait_eq(ptimer, 10000000, + NV_PRMCIO_INP0__COLOR, + 0x00000001, 0x00000000)) + return -EBUSY; + if (!nvkm_timer_wait_eq(ptimer, 10000000, + NV_PRMCIO_INP0__COLOR, + 0x00000001, 0x00000001)) + return -EBUSY; + if (!nvkm_timer_wait_eq(ptimer, 10000000, + NV_PRMCIO_INP0__COLOR, + 0x00000001, 0x00000000)) + return -EBUSY; + + udelay(100); + /* when level triggers, sense is _LO_ */ + sense_a = nvif_rd08(device, NV_PRMCIO_INP0) & 0x10; + + /* take another reading until it agrees with sense_a... */ + do { + udelay(100); + sense_b = nvif_rd08(device, NV_PRMCIO_INP0) & 0x10; + if (sense_a != sense_b) { + sense_b_prime = + nvif_rd08(device, NV_PRMCIO_INP0) & 0x10; + if (sense_b == sense_b_prime) { + /* ... unless two consecutive subsequent + * samples agree; sense_a is replaced */ + sense_a = sense_b; + /* force mis-match so we loop */ + sense_b = !sense_a; + } + } + } while ((sense_a != sense_b) && ++j < MAX_HBLANK_OSC); + + if (j == MAX_HBLANK_OSC) + /* with so much oscillation, default to sense:LO */ + sense[i] = false; + else + sense[i] = sense_a; + } + + return 0; +} + +static enum drm_connector_status nv04_dac_detect(struct drm_encoder *encoder, + struct drm_connector *connector) +{ + struct drm_device *dev = encoder->dev; + struct nvif_device *device = &nouveau_drm(dev)->device; + struct nouveau_drm *drm = nouveau_drm(dev); + uint8_t saved_seq1, saved_pi, saved_rpc1, saved_cr_mode; + uint8_t saved_palette0[3], saved_palette_mask; + uint32_t saved_rtest_ctrl, saved_rgen_ctrl; + int i; + uint8_t blue; + bool sense = true; + + /* + * for this detection to work, there needs to be a mode set up on the + * CRTC. this is presumed to be the case + */ + + if (nv_two_heads(dev)) + /* only implemented for head A for now */ + NVSetOwner(dev, 0); + + saved_cr_mode = NVReadVgaCrtc(dev, 0, NV_CIO_CR_MODE_INDEX); + NVWriteVgaCrtc(dev, 0, NV_CIO_CR_MODE_INDEX, saved_cr_mode | 0x80); + + saved_seq1 = NVReadVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX); + NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1 & ~0x20); + + saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL); + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL, + saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF); + + msleep(10); + + saved_pi = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX); + NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX, + saved_pi & ~(0x80 | MASK(NV_CIO_CRE_PIXEL_FORMAT))); + saved_rpc1 = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX); + NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1 & ~0xc0); + + nvif_wr08(device, NV_PRMDIO_READ_MODE_ADDRESS, 0x0); + for (i = 0; i < 3; i++) + saved_palette0[i] = nvif_rd08(device, NV_PRMDIO_PALETTE_DATA); + saved_palette_mask = nvif_rd08(device, NV_PRMDIO_PIXEL_MASK); + nvif_wr08(device, NV_PRMDIO_PIXEL_MASK, 0); + + saved_rgen_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL); + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL, + (saved_rgen_ctrl & ~(NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS | + NV_PRAMDAC_GENERAL_CONTROL_TERMINATION_75OHM)) | + NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON); + + blue = 8; /* start of test range */ + + do { + bool sense_pair[2]; + + nvif_wr08(device, NV_PRMDIO_WRITE_MODE_ADDRESS, 0); + nvif_wr08(device, NV_PRMDIO_PALETTE_DATA, 0); + nvif_wr08(device, NV_PRMDIO_PALETTE_DATA, 0); + /* testing blue won't find monochrome monitors. I don't care */ + nvif_wr08(device, NV_PRMDIO_PALETTE_DATA, blue); + + i = 0; + /* take sample pairs until both samples in the pair agree */ + do { + if (sample_load_twice(dev, sense_pair)) + goto out; + } while ((sense_pair[0] != sense_pair[1]) && + ++i < MAX_SAMPLE_PAIRS); + + if (i == MAX_SAMPLE_PAIRS) + /* too much oscillation defaults to LO */ + sense = false; + else + sense = sense_pair[0]; + + /* + * if sense goes LO before blue ramps to 0x18, monitor is not connected. + * ergo, if blue gets to 0x18, monitor must be connected + */ + } while (++blue < 0x18 && sense); + +out: + nvif_wr08(device, NV_PRMDIO_PIXEL_MASK, saved_palette_mask); + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL, saved_rgen_ctrl); + nvif_wr08(device, NV_PRMDIO_WRITE_MODE_ADDRESS, 0); + for (i = 0; i < 3; i++) + nvif_wr08(device, NV_PRMDIO_PALETTE_DATA, saved_palette0[i]); + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL, saved_rtest_ctrl); + NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX, saved_pi); + NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1); + NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1); + NVWriteVgaCrtc(dev, 0, NV_CIO_CR_MODE_INDEX, saved_cr_mode); + + if (blue == 0x18) { + NV_DEBUG(drm, "Load detected on head A\n"); + return connector_status_connected; + } + + return connector_status_disconnected; +} + +uint32_t nv17_dac_sample_load(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvif_device *device = &nouveau_drm(dev)->device; + struct nvkm_gpio *gpio = nvxx_gpio(device); + struct dcb_output *dcb = nouveau_encoder(encoder)->dcb; + uint32_t sample, testval, regoffset = nv04_dac_output_offset(encoder); + uint32_t saved_powerctrl_2 = 0, saved_powerctrl_4 = 0, saved_routput, + saved_rtest_ctrl, saved_gpio0 = 0, saved_gpio1 = 0, temp, routput; + int head; + +#define RGB_TEST_DATA(r, g, b) (r << 0 | g << 10 | b << 20) + if (dcb->type == DCB_OUTPUT_TV) { + testval = RGB_TEST_DATA(0xa0, 0xa0, 0xa0); + + if (drm->vbios.tvdactestval) + testval = drm->vbios.tvdactestval; + } else { + testval = RGB_TEST_DATA(0x140, 0x140, 0x140); /* 0x94050140 */ + + if (drm->vbios.dactestval) + testval = drm->vbios.dactestval; + } + + saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset); + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, + saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF); + + saved_powerctrl_2 = nvif_rd32(device, NV_PBUS_POWERCTRL_2); + + nvif_wr32(device, NV_PBUS_POWERCTRL_2, saved_powerctrl_2 & 0xd7ffffff); + if (regoffset == 0x68) { + saved_powerctrl_4 = nvif_rd32(device, NV_PBUS_POWERCTRL_4); + nvif_wr32(device, NV_PBUS_POWERCTRL_4, saved_powerctrl_4 & 0xffffffcf); + } + + if (gpio) { + saved_gpio1 = gpio->get(gpio, 0, DCB_GPIO_TVDAC1, 0xff); + saved_gpio0 = gpio->get(gpio, 0, DCB_GPIO_TVDAC0, 0xff); + gpio->set(gpio, 0, DCB_GPIO_TVDAC1, 0xff, dcb->type == DCB_OUTPUT_TV); + gpio->set(gpio, 0, DCB_GPIO_TVDAC0, 0xff, dcb->type == DCB_OUTPUT_TV); + } + + msleep(4); + + saved_routput = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset); + head = (saved_routput & 0x100) >> 8; + + /* if there's a spare crtc, using it will minimise flicker */ + if (!(NVReadVgaCrtc(dev, head, NV_CIO_CRE_RPC1_INDEX) & 0xC0)) + head ^= 1; + + /* nv driver and nv31 use 0xfffffeee, nv34 and 6600 use 0xfffffece */ + routput = (saved_routput & 0xfffffece) | head << 8; + + if (drm->device.info.family >= NV_DEVICE_INFO_V0_CURIE) { + if (dcb->type == DCB_OUTPUT_TV) + routput |= 0x1a << 16; + else + routput &= ~(0x1a << 16); + } + + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, routput); + msleep(1); + + temp = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset); + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, temp | 1); + + NVWriteRAMDAC(dev, head, NV_PRAMDAC_TESTPOINT_DATA, + NV_PRAMDAC_TESTPOINT_DATA_NOTBLANK | testval); + temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL, + temp | NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED); + msleep(5); + + sample = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset); + /* do it again just in case it's a residual current */ + sample &= NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset); + + temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL, + temp & ~NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_TESTPOINT_DATA, 0); + + /* bios does something more complex for restoring, but I think this is good enough */ + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, saved_routput); + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, saved_rtest_ctrl); + if (regoffset == 0x68) + nvif_wr32(device, NV_PBUS_POWERCTRL_4, saved_powerctrl_4); + nvif_wr32(device, NV_PBUS_POWERCTRL_2, saved_powerctrl_2); + + if (gpio) { + gpio->set(gpio, 0, DCB_GPIO_TVDAC1, 0xff, saved_gpio1); + gpio->set(gpio, 0, DCB_GPIO_TVDAC0, 0xff, saved_gpio0); + } + + return sample; +} + +static enum drm_connector_status +nv17_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector) +{ + struct nouveau_drm *drm = nouveau_drm(encoder->dev); + struct dcb_output *dcb = nouveau_encoder(encoder)->dcb; + + if (nv04_dac_in_use(encoder)) + return connector_status_disconnected; + + if (nv17_dac_sample_load(encoder) & + NV_PRAMDAC_TEST_CONTROL_SENSEB_ALLHI) { + NV_DEBUG(drm, "Load detected on output %c\n", + '@' + ffs(dcb->or)); + return connector_status_connected; + } else { + return connector_status_disconnected; + } +} + +static bool nv04_dac_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + if (nv04_dac_in_use(encoder)) + return false; + + return true; +} + +static void nv04_dac_prepare(struct drm_encoder *encoder) +{ + const struct drm_encoder_helper_funcs *helper = encoder->helper_private; + struct drm_device *dev = encoder->dev; + int head = nouveau_crtc(encoder->crtc)->index; + + helper->dpms(encoder, DRM_MODE_DPMS_OFF); + + nv04_dfp_disable(dev, head); +} + +static void nv04_dac_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + int head = nouveau_crtc(encoder->crtc)->index; + + if (nv_gf4_disp_arch(dev)) { + struct drm_encoder *rebind; + uint32_t dac_offset = nv04_dac_output_offset(encoder); + uint32_t otherdac; + + /* bit 16-19 are bits that are set on some G70 cards, + * but don't seem to have much effect */ + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset, + head << 8 | NV_PRAMDAC_DACCLK_SEL_DACCLK); + /* force any other vga encoders to bind to the other crtc */ + list_for_each_entry(rebind, &dev->mode_config.encoder_list, head) { + if (rebind == encoder + || nouveau_encoder(rebind)->dcb->type != DCB_OUTPUT_ANALOG) + continue; + + dac_offset = nv04_dac_output_offset(rebind); + otherdac = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset); + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset, + (otherdac & ~0x0100) | (head ^ 1) << 8); + } + } + + /* This could use refinement for flatpanels, but it should work this way */ + if (drm->device.info.chipset < 0x44) + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0xf0000000); + else + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0x00100000); +} + +static void nv04_dac_commit(struct drm_encoder *encoder) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_drm *drm = nouveau_drm(encoder->dev); + struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); + const struct drm_encoder_helper_funcs *helper = encoder->helper_private; + + helper->dpms(encoder, DRM_MODE_DPMS_ON); + + NV_DEBUG(drm, "Output %s is running on CRTC %d using output %c\n", + nouveau_encoder_connector_get(nv_encoder)->base.name, + nv_crtc->index, '@' + ffs(nv_encoder->dcb->or)); +} + +void nv04_dac_update_dacclk(struct drm_encoder *encoder, bool enable) +{ + struct drm_device *dev = encoder->dev; + struct dcb_output *dcb = nouveau_encoder(encoder)->dcb; + + if (nv_gf4_disp_arch(dev)) { + uint32_t *dac_users = &nv04_display(dev)->dac_users[ffs(dcb->or) - 1]; + int dacclk_off = NV_PRAMDAC_DACCLK + nv04_dac_output_offset(encoder); + uint32_t dacclk = NVReadRAMDAC(dev, 0, dacclk_off); + + if (enable) { + *dac_users |= 1 << dcb->index; + NVWriteRAMDAC(dev, 0, dacclk_off, dacclk | NV_PRAMDAC_DACCLK_SEL_DACCLK); + + } else { + *dac_users &= ~(1 << dcb->index); + if (!*dac_users) + NVWriteRAMDAC(dev, 0, dacclk_off, + dacclk & ~NV_PRAMDAC_DACCLK_SEL_DACCLK); + } + } +} + +/* Check if the DAC corresponding to 'encoder' is being used by + * someone else. */ +bool nv04_dac_in_use(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct dcb_output *dcb = nouveau_encoder(encoder)->dcb; + + return nv_gf4_disp_arch(encoder->dev) && + (nv04_display(dev)->dac_users[ffs(dcb->or) - 1] & ~(1 << dcb->index)); +} + +static void nv04_dac_dpms(struct drm_encoder *encoder, int mode) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_drm *drm = nouveau_drm(encoder->dev); + + if (nv_encoder->last_dpms == mode) + return; + nv_encoder->last_dpms = mode; + + NV_DEBUG(drm, "Setting dpms mode %d on vga encoder (output %d)\n", + mode, nv_encoder->dcb->index); + + nv04_dac_update_dacclk(encoder, mode == DRM_MODE_DPMS_ON); +} + +static void nv04_dac_save(struct drm_encoder *encoder) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct drm_device *dev = encoder->dev; + + if (nv_gf4_disp_arch(dev)) + nv_encoder->restore.output = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + + nv04_dac_output_offset(encoder)); +} + +static void nv04_dac_restore(struct drm_encoder *encoder) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct drm_device *dev = encoder->dev; + + if (nv_gf4_disp_arch(dev)) + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + nv04_dac_output_offset(encoder), + nv_encoder->restore.output); + + nv_encoder->last_dpms = NV_DPMS_CLEARED; +} + +static void nv04_dac_destroy(struct drm_encoder *encoder) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + + drm_encoder_cleanup(encoder); + kfree(nv_encoder); +} + +static const struct drm_encoder_helper_funcs nv04_dac_helper_funcs = { + .dpms = nv04_dac_dpms, + .save = nv04_dac_save, + .restore = nv04_dac_restore, + .mode_fixup = nv04_dac_mode_fixup, + .prepare = nv04_dac_prepare, + .commit = nv04_dac_commit, + .mode_set = nv04_dac_mode_set, + .detect = nv04_dac_detect +}; + +static const struct drm_encoder_helper_funcs nv17_dac_helper_funcs = { + .dpms = nv04_dac_dpms, + .save = nv04_dac_save, + .restore = nv04_dac_restore, + .mode_fixup = nv04_dac_mode_fixup, + .prepare = nv04_dac_prepare, + .commit = nv04_dac_commit, + .mode_set = nv04_dac_mode_set, + .detect = nv17_dac_detect +}; + +static const struct drm_encoder_funcs nv04_dac_funcs = { + .destroy = nv04_dac_destroy, +}; + +int +nv04_dac_create(struct drm_connector *connector, struct dcb_output *entry) +{ + const struct drm_encoder_helper_funcs *helper; + struct nouveau_encoder *nv_encoder = NULL; + struct drm_device *dev = connector->dev; + struct drm_encoder *encoder; + + nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); + if (!nv_encoder) + return -ENOMEM; + + encoder = to_drm_encoder(nv_encoder); + + nv_encoder->dcb = entry; + nv_encoder->or = ffs(entry->or) - 1; + + if (nv_gf4_disp_arch(dev)) + helper = &nv17_dac_helper_funcs; + else + helper = &nv04_dac_helper_funcs; + + drm_encoder_init(dev, encoder, &nv04_dac_funcs, DRM_MODE_ENCODER_DAC); + drm_encoder_helper_add(encoder, helper); + + encoder->possible_crtcs = entry->heads; + encoder->possible_clones = 0; + + drm_mode_connector_attach_encoder(connector, encoder); + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/dispnv04/dfp.c b/kernel/drivers/gpu/drm/nouveau/dispnv04/dfp.c new file mode 100644 index 000000000..7cfb0cbc9 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/dispnv04/dfp.c @@ -0,0 +1,722 @@ +/* + * Copyright 2003 NVIDIA, Corporation + * Copyright 2006 Dave Airlie + * Copyright 2007 Maarten Maathuis + * 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 (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 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 "nouveau_drm.h" +#include "nouveau_reg.h" +#include "nouveau_encoder.h" +#include "nouveau_connector.h" +#include "nouveau_crtc.h" +#include "hw.h" +#include "nvreg.h" + +#include + +#include + +#define FP_TG_CONTROL_ON (NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS | \ + NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS | \ + NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS) +#define FP_TG_CONTROL_OFF (NV_PRAMDAC_FP_TG_CONTROL_DISPEN_DISABLE | \ + NV_PRAMDAC_FP_TG_CONTROL_HSYNC_DISABLE | \ + NV_PRAMDAC_FP_TG_CONTROL_VSYNC_DISABLE) + +static inline bool is_fpc_off(uint32_t fpc) +{ + return ((fpc & (FP_TG_CONTROL_ON | FP_TG_CONTROL_OFF)) == + FP_TG_CONTROL_OFF); +} + +int nv04_dfp_get_bound_head(struct drm_device *dev, struct dcb_output *dcbent) +{ + /* special case of nv_read_tmds to find crtc associated with an output. + * this does not give a correct answer for off-chip dvi, but there's no + * use for such an answer anyway + */ + int ramdac = (dcbent->or & DCB_OUTPUT_C) >> 2; + + NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_CONTROL, + NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE | 0x4); + return ((NVReadRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_DATA) & 0x8) >> 3) ^ ramdac; +} + +void nv04_dfp_bind_head(struct drm_device *dev, struct dcb_output *dcbent, + int head, bool dl) +{ + /* The BIOS scripts don't do this for us, sadly + * Luckily we do know the values ;-) + * + * head < 0 indicates we wish to force a setting with the overrideval + * (for VT restore etc.) + */ + + int ramdac = (dcbent->or & DCB_OUTPUT_C) >> 2; + uint8_t tmds04 = 0x80; + + if (head != ramdac) + tmds04 = 0x88; + + if (dcbent->type == DCB_OUTPUT_LVDS) + tmds04 |= 0x01; + + nv_write_tmds(dev, dcbent->or, 0, 0x04, tmds04); + + if (dl) /* dual link */ + nv_write_tmds(dev, dcbent->or, 1, 0x04, tmds04 ^ 0x08); +} + +void nv04_dfp_disable(struct drm_device *dev, int head) +{ + struct nv04_crtc_reg *crtcstate = nv04_display(dev)->mode_reg.crtc_reg; + + if (NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL) & + FP_TG_CONTROL_ON) { + /* digital remnants must be cleaned before new crtc + * values programmed. delay is time for the vga stuff + * to realise it's in control again + */ + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL, + FP_TG_CONTROL_OFF); + msleep(50); + } + /* don't inadvertently turn it on when state written later */ + crtcstate[head].fp_control = FP_TG_CONTROL_OFF; + crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX] &= + ~NV_CIO_CRE_LCD_ROUTE_MASK; +} + +void nv04_dfp_update_fp_control(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_crtc *crtc; + struct nouveau_crtc *nv_crtc; + uint32_t *fpc; + + if (mode == DRM_MODE_DPMS_ON) { + nv_crtc = nouveau_crtc(encoder->crtc); + fpc = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index].fp_control; + + if (is_fpc_off(*fpc)) { + /* using saved value is ok, as (is_digital && dpms_on && + * fp_control==OFF) is (at present) *only* true when + * fpc's most recent change was by below "off" code + */ + *fpc = nv_crtc->dpms_saved_fp_control; + } + + nv_crtc->fp_users |= 1 << nouveau_encoder(encoder)->dcb->index; + NVWriteRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_FP_TG_CONTROL, *fpc); + } else { + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + nv_crtc = nouveau_crtc(crtc); + fpc = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index].fp_control; + + nv_crtc->fp_users &= ~(1 << nouveau_encoder(encoder)->dcb->index); + if (!is_fpc_off(*fpc) && !nv_crtc->fp_users) { + nv_crtc->dpms_saved_fp_control = *fpc; + /* cut the FP output */ + *fpc &= ~FP_TG_CONTROL_ON; + *fpc |= FP_TG_CONTROL_OFF; + NVWriteRAMDAC(dev, nv_crtc->index, + NV_PRAMDAC_FP_TG_CONTROL, *fpc); + } + } + } +} + +static struct drm_encoder *get_tmds_slave(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct dcb_output *dcb = nouveau_encoder(encoder)->dcb; + struct drm_encoder *slave; + + if (dcb->type != DCB_OUTPUT_TMDS || dcb->location == DCB_LOC_ON_CHIP) + return NULL; + + /* Some BIOSes (e.g. the one in a Quadro FX1000) report several + * TMDS transmitters at the same I2C address, in the same I2C + * bus. This can still work because in that case one of them is + * always hard-wired to a reasonable configuration using straps, + * and the other one needs to be programmed. + * + * I don't think there's a way to know which is which, even the + * blob programs the one exposed via I2C for *both* heads, so + * let's do the same. + */ + list_for_each_entry(slave, &dev->mode_config.encoder_list, head) { + struct dcb_output *slave_dcb = nouveau_encoder(slave)->dcb; + + if (slave_dcb->type == DCB_OUTPUT_TMDS && get_slave_funcs(slave) && + slave_dcb->tmdsconf.slave_addr == dcb->tmdsconf.slave_addr) + return slave; + } + + return NULL; +} + +static bool nv04_dfp_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_connector *nv_connector = nouveau_encoder_connector_get(nv_encoder); + + if (!nv_connector->native_mode || + nv_connector->scaling_mode == DRM_MODE_SCALE_NONE || + mode->hdisplay > nv_connector->native_mode->hdisplay || + mode->vdisplay > nv_connector->native_mode->vdisplay) { + nv_encoder->mode = *adjusted_mode; + + } else { + nv_encoder->mode = *nv_connector->native_mode; + adjusted_mode->clock = nv_connector->native_mode->clock; + } + + return true; +} + +static void nv04_dfp_prepare_sel_clk(struct drm_device *dev, + struct nouveau_encoder *nv_encoder, int head) +{ + struct nv04_mode_state *state = &nv04_display(dev)->mode_reg; + uint32_t bits1618 = nv_encoder->dcb->or & DCB_OUTPUT_A ? 0x10000 : 0x40000; + + if (nv_encoder->dcb->location != DCB_LOC_ON_CHIP) + return; + + /* SEL_CLK is only used on the primary ramdac + * It toggles spread spectrum PLL output and sets the bindings of PLLs + * to heads on digital outputs + */ + if (head) + state->sel_clk |= bits1618; + else + state->sel_clk &= ~bits1618; + + /* nv30: + * bit 0 NVClk spread spectrum on/off + * bit 2 MemClk spread spectrum on/off + * bit 4 PixClk1 spread spectrum on/off toggle + * bit 6 PixClk2 spread spectrum on/off toggle + * + * nv40 (observations from bios behaviour and mmio traces): + * bits 4&6 as for nv30 + * bits 5&7 head dependent as for bits 4&6, but do not appear with 4&6; + * maybe a different spread mode + * bits 8&10 seen on dual-link dvi outputs, purpose unknown (set by POST scripts) + * The logic behind turning spread spectrum on/off in the first place, + * and which bit-pair to use, is unclear on nv40 (for earlier cards, the fp table + * entry has the necessary info) + */ + if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS && nv04_display(dev)->saved_reg.sel_clk & 0xf0) { + int shift = (nv04_display(dev)->saved_reg.sel_clk & 0x50) ? 0 : 1; + + state->sel_clk &= ~0xf0; + state->sel_clk |= (head ? 0x40 : 0x10) << shift; + } +} + +static void nv04_dfp_prepare(struct drm_encoder *encoder) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + const struct drm_encoder_helper_funcs *helper = encoder->helper_private; + struct drm_device *dev = encoder->dev; + int head = nouveau_crtc(encoder->crtc)->index; + struct nv04_crtc_reg *crtcstate = nv04_display(dev)->mode_reg.crtc_reg; + uint8_t *cr_lcd = &crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX]; + uint8_t *cr_lcd_oth = &crtcstate[head ^ 1].CRTC[NV_CIO_CRE_LCD__INDEX]; + + helper->dpms(encoder, DRM_MODE_DPMS_OFF); + + nv04_dfp_prepare_sel_clk(dev, nv_encoder, head); + + *cr_lcd = (*cr_lcd & ~NV_CIO_CRE_LCD_ROUTE_MASK) | 0x3; + + if (nv_two_heads(dev)) { + if (nv_encoder->dcb->location == DCB_LOC_ON_CHIP) + *cr_lcd |= head ? 0x0 : 0x8; + else { + *cr_lcd |= (nv_encoder->dcb->or << 4) & 0x30; + if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS) + *cr_lcd |= 0x30; + if ((*cr_lcd & 0x30) == (*cr_lcd_oth & 0x30)) { + /* avoid being connected to both crtcs */ + *cr_lcd_oth &= ~0x30; + NVWriteVgaCrtc(dev, head ^ 1, + NV_CIO_CRE_LCD__INDEX, + *cr_lcd_oth); + } + } + } +} + + +static void nv04_dfp_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct nvif_device *device = &nouveau_drm(dev)->device; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); + struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index]; + struct nv04_crtc_reg *savep = &nv04_display(dev)->saved_reg.crtc_reg[nv_crtc->index]; + struct nouveau_connector *nv_connector = nouveau_crtc_connector_get(nv_crtc); + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct drm_display_mode *output_mode = &nv_encoder->mode; + struct drm_connector *connector = &nv_connector->base; + uint32_t mode_ratio, panel_ratio; + + NV_DEBUG(drm, "Output mode on CRTC %d:\n", nv_crtc->index); + drm_mode_debug_printmodeline(output_mode); + + /* Initialize the FP registers in this CRTC. */ + regp->fp_horiz_regs[FP_DISPLAY_END] = output_mode->hdisplay - 1; + regp->fp_horiz_regs[FP_TOTAL] = output_mode->htotal - 1; + if (!nv_gf4_disp_arch(dev) || + (output_mode->hsync_start - output_mode->hdisplay) >= + drm->vbios.digital_min_front_porch) + regp->fp_horiz_regs[FP_CRTC] = output_mode->hdisplay; + else + regp->fp_horiz_regs[FP_CRTC] = output_mode->hsync_start - drm->vbios.digital_min_front_porch - 1; + regp->fp_horiz_regs[FP_SYNC_START] = output_mode->hsync_start - 1; + regp->fp_horiz_regs[FP_SYNC_END] = output_mode->hsync_end - 1; + regp->fp_horiz_regs[FP_VALID_START] = output_mode->hskew; + regp->fp_horiz_regs[FP_VALID_END] = output_mode->hdisplay - 1; + + regp->fp_vert_regs[FP_DISPLAY_END] = output_mode->vdisplay - 1; + regp->fp_vert_regs[FP_TOTAL] = output_mode->vtotal - 1; + regp->fp_vert_regs[FP_CRTC] = output_mode->vtotal - 5 - 1; + regp->fp_vert_regs[FP_SYNC_START] = output_mode->vsync_start - 1; + regp->fp_vert_regs[FP_SYNC_END] = output_mode->vsync_end - 1; + regp->fp_vert_regs[FP_VALID_START] = 0; + regp->fp_vert_regs[FP_VALID_END] = output_mode->vdisplay - 1; + + /* bit26: a bit seen on some g7x, no as yet discernable purpose */ + regp->fp_control = NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS | + (savep->fp_control & (1 << 26 | NV_PRAMDAC_FP_TG_CONTROL_READ_PROG)); + /* Deal with vsync/hsync polarity */ + /* LVDS screens do set this, but modes with +ve syncs are very rare */ + if (output_mode->flags & DRM_MODE_FLAG_PVSYNC) + regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS; + if (output_mode->flags & DRM_MODE_FLAG_PHSYNC) + regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS; + /* panel scaling first, as native would get set otherwise */ + if (nv_connector->scaling_mode == DRM_MODE_SCALE_NONE || + nv_connector->scaling_mode == DRM_MODE_SCALE_CENTER) /* panel handles it */ + regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_MODE_CENTER; + else if (adjusted_mode->hdisplay == output_mode->hdisplay && + adjusted_mode->vdisplay == output_mode->vdisplay) /* native mode */ + regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_MODE_NATIVE; + else /* gpu needs to scale */ + regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_MODE_SCALE; + if (nvif_rd32(device, NV_PEXTDEV_BOOT_0) & NV_PEXTDEV_BOOT_0_STRAP_FP_IFACE_12BIT) + regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12; + if (nv_encoder->dcb->location != DCB_LOC_ON_CHIP && + output_mode->clock > 165000) + regp->fp_control |= (2 << 24); + if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS) { + bool duallink = false, dummy; + if (nv_connector->edid && + nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) { + duallink = (((u8 *)nv_connector->edid)[121] == 2); + } else { + nouveau_bios_parse_lvds_table(dev, output_mode->clock, + &duallink, &dummy); + } + + if (duallink) + regp->fp_control |= (8 << 28); + } else + if (output_mode->clock > 165000) + regp->fp_control |= (8 << 28); + + regp->fp_debug_0 = NV_PRAMDAC_FP_DEBUG_0_YWEIGHT_ROUND | + NV_PRAMDAC_FP_DEBUG_0_XWEIGHT_ROUND | + NV_PRAMDAC_FP_DEBUG_0_YINTERP_BILINEAR | + NV_PRAMDAC_FP_DEBUG_0_XINTERP_BILINEAR | + NV_RAMDAC_FP_DEBUG_0_TMDS_ENABLED | + NV_PRAMDAC_FP_DEBUG_0_YSCALE_ENABLE | + NV_PRAMDAC_FP_DEBUG_0_XSCALE_ENABLE; + + /* We want automatic scaling */ + regp->fp_debug_1 = 0; + /* This can override HTOTAL and VTOTAL */ + regp->fp_debug_2 = 0; + + /* Use 20.12 fixed point format to avoid floats */ + mode_ratio = (1 << 12) * adjusted_mode->hdisplay / adjusted_mode->vdisplay; + panel_ratio = (1 << 12) * output_mode->hdisplay / output_mode->vdisplay; + /* if ratios are equal, SCALE_ASPECT will automatically (and correctly) + * get treated the same as SCALE_FULLSCREEN */ + if (nv_connector->scaling_mode == DRM_MODE_SCALE_ASPECT && + mode_ratio != panel_ratio) { + uint32_t diff, scale; + bool divide_by_2 = nv_gf4_disp_arch(dev); + + if (mode_ratio < panel_ratio) { + /* vertical needs to expand to glass size (automatic) + * horizontal needs to be scaled at vertical scale factor + * to maintain aspect */ + + scale = (1 << 12) * adjusted_mode->vdisplay / output_mode->vdisplay; + regp->fp_debug_1 = NV_PRAMDAC_FP_DEBUG_1_XSCALE_TESTMODE_ENABLE | + XLATE(scale, divide_by_2, NV_PRAMDAC_FP_DEBUG_1_XSCALE_VALUE); + + /* restrict area of screen used, horizontally */ + diff = output_mode->hdisplay - + output_mode->vdisplay * mode_ratio / (1 << 12); + regp->fp_horiz_regs[FP_VALID_START] += diff / 2; + regp->fp_horiz_regs[FP_VALID_END] -= diff / 2; + } + + if (mode_ratio > panel_ratio) { + /* horizontal needs to expand to glass size (automatic) + * vertical needs to be scaled at horizontal scale factor + * to maintain aspect */ + + scale = (1 << 12) * adjusted_mode->hdisplay / output_mode->hdisplay; + regp->fp_debug_1 = NV_PRAMDAC_FP_DEBUG_1_YSCALE_TESTMODE_ENABLE | + XLATE(scale, divide_by_2, NV_PRAMDAC_FP_DEBUG_1_YSCALE_VALUE); + + /* restrict area of screen used, vertically */ + diff = output_mode->vdisplay - + (1 << 12) * output_mode->hdisplay / mode_ratio; + regp->fp_vert_regs[FP_VALID_START] += diff / 2; + regp->fp_vert_regs[FP_VALID_END] -= diff / 2; + } + } + + /* Output property. */ + if ((nv_connector->dithering_mode == DITHERING_MODE_ON) || + (nv_connector->dithering_mode == DITHERING_MODE_AUTO && + encoder->crtc->primary->fb->depth > connector->display_info.bpc * 3)) { + if (drm->device.info.chipset == 0x11) + regp->dither = savep->dither | 0x00010000; + else { + int i; + regp->dither = savep->dither | 0x00000001; + for (i = 0; i < 3; i++) { + regp->dither_regs[i] = 0xe4e4e4e4; + regp->dither_regs[i + 3] = 0x44444444; + } + } + } else { + if (drm->device.info.chipset != 0x11) { + /* reset them */ + int i; + for (i = 0; i < 3; i++) { + regp->dither_regs[i] = savep->dither_regs[i]; + regp->dither_regs[i + 3] = savep->dither_regs[i + 3]; + } + } + regp->dither = savep->dither; + } + + regp->fp_margin_color = 0; +} + +static void nv04_dfp_commit(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + const struct drm_encoder_helper_funcs *helper = encoder->helper_private; + struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct dcb_output *dcbe = nv_encoder->dcb; + int head = nouveau_crtc(encoder->crtc)->index; + struct drm_encoder *slave_encoder; + + if (dcbe->type == DCB_OUTPUT_TMDS) + run_tmds_table(dev, dcbe, head, nv_encoder->mode.clock); + else if (dcbe->type == DCB_OUTPUT_LVDS) + call_lvds_script(dev, dcbe, head, LVDS_RESET, nv_encoder->mode.clock); + + /* update fp_control state for any changes made by scripts, + * so correct value is written at DPMS on */ + nv04_display(dev)->mode_reg.crtc_reg[head].fp_control = + NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL); + + /* This could use refinement for flatpanels, but it should work this way */ + if (drm->device.info.chipset < 0x44) + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0xf0000000); + else + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0x00100000); + + /* Init external transmitters */ + slave_encoder = get_tmds_slave(encoder); + if (slave_encoder) + get_slave_funcs(slave_encoder)->mode_set( + slave_encoder, &nv_encoder->mode, &nv_encoder->mode); + + helper->dpms(encoder, DRM_MODE_DPMS_ON); + + NV_DEBUG(drm, "Output %s is running on CRTC %d using output %c\n", + nouveau_encoder_connector_get(nv_encoder)->base.name, + nv_crtc->index, '@' + ffs(nv_encoder->dcb->or)); +} + +static void nv04_dfp_update_backlight(struct drm_encoder *encoder, int mode) +{ +#ifdef __powerpc__ + struct drm_device *dev = encoder->dev; + struct nvif_device *device = &nouveau_drm(dev)->device; + + /* BIOS scripts usually take care of the backlight, thanks + * Apple for your consistency. + */ + if (dev->pdev->device == 0x0174 || dev->pdev->device == 0x0179 || + dev->pdev->device == 0x0189 || dev->pdev->device == 0x0329) { + if (mode == DRM_MODE_DPMS_ON) { + nv_mask(device, NV_PBUS_DEBUG_DUALHEAD_CTL, 1 << 31, 1 << 31); + nv_mask(device, NV_PCRTC_GPIO_EXT, 3, 1); + } else { + nv_mask(device, NV_PBUS_DEBUG_DUALHEAD_CTL, 1 << 31, 0); + nv_mask(device, NV_PCRTC_GPIO_EXT, 3, 0); + } + } +#endif +} + +static inline bool is_powersaving_dpms(int mode) +{ + return mode != DRM_MODE_DPMS_ON && mode != NV_DPMS_CLEARED; +} + +static void nv04_lvds_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_crtc *crtc = encoder->crtc; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + bool was_powersaving = is_powersaving_dpms(nv_encoder->last_dpms); + + if (nv_encoder->last_dpms == mode) + return; + nv_encoder->last_dpms = mode; + + NV_DEBUG(drm, "Setting dpms mode %d on lvds encoder (output %d)\n", + mode, nv_encoder->dcb->index); + + if (was_powersaving && is_powersaving_dpms(mode)) + return; + + if (nv_encoder->dcb->lvdsconf.use_power_scripts) { + /* when removing an output, crtc may not be set, but PANEL_OFF + * must still be run + */ + int head = crtc ? nouveau_crtc(crtc)->index : + nv04_dfp_get_bound_head(dev, nv_encoder->dcb); + + if (mode == DRM_MODE_DPMS_ON) { + call_lvds_script(dev, nv_encoder->dcb, head, + LVDS_PANEL_ON, nv_encoder->mode.clock); + } else + /* pxclk of 0 is fine for PANEL_OFF, and for a + * disconnected LVDS encoder there is no native_mode + */ + call_lvds_script(dev, nv_encoder->dcb, head, + LVDS_PANEL_OFF, 0); + } + + nv04_dfp_update_backlight(encoder, mode); + nv04_dfp_update_fp_control(encoder, mode); + + if (mode == DRM_MODE_DPMS_ON) + nv04_dfp_prepare_sel_clk(dev, nv_encoder, nouveau_crtc(crtc)->index); + else { + nv04_display(dev)->mode_reg.sel_clk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK); + nv04_display(dev)->mode_reg.sel_clk &= ~0xf0; + } + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, nv04_display(dev)->mode_reg.sel_clk); +} + +static void nv04_tmds_dpms(struct drm_encoder *encoder, int mode) +{ + struct nouveau_drm *drm = nouveau_drm(encoder->dev); + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + + if (nv_encoder->last_dpms == mode) + return; + nv_encoder->last_dpms = mode; + + NV_DEBUG(drm, "Setting dpms mode %d on tmds encoder (output %d)\n", + mode, nv_encoder->dcb->index); + + nv04_dfp_update_backlight(encoder, mode); + nv04_dfp_update_fp_control(encoder, mode); +} + +static void nv04_dfp_save(struct drm_encoder *encoder) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct drm_device *dev = encoder->dev; + + if (nv_two_heads(dev)) + nv_encoder->restore.head = + nv04_dfp_get_bound_head(dev, nv_encoder->dcb); +} + +static void nv04_dfp_restore(struct drm_encoder *encoder) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct drm_device *dev = encoder->dev; + int head = nv_encoder->restore.head; + + if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS) { + struct nouveau_connector *connector = + nouveau_encoder_connector_get(nv_encoder); + + if (connector && connector->native_mode) + call_lvds_script(dev, nv_encoder->dcb, head, + LVDS_PANEL_ON, + connector->native_mode->clock); + + } else if (nv_encoder->dcb->type == DCB_OUTPUT_TMDS) { + int clock = nouveau_hw_pllvals_to_clk + (&nv04_display(dev)->saved_reg.crtc_reg[head].pllvals); + + run_tmds_table(dev, nv_encoder->dcb, head, clock); + } + + nv_encoder->last_dpms = NV_DPMS_CLEARED; +} + +static void nv04_dfp_destroy(struct drm_encoder *encoder) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + + if (get_slave_funcs(encoder)) + get_slave_funcs(encoder)->destroy(encoder); + + drm_encoder_cleanup(encoder); + kfree(nv_encoder); +} + +static void nv04_tmds_slave_init(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct dcb_output *dcb = nouveau_encoder(encoder)->dcb; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_i2c *i2c = nvxx_i2c(&drm->device); + struct nvkm_i2c_port *port = i2c->find(i2c, 2); + struct nvkm_i2c_board_info info[] = { + { + { + .type = "sil164", + .addr = (dcb->tmdsconf.slave_addr == 0x7 ? 0x3a : 0x38), + .platform_data = &(struct sil164_encoder_params) { + SIL164_INPUT_EDGE_RISING + } + }, 0 + }, + { } + }; + int type; + + if (!nv_gf4_disp_arch(dev) || !port || + get_tmds_slave(encoder)) + return; + + type = i2c->identify(i2c, 2, "TMDS transmitter", info, NULL, NULL); + if (type < 0) + return; + + drm_i2c_encoder_init(dev, to_encoder_slave(encoder), + &port->adapter, &info[type].dev); +} + +static const struct drm_encoder_helper_funcs nv04_lvds_helper_funcs = { + .dpms = nv04_lvds_dpms, + .save = nv04_dfp_save, + .restore = nv04_dfp_restore, + .mode_fixup = nv04_dfp_mode_fixup, + .prepare = nv04_dfp_prepare, + .commit = nv04_dfp_commit, + .mode_set = nv04_dfp_mode_set, + .detect = NULL, +}; + +static const struct drm_encoder_helper_funcs nv04_tmds_helper_funcs = { + .dpms = nv04_tmds_dpms, + .save = nv04_dfp_save, + .restore = nv04_dfp_restore, + .mode_fixup = nv04_dfp_mode_fixup, + .prepare = nv04_dfp_prepare, + .commit = nv04_dfp_commit, + .mode_set = nv04_dfp_mode_set, + .detect = NULL, +}; + +static const struct drm_encoder_funcs nv04_dfp_funcs = { + .destroy = nv04_dfp_destroy, +}; + +int +nv04_dfp_create(struct drm_connector *connector, struct dcb_output *entry) +{ + const struct drm_encoder_helper_funcs *helper; + struct nouveau_encoder *nv_encoder = NULL; + struct drm_encoder *encoder; + int type; + + switch (entry->type) { + case DCB_OUTPUT_TMDS: + type = DRM_MODE_ENCODER_TMDS; + helper = &nv04_tmds_helper_funcs; + break; + case DCB_OUTPUT_LVDS: + type = DRM_MODE_ENCODER_LVDS; + helper = &nv04_lvds_helper_funcs; + break; + default: + return -EINVAL; + } + + nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); + if (!nv_encoder) + return -ENOMEM; + + encoder = to_drm_encoder(nv_encoder); + + nv_encoder->dcb = entry; + nv_encoder->or = ffs(entry->or) - 1; + + drm_encoder_init(connector->dev, encoder, &nv04_dfp_funcs, type); + drm_encoder_helper_add(encoder, helper); + + encoder->possible_crtcs = entry->heads; + encoder->possible_clones = 0; + + if (entry->type == DCB_OUTPUT_TMDS && + entry->location != DCB_LOC_ON_CHIP) + nv04_tmds_slave_init(encoder); + + drm_mode_connector_attach_encoder(connector, encoder); + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/dispnv04/disp.c b/kernel/drivers/gpu/drm/nouveau/dispnv04/disp.c new file mode 100644 index 000000000..4131be550 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/dispnv04/disp.c @@ -0,0 +1,190 @@ +/* + * 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. + * + * Author: Ben Skeggs + */ + +#include +#include + +#include "nouveau_drm.h" +#include "nouveau_reg.h" +#include "hw.h" +#include "nouveau_encoder.h" +#include "nouveau_connector.h" + +int +nv04_display_create(struct drm_device *dev) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_i2c *i2c = nvxx_i2c(&drm->device); + struct dcb_table *dcb = &drm->vbios.dcb; + struct drm_connector *connector, *ct; + struct drm_encoder *encoder; + struct drm_crtc *crtc; + struct nv04_display *disp; + int i, ret; + + disp = kzalloc(sizeof(*disp), GFP_KERNEL); + if (!disp) + return -ENOMEM; + + nvif_object_map(nvif_object(&drm->device)); + + nouveau_display(dev)->priv = disp; + nouveau_display(dev)->dtor = nv04_display_destroy; + nouveau_display(dev)->init = nv04_display_init; + nouveau_display(dev)->fini = nv04_display_fini; + + nouveau_hw_save_vga_fonts(dev, 1); + + nv04_crtc_create(dev, 0); + if (nv_two_heads(dev)) + nv04_crtc_create(dev, 1); + + for (i = 0; i < dcb->entries; i++) { + struct dcb_output *dcbent = &dcb->entry[i]; + + connector = nouveau_connector_create(dev, dcbent->connector); + if (IS_ERR(connector)) + continue; + + switch (dcbent->type) { + case DCB_OUTPUT_ANALOG: + ret = nv04_dac_create(connector, dcbent); + break; + case DCB_OUTPUT_LVDS: + case DCB_OUTPUT_TMDS: + ret = nv04_dfp_create(connector, dcbent); + break; + case DCB_OUTPUT_TV: + if (dcbent->location == DCB_LOC_ON_CHIP) + ret = nv17_tv_create(connector, dcbent); + else + ret = nv04_tv_create(connector, dcbent); + break; + default: + NV_WARN(drm, "DCB type %d not known\n", dcbent->type); + continue; + } + + if (ret) + continue; + } + + list_for_each_entry_safe(connector, ct, + &dev->mode_config.connector_list, head) { + if (!connector->encoder_ids[0]) { + NV_WARN(drm, "%s has no encoders, removing\n", + connector->name); + connector->funcs->destroy(connector); + } + } + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + nv_encoder->i2c = i2c->find(i2c, nv_encoder->dcb->i2c_index); + } + + /* Save previous state */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) + crtc->funcs->save(crtc); + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + const struct drm_encoder_helper_funcs *func = encoder->helper_private; + + func->save(encoder); + } + + nouveau_overlay_init(dev); + + return 0; +} + +void +nv04_display_destroy(struct drm_device *dev) +{ + struct nv04_display *disp = nv04_display(dev); + struct nouveau_drm *drm = nouveau_drm(dev); + struct drm_encoder *encoder; + struct drm_crtc *crtc; + + /* Turn every CRTC off. */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct drm_mode_set modeset = { + .crtc = crtc, + }; + + drm_mode_set_config_internal(&modeset); + } + + /* Restore state */ + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + const struct drm_encoder_helper_funcs *func = encoder->helper_private; + + func->restore(encoder); + } + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) + crtc->funcs->restore(crtc); + + nouveau_hw_save_vga_fonts(dev, 0); + + nouveau_display(dev)->priv = NULL; + kfree(disp); + + nvif_object_unmap(nvif_object(&drm->device)); +} + +int +nv04_display_init(struct drm_device *dev) +{ + struct drm_encoder *encoder; + struct drm_crtc *crtc; + + /* meh.. modeset apparently doesn't setup all the regs and depends + * on pre-existing state, for now load the state of the card *before* + * nouveau was loaded, and then do a modeset. + * + * best thing to do probably is to make save/restore routines not + * save/restore "pre-load" state, but more general so we can save + * on suspend too. + */ + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + const struct drm_encoder_helper_funcs *func = encoder->helper_private; + + func->restore(encoder); + } + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) + crtc->funcs->restore(crtc); + + return 0; +} + +void +nv04_display_fini(struct drm_device *dev) +{ + /* disable vblank interrupts */ + NVWriteCRTC(dev, 0, NV_PCRTC_INTR_EN_0, 0); + if (nv_two_heads(dev)) + NVWriteCRTC(dev, 1, NV_PCRTC_INTR_EN_0, 0); +} diff --git a/kernel/drivers/gpu/drm/nouveau/dispnv04/disp.h b/kernel/drivers/gpu/drm/nouveau/dispnv04/disp.h new file mode 100644 index 000000000..c910c5d5c --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/dispnv04/disp.h @@ -0,0 +1,186 @@ +#ifndef __NV04_DISPLAY_H__ +#define __NV04_DISPLAY_H__ + +#include + +#include "nouveau_display.h" + +enum nv04_fp_display_regs { + FP_DISPLAY_END, + FP_TOTAL, + FP_CRTC, + FP_SYNC_START, + FP_SYNC_END, + FP_VALID_START, + FP_VALID_END +}; + +struct nv04_crtc_reg { + unsigned char MiscOutReg; + uint8_t CRTC[0xa0]; + uint8_t CR58[0x10]; + uint8_t Sequencer[5]; + uint8_t Graphics[9]; + uint8_t Attribute[21]; + unsigned char DAC[768]; + + /* PCRTC regs */ + uint32_t fb_start; + uint32_t crtc_cfg; + uint32_t cursor_cfg; + uint32_t gpio_ext; + uint32_t crtc_830; + uint32_t crtc_834; + uint32_t crtc_850; + uint32_t crtc_eng_ctrl; + + /* PRAMDAC regs */ + uint32_t nv10_cursync; + struct nvkm_pll_vals pllvals; + uint32_t ramdac_gen_ctrl; + uint32_t ramdac_630; + uint32_t ramdac_634; + uint32_t tv_setup; + uint32_t tv_vtotal; + uint32_t tv_vskew; + uint32_t tv_vsync_delay; + uint32_t tv_htotal; + uint32_t tv_hskew; + uint32_t tv_hsync_delay; + uint32_t tv_hsync_delay2; + uint32_t fp_horiz_regs[7]; + uint32_t fp_vert_regs[7]; + uint32_t dither; + uint32_t fp_control; + uint32_t dither_regs[6]; + uint32_t fp_debug_0; + uint32_t fp_debug_1; + uint32_t fp_debug_2; + uint32_t fp_margin_color; + uint32_t ramdac_8c0; + uint32_t ramdac_a20; + uint32_t ramdac_a24; + uint32_t ramdac_a34; + uint32_t ctv_regs[38]; +}; + +struct nv04_output_reg { + uint32_t output; + int head; +}; + +struct nv04_mode_state { + struct nv04_crtc_reg crtc_reg[2]; + uint32_t pllsel; + uint32_t sel_clk; +}; + +struct nv04_display { + struct nv04_mode_state mode_reg; + struct nv04_mode_state saved_reg; + uint32_t saved_vga_font[4][16384]; + uint32_t dac_users[4]; + struct nouveau_bo *image[2]; +}; + +static inline struct nv04_display * +nv04_display(struct drm_device *dev) +{ + return nouveau_display(dev)->priv; +} + +/* nv04_display.c */ +int nv04_display_create(struct drm_device *); +void nv04_display_destroy(struct drm_device *); +int nv04_display_init(struct drm_device *); +void nv04_display_fini(struct drm_device *); + +/* nv04_crtc.c */ +int nv04_crtc_create(struct drm_device *, int index); + +/* nv04_dac.c */ +int nv04_dac_create(struct drm_connector *, struct dcb_output *); +uint32_t nv17_dac_sample_load(struct drm_encoder *encoder); +int nv04_dac_output_offset(struct drm_encoder *encoder); +void nv04_dac_update_dacclk(struct drm_encoder *encoder, bool enable); +bool nv04_dac_in_use(struct drm_encoder *encoder); + +/* nv04_dfp.c */ +int nv04_dfp_create(struct drm_connector *, struct dcb_output *); +int nv04_dfp_get_bound_head(struct drm_device *dev, struct dcb_output *dcbent); +void nv04_dfp_bind_head(struct drm_device *dev, struct dcb_output *dcbent, + int head, bool dl); +void nv04_dfp_disable(struct drm_device *dev, int head); +void nv04_dfp_update_fp_control(struct drm_encoder *encoder, int mode); + +/* nv04_tv.c */ +int nv04_tv_identify(struct drm_device *dev, int i2c_index); +int nv04_tv_create(struct drm_connector *, struct dcb_output *); + +/* nv17_tv.c */ +int nv17_tv_create(struct drm_connector *, struct dcb_output *); + +/* overlay.c */ +void nouveau_overlay_init(struct drm_device *dev); + +static inline bool +nv_two_heads(struct drm_device *dev) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + const int impl = dev->pdev->device & 0x0ff0; + + if (drm->device.info.family >= NV_DEVICE_INFO_V0_CELSIUS && impl != 0x0100 && + impl != 0x0150 && impl != 0x01a0 && impl != 0x0200) + return true; + + return false; +} + +static inline bool +nv_gf4_disp_arch(struct drm_device *dev) +{ + return nv_two_heads(dev) && (dev->pdev->device & 0x0ff0) != 0x0110; +} + +static inline bool +nv_two_reg_pll(struct drm_device *dev) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + const int impl = dev->pdev->device & 0x0ff0; + + if (impl == 0x0310 || impl == 0x0340 || drm->device.info.family >= NV_DEVICE_INFO_V0_CURIE) + return true; + return false; +} + +static inline bool +nv_match_device(struct drm_device *dev, unsigned device, + unsigned sub_vendor, unsigned sub_device) +{ + return dev->pdev->device == device && + dev->pdev->subsystem_vendor == sub_vendor && + dev->pdev->subsystem_device == sub_device; +} + +#include +#include + +static inline void +nouveau_bios_run_init_table(struct drm_device *dev, u16 table, + struct dcb_output *outp, int crtc) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_bios *bios = nvxx_bios(&drm->device); + struct nvbios_init init = { + .subdev = nv_subdev(bios), + .bios = bios, + .offset = table, + .outp = outp, + .crtc = crtc, + .execute = 1, + }; + + nvbios_exec(&init); +} + +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/dispnv04/hw.c b/kernel/drivers/gpu/drm/nouveau/dispnv04/hw.c new file mode 100644 index 000000000..42e07afc4 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/dispnv04/hw.c @@ -0,0 +1,825 @@ +/* + * Copyright 2006 Dave Airlie + * Copyright 2007 Maarten Maathuis + * 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 "nouveau_drm.h" +#include "hw.h" + +#include + +#define CHIPSET_NFORCE 0x01a0 +#define CHIPSET_NFORCE2 0x01f0 + +/* + * misc hw access wrappers/control functions + */ + +void +NVWriteVgaSeq(struct drm_device *dev, int head, uint8_t index, uint8_t value) +{ + NVWritePRMVIO(dev, head, NV_PRMVIO_SRX, index); + NVWritePRMVIO(dev, head, NV_PRMVIO_SR, value); +} + +uint8_t +NVReadVgaSeq(struct drm_device *dev, int head, uint8_t index) +{ + NVWritePRMVIO(dev, head, NV_PRMVIO_SRX, index); + return NVReadPRMVIO(dev, head, NV_PRMVIO_SR); +} + +void +NVWriteVgaGr(struct drm_device *dev, int head, uint8_t index, uint8_t value) +{ + NVWritePRMVIO(dev, head, NV_PRMVIO_GRX, index); + NVWritePRMVIO(dev, head, NV_PRMVIO_GX, value); +} + +uint8_t +NVReadVgaGr(struct drm_device *dev, int head, uint8_t index) +{ + NVWritePRMVIO(dev, head, NV_PRMVIO_GRX, index); + return NVReadPRMVIO(dev, head, NV_PRMVIO_GX); +} + +/* 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 + */ +void +NVSetOwner(struct drm_device *dev, int owner) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + + if (owner == 1) + owner *= 3; + + if (drm->device.info.chipset == 0x11) { + /* This might seem stupid, but the blob does it and + * omitting it often locks the system up. + */ + NVReadVgaCrtc(dev, 0, NV_CIO_SR_LOCK_INDEX); + NVReadVgaCrtc(dev, 1, NV_CIO_SR_LOCK_INDEX); + } + + /* CR44 is always changed on CRTC0 */ + NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_44, owner); + + if (drm->device.info.chipset == 0x11) { /* set me harder */ + NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_2E, owner); + NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_2E, owner); + } +} + +void +NVBlankScreen(struct drm_device *dev, int head, bool blank) +{ + unsigned char seq1; + + if (nv_two_heads(dev)) + NVSetOwner(dev, head); + + seq1 = NVReadVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX); + + NVVgaSeqReset(dev, head, true); + if (blank) + NVWriteVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX, seq1 | 0x20); + else + NVWriteVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX, seq1 & ~0x20); + NVVgaSeqReset(dev, head, false); +} + +/* + * PLL getting + */ + +static void +nouveau_hw_decode_pll(struct drm_device *dev, uint32_t reg1, uint32_t pll1, + uint32_t pll2, struct nvkm_pll_vals *pllvals) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + + /* to force parsing as single stage (i.e. nv40 vplls) pass pll2 as 0 */ + + /* log2P is & 0x7 as never more than 7, and nv30/35 only uses 3 bits */ + pllvals->log2P = (pll1 >> 16) & 0x7; + pllvals->N2 = pllvals->M2 = 1; + + if (reg1 <= 0x405c) { + pllvals->NM1 = pll2 & 0xffff; + /* single stage NVPLL and VPLLs use 1 << 8, MPLL uses 1 << 12 */ + if (!(pll1 & 0x1100)) + pllvals->NM2 = pll2 >> 16; + } else { + pllvals->NM1 = pll1 & 0xffff; + if (nv_two_reg_pll(dev) && pll2 & NV31_RAMDAC_ENABLE_VCO2) + pllvals->NM2 = pll2 & 0xffff; + else if (drm->device.info.chipset == 0x30 || drm->device.info.chipset == 0x35) { + pllvals->M1 &= 0xf; /* only 4 bits */ + if (pll1 & NV30_RAMDAC_ENABLE_VCO2) { + pllvals->M2 = (pll1 >> 4) & 0x7; + pllvals->N2 = ((pll1 >> 21) & 0x18) | + ((pll1 >> 19) & 0x7); + } + } + } +} + +int +nouveau_hw_get_pllvals(struct drm_device *dev, enum nvbios_pll_type plltype, + struct nvkm_pll_vals *pllvals) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvif_device *device = &drm->device; + struct nvkm_bios *bios = nvxx_bios(device); + uint32_t reg1, pll1, pll2 = 0; + struct nvbios_pll pll_lim; + int ret; + + ret = nvbios_pll_parse(bios, plltype, &pll_lim); + if (ret || !(reg1 = pll_lim.reg)) + return -ENOENT; + + pll1 = nvif_rd32(device, reg1); + if (reg1 <= 0x405c) + pll2 = nvif_rd32(device, reg1 + 4); + else if (nv_two_reg_pll(dev)) { + uint32_t reg2 = reg1 + (reg1 == NV_RAMDAC_VPLL2 ? 0x5c : 0x70); + + pll2 = nvif_rd32(device, reg2); + } + + if (drm->device.info.family == NV_DEVICE_INFO_V0_CELSIUS && reg1 >= NV_PRAMDAC_VPLL_COEFF) { + uint32_t ramdac580 = NVReadRAMDAC(dev, 0, NV_PRAMDAC_580); + + /* check whether vpll has been forced into single stage mode */ + if (reg1 == NV_PRAMDAC_VPLL_COEFF) { + if (ramdac580 & NV_RAMDAC_580_VPLL1_ACTIVE) + pll2 = 0; + } else + if (ramdac580 & NV_RAMDAC_580_VPLL2_ACTIVE) + pll2 = 0; + } + + nouveau_hw_decode_pll(dev, reg1, pll1, pll2, pllvals); + pllvals->refclk = pll_lim.refclk; + return 0; +} + +int +nouveau_hw_pllvals_to_clk(struct nvkm_pll_vals *pv) +{ + /* Avoid divide by zero if called at an inappropriate time */ + if (!pv->M1 || !pv->M2) + return 0; + + return pv->N1 * pv->N2 * pv->refclk / (pv->M1 * pv->M2) >> pv->log2P; +} + +int +nouveau_hw_get_clock(struct drm_device *dev, enum nvbios_pll_type plltype) +{ + struct nvkm_pll_vals pllvals; + int ret; + + if (plltype == PLL_MEMORY && + (dev->pdev->device & 0x0ff0) == CHIPSET_NFORCE) { + uint32_t mpllP; + + pci_read_config_dword(pci_get_bus_and_slot(0, 3), 0x6c, &mpllP); + if (!mpllP) + mpllP = 4; + + return 400000 / mpllP; + } else + if (plltype == PLL_MEMORY && + (dev->pdev->device & 0xff0) == CHIPSET_NFORCE2) { + uint32_t clock; + + pci_read_config_dword(pci_get_bus_and_slot(0, 5), 0x4c, &clock); + return clock; + } + + ret = nouveau_hw_get_pllvals(dev, plltype, &pllvals); + if (ret) + return ret; + + return nouveau_hw_pllvals_to_clk(&pllvals); +} + +static void +nouveau_hw_fix_bad_vpll(struct drm_device *dev, int head) +{ + /* the vpll on an unused head can come up with a random value, way + * beyond the pll limits. for some reason this causes the chip to + * lock up when reading the dac palette regs, so set a valid pll here + * when such a condition detected. only seen on nv11 to date + */ + + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvif_device *device = &drm->device; + struct nvkm_clk *clk = nvxx_clk(device); + struct nvkm_bios *bios = nvxx_bios(device); + struct nvbios_pll pll_lim; + struct nvkm_pll_vals pv; + enum nvbios_pll_type pll = head ? PLL_VPLL1 : PLL_VPLL0; + + if (nvbios_pll_parse(bios, pll, &pll_lim)) + return; + nouveau_hw_get_pllvals(dev, pll, &pv); + + if (pv.M1 >= pll_lim.vco1.min_m && pv.M1 <= pll_lim.vco1.max_m && + pv.N1 >= pll_lim.vco1.min_n && pv.N1 <= pll_lim.vco1.max_n && + pv.log2P <= pll_lim.max_p) + return; + + NV_WARN(drm, "VPLL %d outwith limits, attempting to fix\n", head + 1); + + /* set lowest clock within static limits */ + pv.M1 = pll_lim.vco1.max_m; + pv.N1 = pll_lim.vco1.min_n; + pv.log2P = pll_lim.max_p_usable; + clk->pll_prog(clk, pll_lim.reg, &pv); +} + +/* + * vga font save/restore + */ + +static void nouveau_vga_font_io(struct drm_device *dev, + void __iomem *iovram, + bool save, unsigned plane) +{ + unsigned i; + + NVWriteVgaSeq(dev, 0, NV_VIO_SR_PLANE_MASK_INDEX, 1 << plane); + NVWriteVgaGr(dev, 0, NV_VIO_GX_READ_MAP_INDEX, plane); + for (i = 0; i < 16384; i++) { + if (save) { + nv04_display(dev)->saved_vga_font[plane][i] = + ioread32_native(iovram + i * 4); + } else { + iowrite32_native(nv04_display(dev)->saved_vga_font[plane][i], + iovram + i * 4); + } + } +} + +void +nouveau_hw_save_vga_fonts(struct drm_device *dev, bool save) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + uint8_t misc, gr4, gr5, gr6, seq2, seq4; + bool graphicsmode; + unsigned plane; + void __iomem *iovram; + + if (nv_two_heads(dev)) + NVSetOwner(dev, 0); + + NVSetEnablePalette(dev, 0, true); + graphicsmode = NVReadVgaAttr(dev, 0, NV_CIO_AR_MODE_INDEX) & 1; + NVSetEnablePalette(dev, 0, false); + + if (graphicsmode) /* graphics mode => framebuffer => no need to save */ + return; + + NV_INFO(drm, "%sing VGA fonts\n", save ? "Sav" : "Restor"); + + /* map first 64KiB of VRAM, holds VGA fonts etc */ + iovram = ioremap(pci_resource_start(dev->pdev, 1), 65536); + if (!iovram) { + NV_ERROR(drm, "Failed to map VRAM, " + "cannot save/restore VGA fonts.\n"); + return; + } + + if (nv_two_heads(dev)) + NVBlankScreen(dev, 1, true); + NVBlankScreen(dev, 0, true); + + /* save control regs */ + misc = NVReadPRMVIO(dev, 0, NV_PRMVIO_MISC__READ); + seq2 = NVReadVgaSeq(dev, 0, NV_VIO_SR_PLANE_MASK_INDEX); + seq4 = NVReadVgaSeq(dev, 0, NV_VIO_SR_MEM_MODE_INDEX); + gr4 = NVReadVgaGr(dev, 0, NV_VIO_GX_READ_MAP_INDEX); + gr5 = NVReadVgaGr(dev, 0, NV_VIO_GX_MODE_INDEX); + gr6 = NVReadVgaGr(dev, 0, NV_VIO_GX_MISC_INDEX); + + NVWritePRMVIO(dev, 0, NV_PRMVIO_MISC__WRITE, 0x67); + NVWriteVgaSeq(dev, 0, NV_VIO_SR_MEM_MODE_INDEX, 0x6); + NVWriteVgaGr(dev, 0, NV_VIO_GX_MODE_INDEX, 0x0); + NVWriteVgaGr(dev, 0, NV_VIO_GX_MISC_INDEX, 0x5); + + /* store font in planes 0..3 */ + for (plane = 0; plane < 4; plane++) + nouveau_vga_font_io(dev, iovram, save, plane); + + /* restore control regs */ + NVWritePRMVIO(dev, 0, NV_PRMVIO_MISC__WRITE, misc); + NVWriteVgaGr(dev, 0, NV_VIO_GX_READ_MAP_INDEX, gr4); + NVWriteVgaGr(dev, 0, NV_VIO_GX_MODE_INDEX, gr5); + NVWriteVgaGr(dev, 0, NV_VIO_GX_MISC_INDEX, gr6); + NVWriteVgaSeq(dev, 0, NV_VIO_SR_PLANE_MASK_INDEX, seq2); + NVWriteVgaSeq(dev, 0, NV_VIO_SR_MEM_MODE_INDEX, seq4); + + if (nv_two_heads(dev)) + NVBlankScreen(dev, 1, false); + NVBlankScreen(dev, 0, false); + + iounmap(iovram); +} + +/* + * mode state save/load + */ + +static void +rd_cio_state(struct drm_device *dev, int head, + struct nv04_crtc_reg *crtcstate, int index) +{ + crtcstate->CRTC[index] = NVReadVgaCrtc(dev, head, index); +} + +static void +wr_cio_state(struct drm_device *dev, int head, + struct nv04_crtc_reg *crtcstate, int index) +{ + NVWriteVgaCrtc(dev, head, index, crtcstate->CRTC[index]); +} + +static void +nv_save_state_ramdac(struct drm_device *dev, int head, + struct nv04_mode_state *state) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nv04_crtc_reg *regp = &state->crtc_reg[head]; + int i; + + if (drm->device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) + regp->nv10_cursync = NVReadRAMDAC(dev, head, NV_RAMDAC_NV10_CURSYNC); + + nouveau_hw_get_pllvals(dev, head ? PLL_VPLL1 : PLL_VPLL0, ®p->pllvals); + state->pllsel = NVReadRAMDAC(dev, 0, NV_PRAMDAC_PLL_COEFF_SELECT); + if (nv_two_heads(dev)) + state->sel_clk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK); + if (drm->device.info.chipset == 0x11) + regp->dither = NVReadRAMDAC(dev, head, NV_RAMDAC_DITHER_NV11); + + regp->ramdac_gen_ctrl = NVReadRAMDAC(dev, head, NV_PRAMDAC_GENERAL_CONTROL); + + if (nv_gf4_disp_arch(dev)) + regp->ramdac_630 = NVReadRAMDAC(dev, head, NV_PRAMDAC_630); + if (drm->device.info.chipset >= 0x30) + regp->ramdac_634 = NVReadRAMDAC(dev, head, NV_PRAMDAC_634); + + regp->tv_setup = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_SETUP); + regp->tv_vtotal = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_VTOTAL); + regp->tv_vskew = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_VSKEW); + regp->tv_vsync_delay = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_VSYNC_DELAY); + regp->tv_htotal = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_HTOTAL); + regp->tv_hskew = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_HSKEW); + regp->tv_hsync_delay = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_HSYNC_DELAY); + regp->tv_hsync_delay2 = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_HSYNC_DELAY2); + + for (i = 0; i < 7; i++) { + uint32_t ramdac_reg = NV_PRAMDAC_FP_VDISPLAY_END + (i * 4); + regp->fp_vert_regs[i] = NVReadRAMDAC(dev, head, ramdac_reg); + regp->fp_horiz_regs[i] = NVReadRAMDAC(dev, head, ramdac_reg + 0x20); + } + + if (nv_gf4_disp_arch(dev)) { + regp->dither = NVReadRAMDAC(dev, head, NV_RAMDAC_FP_DITHER); + for (i = 0; i < 3; i++) { + regp->dither_regs[i] = NVReadRAMDAC(dev, head, NV_PRAMDAC_850 + i * 4); + regp->dither_regs[i + 3] = NVReadRAMDAC(dev, head, NV_PRAMDAC_85C + i * 4); + } + } + + regp->fp_control = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL); + regp->fp_debug_0 = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_0); + if (!nv_gf4_disp_arch(dev) && head == 0) { + /* early chips don't allow access to PRAMDAC_TMDS_* without + * the head A FPCLK on (nv11 even locks up) */ + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_FP_DEBUG_0, regp->fp_debug_0 & + ~NV_PRAMDAC_FP_DEBUG_0_PWRDOWN_FPCLK); + } + regp->fp_debug_1 = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_1); + regp->fp_debug_2 = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_2); + + regp->fp_margin_color = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_MARGIN_COLOR); + + if (nv_gf4_disp_arch(dev)) + regp->ramdac_8c0 = NVReadRAMDAC(dev, head, NV_PRAMDAC_8C0); + + if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE) { + regp->ramdac_a20 = NVReadRAMDAC(dev, head, NV_PRAMDAC_A20); + regp->ramdac_a24 = NVReadRAMDAC(dev, head, NV_PRAMDAC_A24); + regp->ramdac_a34 = NVReadRAMDAC(dev, head, NV_PRAMDAC_A34); + + for (i = 0; i < 38; i++) + regp->ctv_regs[i] = NVReadRAMDAC(dev, head, + NV_PRAMDAC_CTV + 4*i); + } +} + +static void +nv_load_state_ramdac(struct drm_device *dev, int head, + struct nv04_mode_state *state) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_clk *clk = nvxx_clk(&drm->device); + struct nv04_crtc_reg *regp = &state->crtc_reg[head]; + uint32_t pllreg = head ? NV_RAMDAC_VPLL2 : NV_PRAMDAC_VPLL_COEFF; + int i; + + if (drm->device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) + NVWriteRAMDAC(dev, head, NV_RAMDAC_NV10_CURSYNC, regp->nv10_cursync); + + clk->pll_prog(clk, pllreg, ®p->pllvals); + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_PLL_COEFF_SELECT, state->pllsel); + if (nv_two_heads(dev)) + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, state->sel_clk); + if (drm->device.info.chipset == 0x11) + NVWriteRAMDAC(dev, head, NV_RAMDAC_DITHER_NV11, regp->dither); + + NVWriteRAMDAC(dev, head, NV_PRAMDAC_GENERAL_CONTROL, regp->ramdac_gen_ctrl); + + if (nv_gf4_disp_arch(dev)) + NVWriteRAMDAC(dev, head, NV_PRAMDAC_630, regp->ramdac_630); + if (drm->device.info.chipset >= 0x30) + NVWriteRAMDAC(dev, head, NV_PRAMDAC_634, regp->ramdac_634); + + NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_SETUP, regp->tv_setup); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_VTOTAL, regp->tv_vtotal); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_VSKEW, regp->tv_vskew); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_VSYNC_DELAY, regp->tv_vsync_delay); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_HTOTAL, regp->tv_htotal); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_HSKEW, regp->tv_hskew); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_HSYNC_DELAY, regp->tv_hsync_delay); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_HSYNC_DELAY2, regp->tv_hsync_delay2); + + for (i = 0; i < 7; i++) { + uint32_t ramdac_reg = NV_PRAMDAC_FP_VDISPLAY_END + (i * 4); + + NVWriteRAMDAC(dev, head, ramdac_reg, regp->fp_vert_regs[i]); + NVWriteRAMDAC(dev, head, ramdac_reg + 0x20, regp->fp_horiz_regs[i]); + } + + if (nv_gf4_disp_arch(dev)) { + NVWriteRAMDAC(dev, head, NV_RAMDAC_FP_DITHER, regp->dither); + for (i = 0; i < 3; i++) { + NVWriteRAMDAC(dev, head, NV_PRAMDAC_850 + i * 4, regp->dither_regs[i]); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_85C + i * 4, regp->dither_regs[i + 3]); + } + } + + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL, regp->fp_control); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_0, regp->fp_debug_0); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_1, regp->fp_debug_1); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_2, regp->fp_debug_2); + + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_MARGIN_COLOR, regp->fp_margin_color); + + if (nv_gf4_disp_arch(dev)) + NVWriteRAMDAC(dev, head, NV_PRAMDAC_8C0, regp->ramdac_8c0); + + if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE) { + NVWriteRAMDAC(dev, head, NV_PRAMDAC_A20, regp->ramdac_a20); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_A24, regp->ramdac_a24); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_A34, regp->ramdac_a34); + + for (i = 0; i < 38; i++) + NVWriteRAMDAC(dev, head, + NV_PRAMDAC_CTV + 4*i, regp->ctv_regs[i]); + } +} + +static void +nv_save_state_vga(struct drm_device *dev, int head, + struct nv04_mode_state *state) +{ + struct nv04_crtc_reg *regp = &state->crtc_reg[head]; + int i; + + regp->MiscOutReg = NVReadPRMVIO(dev, head, NV_PRMVIO_MISC__READ); + + for (i = 0; i < 25; i++) + rd_cio_state(dev, head, regp, i); + + NVSetEnablePalette(dev, head, true); + for (i = 0; i < 21; i++) + regp->Attribute[i] = NVReadVgaAttr(dev, head, i); + NVSetEnablePalette(dev, head, false); + + for (i = 0; i < 9; i++) + regp->Graphics[i] = NVReadVgaGr(dev, head, i); + + for (i = 0; i < 5; i++) + regp->Sequencer[i] = NVReadVgaSeq(dev, head, i); +} + +static void +nv_load_state_vga(struct drm_device *dev, int head, + struct nv04_mode_state *state) +{ + struct nv04_crtc_reg *regp = &state->crtc_reg[head]; + int i; + + NVWritePRMVIO(dev, head, NV_PRMVIO_MISC__WRITE, regp->MiscOutReg); + + for (i = 0; i < 5; i++) + NVWriteVgaSeq(dev, head, i, regp->Sequencer[i]); + + nv_lock_vga_crtc_base(dev, head, false); + for (i = 0; i < 25; i++) + wr_cio_state(dev, head, regp, i); + nv_lock_vga_crtc_base(dev, head, true); + + for (i = 0; i < 9; i++) + NVWriteVgaGr(dev, head, i, regp->Graphics[i]); + + NVSetEnablePalette(dev, head, true); + for (i = 0; i < 21; i++) + NVWriteVgaAttr(dev, head, i, regp->Attribute[i]); + NVSetEnablePalette(dev, head, false); +} + +static void +nv_save_state_ext(struct drm_device *dev, int head, + struct nv04_mode_state *state) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nv04_crtc_reg *regp = &state->crtc_reg[head]; + int i; + + rd_cio_state(dev, head, regp, NV_CIO_CRE_LCD__INDEX); + rd_cio_state(dev, head, regp, NV_CIO_CRE_RPC0_INDEX); + rd_cio_state(dev, head, regp, NV_CIO_CRE_RPC1_INDEX); + rd_cio_state(dev, head, regp, NV_CIO_CRE_LSR_INDEX); + rd_cio_state(dev, head, regp, NV_CIO_CRE_PIXEL_INDEX); + rd_cio_state(dev, head, regp, NV_CIO_CRE_HEB__INDEX); + rd_cio_state(dev, head, regp, NV_CIO_CRE_ENH_INDEX); + + rd_cio_state(dev, head, regp, NV_CIO_CRE_FF_INDEX); + rd_cio_state(dev, head, regp, NV_CIO_CRE_FFLWM__INDEX); + rd_cio_state(dev, head, regp, NV_CIO_CRE_21); + + if (drm->device.info.family >= NV_DEVICE_INFO_V0_KELVIN) + rd_cio_state(dev, head, regp, NV_CIO_CRE_47); + + if (drm->device.info.family >= NV_DEVICE_INFO_V0_RANKINE) + rd_cio_state(dev, head, regp, 0x9f); + + rd_cio_state(dev, head, regp, NV_CIO_CRE_49); + rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX); + rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR1_INDEX); + rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR2_INDEX); + rd_cio_state(dev, head, regp, NV_CIO_CRE_ILACE__INDEX); + + if (drm->device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) { + regp->crtc_830 = NVReadCRTC(dev, head, NV_PCRTC_830); + regp->crtc_834 = NVReadCRTC(dev, head, NV_PCRTC_834); + + if (drm->device.info.family >= NV_DEVICE_INFO_V0_RANKINE) + regp->gpio_ext = NVReadCRTC(dev, head, NV_PCRTC_GPIO_EXT); + + if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE) + regp->crtc_850 = NVReadCRTC(dev, head, NV_PCRTC_850); + + if (nv_two_heads(dev)) + regp->crtc_eng_ctrl = NVReadCRTC(dev, head, NV_PCRTC_ENGINE_CTRL); + regp->cursor_cfg = NVReadCRTC(dev, head, NV_PCRTC_CURSOR_CONFIG); + } + + regp->crtc_cfg = NVReadCRTC(dev, head, NV_PCRTC_CONFIG); + + rd_cio_state(dev, head, regp, NV_CIO_CRE_SCRATCH3__INDEX); + rd_cio_state(dev, head, regp, NV_CIO_CRE_SCRATCH4__INDEX); + if (drm->device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) { + rd_cio_state(dev, head, regp, NV_CIO_CRE_EBR_INDEX); + rd_cio_state(dev, head, regp, NV_CIO_CRE_CSB); + rd_cio_state(dev, head, regp, NV_CIO_CRE_4B); + rd_cio_state(dev, head, regp, NV_CIO_CRE_TVOUT_LATENCY); + } + /* NV11 and NV20 don't have this, they stop at 0x52. */ + if (nv_gf4_disp_arch(dev)) { + rd_cio_state(dev, head, regp, NV_CIO_CRE_42); + rd_cio_state(dev, head, regp, NV_CIO_CRE_53); + rd_cio_state(dev, head, regp, NV_CIO_CRE_54); + + for (i = 0; i < 0x10; i++) + regp->CR58[i] = NVReadVgaCrtc5758(dev, head, i); + rd_cio_state(dev, head, regp, NV_CIO_CRE_59); + rd_cio_state(dev, head, regp, NV_CIO_CRE_5B); + + rd_cio_state(dev, head, regp, NV_CIO_CRE_85); + rd_cio_state(dev, head, regp, NV_CIO_CRE_86); + } + + regp->fb_start = NVReadCRTC(dev, head, NV_PCRTC_START); +} + +static void +nv_load_state_ext(struct drm_device *dev, int head, + struct nv04_mode_state *state) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvif_device *device = &drm->device; + struct nvkm_timer *ptimer = nvxx_timer(device); + struct nv04_crtc_reg *regp = &state->crtc_reg[head]; + uint32_t reg900; + int i; + + if (drm->device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) { + if (nv_two_heads(dev)) + /* setting ENGINE_CTRL (EC) *must* come before + * CIO_CRE_LCD, as writing CRE_LCD sets bits 16 & 17 in + * EC that should not be overwritten by writing stale EC + */ + NVWriteCRTC(dev, head, NV_PCRTC_ENGINE_CTRL, regp->crtc_eng_ctrl); + + nvif_wr32(device, NV_PVIDEO_STOP, 1); + nvif_wr32(device, NV_PVIDEO_INTR_EN, 0); + nvif_wr32(device, NV_PVIDEO_OFFSET_BUFF(0), 0); + nvif_wr32(device, NV_PVIDEO_OFFSET_BUFF(1), 0); + nvif_wr32(device, NV_PVIDEO_LIMIT(0), device->info.ram_size - 1); + nvif_wr32(device, NV_PVIDEO_LIMIT(1), device->info.ram_size - 1); + nvif_wr32(device, NV_PVIDEO_UVPLANE_LIMIT(0), device->info.ram_size - 1); + nvif_wr32(device, NV_PVIDEO_UVPLANE_LIMIT(1), device->info.ram_size - 1); + nvif_wr32(device, NV_PBUS_POWERCTRL_2, 0); + + NVWriteCRTC(dev, head, NV_PCRTC_CURSOR_CONFIG, regp->cursor_cfg); + NVWriteCRTC(dev, head, NV_PCRTC_830, regp->crtc_830); + NVWriteCRTC(dev, head, NV_PCRTC_834, regp->crtc_834); + + if (drm->device.info.family >= NV_DEVICE_INFO_V0_RANKINE) + NVWriteCRTC(dev, head, NV_PCRTC_GPIO_EXT, regp->gpio_ext); + + if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE) { + NVWriteCRTC(dev, head, NV_PCRTC_850, regp->crtc_850); + + reg900 = NVReadRAMDAC(dev, head, NV_PRAMDAC_900); + if (regp->crtc_cfg == NV10_PCRTC_CONFIG_START_ADDRESS_HSYNC) + NVWriteRAMDAC(dev, head, NV_PRAMDAC_900, reg900 | 0x10000); + else + NVWriteRAMDAC(dev, head, NV_PRAMDAC_900, reg900 & ~0x10000); + } + } + + NVWriteCRTC(dev, head, NV_PCRTC_CONFIG, regp->crtc_cfg); + + wr_cio_state(dev, head, regp, NV_CIO_CRE_RPC0_INDEX); + wr_cio_state(dev, head, regp, NV_CIO_CRE_RPC1_INDEX); + wr_cio_state(dev, head, regp, NV_CIO_CRE_LSR_INDEX); + wr_cio_state(dev, head, regp, NV_CIO_CRE_PIXEL_INDEX); + wr_cio_state(dev, head, regp, NV_CIO_CRE_LCD__INDEX); + wr_cio_state(dev, head, regp, NV_CIO_CRE_HEB__INDEX); + wr_cio_state(dev, head, regp, NV_CIO_CRE_ENH_INDEX); + wr_cio_state(dev, head, regp, NV_CIO_CRE_FF_INDEX); + wr_cio_state(dev, head, regp, NV_CIO_CRE_FFLWM__INDEX); + + if (drm->device.info.family >= NV_DEVICE_INFO_V0_KELVIN) + wr_cio_state(dev, head, regp, NV_CIO_CRE_47); + + if (drm->device.info.family >= NV_DEVICE_INFO_V0_RANKINE) + wr_cio_state(dev, head, regp, 0x9f); + + wr_cio_state(dev, head, regp, NV_CIO_CRE_49); + wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX); + wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR1_INDEX); + wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR2_INDEX); + if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE) + nv_fix_nv40_hw_cursor(dev, head); + wr_cio_state(dev, head, regp, NV_CIO_CRE_ILACE__INDEX); + + wr_cio_state(dev, head, regp, NV_CIO_CRE_SCRATCH3__INDEX); + wr_cio_state(dev, head, regp, NV_CIO_CRE_SCRATCH4__INDEX); + if (drm->device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) { + wr_cio_state(dev, head, regp, NV_CIO_CRE_EBR_INDEX); + wr_cio_state(dev, head, regp, NV_CIO_CRE_CSB); + wr_cio_state(dev, head, regp, NV_CIO_CRE_4B); + wr_cio_state(dev, head, regp, NV_CIO_CRE_TVOUT_LATENCY); + } + /* NV11 and NV20 stop at 0x52. */ + if (nv_gf4_disp_arch(dev)) { + if (drm->device.info.family < NV_DEVICE_INFO_V0_KELVIN) { + /* Not waiting for vertical retrace before modifying + CRE_53/CRE_54 causes lockups. */ + nvkm_timer_wait_eq(ptimer, 650000000, NV_PRMCIO_INP0__COLOR, 0x8, 0x8); + nvkm_timer_wait_eq(ptimer, 650000000, NV_PRMCIO_INP0__COLOR, 0x8, 0x0); + } + + wr_cio_state(dev, head, regp, NV_CIO_CRE_42); + wr_cio_state(dev, head, regp, NV_CIO_CRE_53); + wr_cio_state(dev, head, regp, NV_CIO_CRE_54); + + for (i = 0; i < 0x10; i++) + NVWriteVgaCrtc5758(dev, head, i, regp->CR58[i]); + wr_cio_state(dev, head, regp, NV_CIO_CRE_59); + wr_cio_state(dev, head, regp, NV_CIO_CRE_5B); + + wr_cio_state(dev, head, regp, NV_CIO_CRE_85); + wr_cio_state(dev, head, regp, NV_CIO_CRE_86); + } + + NVWriteCRTC(dev, head, NV_PCRTC_START, regp->fb_start); +} + +static void +nv_save_state_palette(struct drm_device *dev, int head, + struct nv04_mode_state *state) +{ + struct nvif_device *device = &nouveau_drm(dev)->device; + int head_offset = head * NV_PRMDIO_SIZE, i; + + nvif_wr08(device, NV_PRMDIO_PIXEL_MASK + head_offset, + NV_PRMDIO_PIXEL_MASK_MASK); + nvif_wr08(device, NV_PRMDIO_READ_MODE_ADDRESS + head_offset, 0x0); + + for (i = 0; i < 768; i++) { + state->crtc_reg[head].DAC[i] = nvif_rd08(device, + NV_PRMDIO_PALETTE_DATA + head_offset); + } + + NVSetEnablePalette(dev, head, false); +} + +void +nouveau_hw_load_state_palette(struct drm_device *dev, int head, + struct nv04_mode_state *state) +{ + struct nvif_device *device = &nouveau_drm(dev)->device; + int head_offset = head * NV_PRMDIO_SIZE, i; + + nvif_wr08(device, NV_PRMDIO_PIXEL_MASK + head_offset, + NV_PRMDIO_PIXEL_MASK_MASK); + nvif_wr08(device, NV_PRMDIO_WRITE_MODE_ADDRESS + head_offset, 0x0); + + for (i = 0; i < 768; i++) { + nvif_wr08(device, NV_PRMDIO_PALETTE_DATA + head_offset, + state->crtc_reg[head].DAC[i]); + } + + NVSetEnablePalette(dev, head, false); +} + +void nouveau_hw_save_state(struct drm_device *dev, int head, + struct nv04_mode_state *state) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + + if (drm->device.info.chipset == 0x11) + /* NB: no attempt is made to restore the bad pll later on */ + nouveau_hw_fix_bad_vpll(dev, head); + nv_save_state_ramdac(dev, head, state); + nv_save_state_vga(dev, head, state); + nv_save_state_palette(dev, head, state); + nv_save_state_ext(dev, head, state); +} + +void nouveau_hw_load_state(struct drm_device *dev, int head, + struct nv04_mode_state *state) +{ + NVVgaProtect(dev, head, true); + nv_load_state_ramdac(dev, head, state); + nv_load_state_ext(dev, head, state); + nouveau_hw_load_state_palette(dev, head, state); + nv_load_state_vga(dev, head, state); + NVVgaProtect(dev, head, false); +} diff --git a/kernel/drivers/gpu/drm/nouveau/dispnv04/hw.h b/kernel/drivers/gpu/drm/nouveau/dispnv04/hw.h new file mode 100644 index 000000000..6c796178b --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/dispnv04/hw.h @@ -0,0 +1,409 @@ +/* + * Copyright 2008 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. + */ + +#ifndef __NOUVEAU_HW_H__ +#define __NOUVEAU_HW_H__ + +#include +#include "disp.h" +#include "nvreg.h" + +#include + +#define MASK(field) ( \ + (0xffffffff >> (31 - ((1 ? field) - (0 ? field)))) << (0 ? field)) + +#define XLATE(src, srclowbit, outfield) ( \ + (((src) >> (srclowbit)) << (0 ? outfield)) & MASK(outfield)) + +void NVWriteVgaSeq(struct drm_device *, int head, uint8_t index, uint8_t value); +uint8_t NVReadVgaSeq(struct drm_device *, int head, uint8_t index); +void NVWriteVgaGr(struct drm_device *, int head, uint8_t index, uint8_t value); +uint8_t NVReadVgaGr(struct drm_device *, int head, uint8_t index); +void NVSetOwner(struct drm_device *, int owner); +void NVBlankScreen(struct drm_device *, int head, bool blank); +int nouveau_hw_get_pllvals(struct drm_device *, enum nvbios_pll_type plltype, + struct nvkm_pll_vals *pllvals); +int nouveau_hw_pllvals_to_clk(struct nvkm_pll_vals *pllvals); +int nouveau_hw_get_clock(struct drm_device *, enum nvbios_pll_type plltype); +void nouveau_hw_save_vga_fonts(struct drm_device *, bool save); +void nouveau_hw_save_state(struct drm_device *, int head, + struct nv04_mode_state *state); +void nouveau_hw_load_state(struct drm_device *, int head, + struct nv04_mode_state *state); +void nouveau_hw_load_state_palette(struct drm_device *, int head, + struct nv04_mode_state *state); + +/* nouveau_calc.c */ +extern void nouveau_calc_arb(struct drm_device *, int vclk, int bpp, + int *burst, int *lwm); + +static inline uint32_t NVReadCRTC(struct drm_device *dev, + int head, uint32_t reg) +{ + struct nvif_device *device = &nouveau_drm(dev)->device; + uint32_t val; + if (head) + reg += NV_PCRTC0_SIZE; + val = nvif_rd32(device, reg); + return val; +} + +static inline void NVWriteCRTC(struct drm_device *dev, + int head, uint32_t reg, uint32_t val) +{ + struct nvif_device *device = &nouveau_drm(dev)->device; + if (head) + reg += NV_PCRTC0_SIZE; + nvif_wr32(device, reg, val); +} + +static inline uint32_t NVReadRAMDAC(struct drm_device *dev, + int head, uint32_t reg) +{ + struct nvif_device *device = &nouveau_drm(dev)->device; + uint32_t val; + if (head) + reg += NV_PRAMDAC0_SIZE; + val = nvif_rd32(device, reg); + return val; +} + +static inline void NVWriteRAMDAC(struct drm_device *dev, + int head, uint32_t reg, uint32_t val) +{ + struct nvif_device *device = &nouveau_drm(dev)->device; + if (head) + reg += NV_PRAMDAC0_SIZE; + nvif_wr32(device, reg, val); +} + +static inline uint8_t nv_read_tmds(struct drm_device *dev, + int or, int dl, uint8_t address) +{ + int ramdac = (or & DCB_OUTPUT_C) >> 2; + + NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_CONTROL + dl * 8, + NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE | address); + return NVReadRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_DATA + dl * 8); +} + +static inline void nv_write_tmds(struct drm_device *dev, + int or, int dl, uint8_t address, + uint8_t data) +{ + int ramdac = (or & DCB_OUTPUT_C) >> 2; + + NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_DATA + dl * 8, data); + NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_CONTROL + dl * 8, address); +} + +static inline void NVWriteVgaCrtc(struct drm_device *dev, + int head, uint8_t index, uint8_t value) +{ + struct nvif_device *device = &nouveau_drm(dev)->device; + nvif_wr08(device, NV_PRMCIO_CRX__COLOR + head * NV_PRMCIO_SIZE, index); + nvif_wr08(device, NV_PRMCIO_CR__COLOR + head * NV_PRMCIO_SIZE, value); +} + +static inline uint8_t NVReadVgaCrtc(struct drm_device *dev, + int head, uint8_t index) +{ + struct nvif_device *device = &nouveau_drm(dev)->device; + uint8_t val; + nvif_wr08(device, NV_PRMCIO_CRX__COLOR + head * NV_PRMCIO_SIZE, index); + val = nvif_rd08(device, NV_PRMCIO_CR__COLOR + head * NV_PRMCIO_SIZE); + return val; +} + +/* CR57 and CR58 are a fun pair of regs. CR57 provides an index (0-0xf) for CR58 + * I suspect they in fact do nothing, but are merely a way to carry useful + * per-head variables around + * + * Known uses: + * CR57 CR58 + * 0x00 index to the appropriate dcb entry (or 7f for inactive) + * 0x02 dcb entry's "or" value (or 00 for inactive) + * 0x03 bit0 set for dual link (LVDS, possibly elsewhere too) + * 0x08 or 0x09 pxclk in MHz + * 0x0f laptop panel info - low nibble for PEXTDEV_BOOT_0 strap + * high nibble for xlat strap value + */ + +static inline void +NVWriteVgaCrtc5758(struct drm_device *dev, int head, uint8_t index, uint8_t value) +{ + NVWriteVgaCrtc(dev, head, NV_CIO_CRE_57, index); + NVWriteVgaCrtc(dev, head, NV_CIO_CRE_58, value); +} + +static inline uint8_t NVReadVgaCrtc5758(struct drm_device *dev, int head, uint8_t index) +{ + NVWriteVgaCrtc(dev, head, NV_CIO_CRE_57, index); + return NVReadVgaCrtc(dev, head, NV_CIO_CRE_58); +} + +static inline uint8_t NVReadPRMVIO(struct drm_device *dev, + int head, uint32_t reg) +{ + struct nvif_device *device = &nouveau_drm(dev)->device; + struct nouveau_drm *drm = nouveau_drm(dev); + uint8_t val; + + /* Only NV4x have two pvio ranges; other twoHeads cards MUST call + * NVSetOwner for the relevant head to be programmed */ + if (head && drm->device.info.family == NV_DEVICE_INFO_V0_CURIE) + reg += NV_PRMVIO_SIZE; + + val = nvif_rd08(device, reg); + return val; +} + +static inline void NVWritePRMVIO(struct drm_device *dev, + int head, uint32_t reg, uint8_t value) +{ + struct nvif_device *device = &nouveau_drm(dev)->device; + struct nouveau_drm *drm = nouveau_drm(dev); + + /* Only NV4x have two pvio ranges; other twoHeads cards MUST call + * NVSetOwner for the relevant head to be programmed */ + if (head && drm->device.info.family == NV_DEVICE_INFO_V0_CURIE) + reg += NV_PRMVIO_SIZE; + + nvif_wr08(device, reg, value); +} + +static inline void NVSetEnablePalette(struct drm_device *dev, int head, bool enable) +{ + struct nvif_device *device = &nouveau_drm(dev)->device; + nvif_rd08(device, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE); + nvif_wr08(device, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE, enable ? 0 : 0x20); +} + +static inline bool NVGetEnablePalette(struct drm_device *dev, int head) +{ + struct nvif_device *device = &nouveau_drm(dev)->device; + nvif_rd08(device, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE); + return !(nvif_rd08(device, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE) & 0x20); +} + +static inline void NVWriteVgaAttr(struct drm_device *dev, + int head, uint8_t index, uint8_t value) +{ + struct nvif_device *device = &nouveau_drm(dev)->device; + if (NVGetEnablePalette(dev, head)) + index &= ~0x20; + else + index |= 0x20; + + nvif_rd08(device, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE); + nvif_wr08(device, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE, index); + nvif_wr08(device, NV_PRMCIO_AR__WRITE + head * NV_PRMCIO_SIZE, value); +} + +static inline uint8_t NVReadVgaAttr(struct drm_device *dev, + int head, uint8_t index) +{ + struct nvif_device *device = &nouveau_drm(dev)->device; + uint8_t val; + if (NVGetEnablePalette(dev, head)) + index &= ~0x20; + else + index |= 0x20; + + nvif_rd08(device, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE); + nvif_wr08(device, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE, index); + val = nvif_rd08(device, NV_PRMCIO_AR__READ + head * NV_PRMCIO_SIZE); + return val; +} + +static inline void NVVgaSeqReset(struct drm_device *dev, int head, bool start) +{ + NVWriteVgaSeq(dev, head, NV_VIO_SR_RESET_INDEX, start ? 0x1 : 0x3); +} + +static inline void NVVgaProtect(struct drm_device *dev, int head, bool protect) +{ + uint8_t seq1 = NVReadVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX); + + if (protect) { + NVVgaSeqReset(dev, head, true); + NVWriteVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX, seq1 | 0x20); + } else { + /* Reenable sequencer, then turn on screen */ + NVWriteVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX, seq1 & ~0x20); /* reenable display */ + NVVgaSeqReset(dev, head, false); + } + NVSetEnablePalette(dev, head, protect); +} + +static inline bool +nv_heads_tied(struct drm_device *dev) +{ + struct nvif_device *device = &nouveau_drm(dev)->device; + struct nouveau_drm *drm = nouveau_drm(dev); + + if (drm->device.info.chipset == 0x11) + return !!(nvif_rd32(device, NV_PBUS_DEBUG_1) & (1 << 28)); + + return NVReadVgaCrtc(dev, 0, NV_CIO_CRE_44) & 0x4; +} + +/* makes cr0-7 on the specified head read-only */ +static inline bool +nv_lock_vga_crtc_base(struct drm_device *dev, int head, bool lock) +{ + uint8_t cr11 = NVReadVgaCrtc(dev, head, NV_CIO_CR_VRE_INDEX); + bool waslocked = cr11 & 0x80; + + if (lock) + cr11 |= 0x80; + else + cr11 &= ~0x80; + NVWriteVgaCrtc(dev, head, NV_CIO_CR_VRE_INDEX, cr11); + + return waslocked; +} + +static inline void +nv_lock_vga_crtc_shadow(struct drm_device *dev, int head, int lock) +{ + /* shadow lock: connects 0x60?3d? regs to "real" 0x3d? regs + * bit7: unlocks HDT, HBS, HBE, HRS, HRE, HEB + * bit6: seems to have some effect on CR09 (double scan, VBS_9) + * bit5: unlocks HDE + * bit4: unlocks VDE + * bit3: unlocks VDT, OVL, VRS, ?VRE?, VBS, VBE, LSR, EBR + * bit2: same as bit 1 of 0x60?804 + * bit0: same as bit 0 of 0x60?804 + */ + + uint8_t cr21 = lock; + + if (lock < 0) + /* 0xfa is generic "unlock all" mask */ + cr21 = NVReadVgaCrtc(dev, head, NV_CIO_CRE_21) | 0xfa; + + NVWriteVgaCrtc(dev, head, NV_CIO_CRE_21, cr21); +} + +/* renders the extended crtc regs (cr19+) on all crtcs impervious: + * immutable and unreadable + */ +static inline bool +NVLockVgaCrtcs(struct drm_device *dev, bool lock) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + bool waslocked = !NVReadVgaCrtc(dev, 0, NV_CIO_SR_LOCK_INDEX); + + NVWriteVgaCrtc(dev, 0, NV_CIO_SR_LOCK_INDEX, + lock ? NV_CIO_SR_LOCK_VALUE : NV_CIO_SR_UNLOCK_RW_VALUE); + /* NV11 has independently lockable extended crtcs, except when tied */ + if (drm->device.info.chipset == 0x11 && !nv_heads_tied(dev)) + NVWriteVgaCrtc(dev, 1, NV_CIO_SR_LOCK_INDEX, + lock ? NV_CIO_SR_LOCK_VALUE : + NV_CIO_SR_UNLOCK_RW_VALUE); + + return waslocked; +} + +/* nv04 cursor max dimensions of 32x32 (A1R5G5B5) */ +#define NV04_CURSOR_SIZE 32 +/* limit nv10 cursors to 64x64 (ARGB8) (we could go to 64x255) */ +#define NV10_CURSOR_SIZE 64 + +static inline int nv_cursor_width(struct drm_device *dev) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + + return drm->device.info.family >= NV_DEVICE_INFO_V0_CELSIUS ? NV10_CURSOR_SIZE : NV04_CURSOR_SIZE; +} + +static inline void +nv_fix_nv40_hw_cursor(struct drm_device *dev, int head) +{ + /* on some nv40 (such as the "true" (in the NV_PFB_BOOT_0 sense) nv40, + * the gf6800gt) a hardware bug requires a write to PRAMDAC_CURSOR_POS + * for changes to the CRTC CURCTL regs to take effect, whether changing + * the pixmap location, or just showing/hiding the cursor + */ + uint32_t curpos = NVReadRAMDAC(dev, head, NV_PRAMDAC_CU_START_POS); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_CU_START_POS, curpos); +} + +static inline void +nv_set_crtc_base(struct drm_device *dev, int head, uint32_t offset) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + + NVWriteCRTC(dev, head, NV_PCRTC_START, offset); + + if (drm->device.info.family == NV_DEVICE_INFO_V0_TNT) { + /* + * Hilarious, the 24th bit doesn't want to stick to + * PCRTC_START... + */ + int cre_heb = NVReadVgaCrtc(dev, head, NV_CIO_CRE_HEB__INDEX); + + NVWriteVgaCrtc(dev, head, NV_CIO_CRE_HEB__INDEX, + (cre_heb & ~0x40) | ((offset >> 18) & 0x40)); + } +} + +static inline void +nv_show_cursor(struct drm_device *dev, int head, bool show) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + uint8_t *curctl1 = + &nv04_display(dev)->mode_reg.crtc_reg[head].CRTC[NV_CIO_CRE_HCUR_ADDR1_INDEX]; + + if (show) + *curctl1 |= MASK(NV_CIO_CRE_HCUR_ADDR1_ENABLE); + else + *curctl1 &= ~MASK(NV_CIO_CRE_HCUR_ADDR1_ENABLE); + NVWriteVgaCrtc(dev, head, NV_CIO_CRE_HCUR_ADDR1_INDEX, *curctl1); + + if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE) + nv_fix_nv40_hw_cursor(dev, head); +} + +static inline uint32_t +nv_pitch_align(struct drm_device *dev, uint32_t width, int bpp) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + int mask; + + if (bpp == 15) + bpp = 16; + if (bpp == 24) + bpp = 8; + + /* Alignment requirements taken from the Haiku driver */ + if (drm->device.info.family == NV_DEVICE_INFO_V0_TNT) + mask = 128 / bpp - 1; + else + mask = 512 / bpp - 1; + + return (width + mask) & ~mask; +} + +#endif /* __NOUVEAU_HW_H__ */ diff --git a/kernel/drivers/gpu/drm/nouveau/dispnv04/nvreg.h b/kernel/drivers/gpu/drm/nouveau/dispnv04/nvreg.h new file mode 100644 index 000000000..bbfb1a68f --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/dispnv04/nvreg.h @@ -0,0 +1,517 @@ +/* $XConsortium: nvreg.h /main/2 1996/10/28 05:13:41 kaleb $ */ +/* + * Copyright 1996-1997 David J. McKay + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * DAVID J. MCKAY BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/nv/nvreg.h,v 1.6 2002/01/25 21:56:06 tsi Exp $ */ + +#ifndef __NVREG_H_ +#define __NVREG_H_ + +#define NV_PMC_OFFSET 0x00000000 +#define NV_PMC_SIZE 0x00001000 + +#define NV_PBUS_OFFSET 0x00001000 +#define NV_PBUS_SIZE 0x00001000 + +#define NV_PFIFO_OFFSET 0x00002000 +#define NV_PFIFO_SIZE 0x00002000 + +#define NV_HDIAG_OFFSET 0x00005000 +#define NV_HDIAG_SIZE 0x00001000 + +#define NV_PRAM_OFFSET 0x00006000 +#define NV_PRAM_SIZE 0x00001000 + +#define NV_PVIDEO_OFFSET 0x00008000 +#define NV_PVIDEO_SIZE 0x00001000 + +#define NV_PTIMER_OFFSET 0x00009000 +#define NV_PTIMER_SIZE 0x00001000 + +#define NV_PPM_OFFSET 0x0000A000 +#define NV_PPM_SIZE 0x00001000 + +#define NV_PTV_OFFSET 0x0000D000 +#define NV_PTV_SIZE 0x00001000 + +#define NV_PRMVGA_OFFSET 0x000A0000 +#define NV_PRMVGA_SIZE 0x00020000 + +#define NV_PRMVIO0_OFFSET 0x000C0000 +#define NV_PRMVIO_SIZE 0x00002000 +#define NV_PRMVIO1_OFFSET 0x000C2000 + +#define NV_PFB_OFFSET 0x00100000 +#define NV_PFB_SIZE 0x00001000 + +#define NV_PEXTDEV_OFFSET 0x00101000 +#define NV_PEXTDEV_SIZE 0x00001000 + +#define NV_PME_OFFSET 0x00200000 +#define NV_PME_SIZE 0x00001000 + +#define NV_PROM_OFFSET 0x00300000 +#define NV_PROM_SIZE 0x00010000 + +#define NV_PGRAPH_OFFSET 0x00400000 +#define NV_PGRAPH_SIZE 0x00010000 + +#define NV_PCRTC0_OFFSET 0x00600000 +#define NV_PCRTC0_SIZE 0x00002000 /* empirical */ + +#define NV_PRMCIO0_OFFSET 0x00601000 +#define NV_PRMCIO_SIZE 0x00002000 +#define NV_PRMCIO1_OFFSET 0x00603000 + +#define NV50_DISPLAY_OFFSET 0x00610000 +#define NV50_DISPLAY_SIZE 0x0000FFFF + +#define NV_PRAMDAC0_OFFSET 0x00680000 +#define NV_PRAMDAC0_SIZE 0x00002000 + +#define NV_PRMDIO0_OFFSET 0x00681000 +#define NV_PRMDIO_SIZE 0x00002000 +#define NV_PRMDIO1_OFFSET 0x00683000 + +#define NV_PRAMIN_OFFSET 0x00700000 +#define NV_PRAMIN_SIZE 0x00100000 + +#define NV_FIFO_OFFSET 0x00800000 +#define NV_FIFO_SIZE 0x00800000 + +#define NV_PMC_BOOT_0 0x00000000 +#define NV_PMC_ENABLE 0x00000200 + +#define NV_VIO_VSE2 0x000003c3 +#define NV_VIO_SRX 0x000003c4 + +#define NV_CIO_CRX__COLOR 0x000003d4 +#define NV_CIO_CR__COLOR 0x000003d5 + +#define NV_PBUS_DEBUG_1 0x00001084 +#define NV_PBUS_DEBUG_4 0x00001098 +#define NV_PBUS_DEBUG_DUALHEAD_CTL 0x000010f0 +#define NV_PBUS_POWERCTRL_1 0x00001584 +#define NV_PBUS_POWERCTRL_2 0x00001588 +#define NV_PBUS_POWERCTRL_4 0x00001590 +#define NV_PBUS_PCI_NV_19 0x0000184C +#define NV_PBUS_PCI_NV_20 0x00001850 +# define NV_PBUS_PCI_NV_20_ROM_SHADOW_DISABLED (0 << 0) +# define NV_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED (1 << 0) + +#define NV_PFIFO_RAMHT 0x00002210 + +#define NV_PTV_TV_INDEX 0x0000d220 +#define NV_PTV_TV_DATA 0x0000d224 +#define NV_PTV_HFILTER 0x0000d310 +#define NV_PTV_HFILTER2 0x0000d390 +#define NV_PTV_VFILTER 0x0000d510 + +#define NV_PRMVIO_MISC__WRITE 0x000c03c2 +#define NV_PRMVIO_SRX 0x000c03c4 +#define NV_PRMVIO_SR 0x000c03c5 +# define NV_VIO_SR_RESET_INDEX 0x00 +# define NV_VIO_SR_CLOCK_INDEX 0x01 +# define NV_VIO_SR_PLANE_MASK_INDEX 0x02 +# define NV_VIO_SR_CHAR_MAP_INDEX 0x03 +# define NV_VIO_SR_MEM_MODE_INDEX 0x04 +#define NV_PRMVIO_MISC__READ 0x000c03cc +#define NV_PRMVIO_GRX 0x000c03ce +#define NV_PRMVIO_GX 0x000c03cf +# define NV_VIO_GX_SR_INDEX 0x00 +# define NV_VIO_GX_SREN_INDEX 0x01 +# define NV_VIO_GX_CCOMP_INDEX 0x02 +# define NV_VIO_GX_ROP_INDEX 0x03 +# define NV_VIO_GX_READ_MAP_INDEX 0x04 +# define NV_VIO_GX_MODE_INDEX 0x05 +# define NV_VIO_GX_MISC_INDEX 0x06 +# define NV_VIO_GX_DONT_CARE_INDEX 0x07 +# define NV_VIO_GX_BIT_MASK_INDEX 0x08 + +#define NV_PCRTC_INTR_0 0x00600100 +# define NV_PCRTC_INTR_0_VBLANK (1 << 0) +#define NV_PCRTC_INTR_EN_0 0x00600140 +#define NV_PCRTC_START 0x00600800 +#define NV_PCRTC_CONFIG 0x00600804 +# define NV_PCRTC_CONFIG_START_ADDRESS_NON_VGA (1 << 0) +# define NV04_PCRTC_CONFIG_START_ADDRESS_HSYNC (4 << 0) +# define NV10_PCRTC_CONFIG_START_ADDRESS_HSYNC (2 << 0) +#define NV_PCRTC_CURSOR_CONFIG 0x00600810 +# define NV_PCRTC_CURSOR_CONFIG_ENABLE_ENABLE (1 << 0) +# define NV_PCRTC_CURSOR_CONFIG_DOUBLE_SCAN_ENABLE (1 << 4) +# define NV_PCRTC_CURSOR_CONFIG_ADDRESS_SPACE_PNVM (1 << 8) +# define NV_PCRTC_CURSOR_CONFIG_CUR_BPP_32 (1 << 12) +# define NV_PCRTC_CURSOR_CONFIG_CUR_PIXELS_64 (1 << 16) +# define NV_PCRTC_CURSOR_CONFIG_CUR_LINES_32 (2 << 24) +# define NV_PCRTC_CURSOR_CONFIG_CUR_LINES_64 (4 << 24) +# define NV_PCRTC_CURSOR_CONFIG_CUR_BLEND_ALPHA (1 << 28) + +/* note: PCRTC_GPIO is not available on nv10, and in fact aliases 0x600810 */ +#define NV_PCRTC_GPIO 0x00600818 +#define NV_PCRTC_GPIO_EXT 0x0060081c +#define NV_PCRTC_830 0x00600830 +#define NV_PCRTC_834 0x00600834 +#define NV_PCRTC_850 0x00600850 +#define NV_PCRTC_ENGINE_CTRL 0x00600860 +# define NV_CRTC_FSEL_I2C (1 << 4) +# define NV_CRTC_FSEL_OVERLAY (1 << 12) + +#define NV_PRMCIO_ARX 0x006013c0 +#define NV_PRMCIO_AR__WRITE 0x006013c0 +#define NV_PRMCIO_AR__READ 0x006013c1 +# define NV_CIO_AR_MODE_INDEX 0x10 +# define NV_CIO_AR_OSCAN_INDEX 0x11 +# define NV_CIO_AR_PLANE_INDEX 0x12 +# define NV_CIO_AR_HPP_INDEX 0x13 +# define NV_CIO_AR_CSEL_INDEX 0x14 +#define NV_PRMCIO_INP0 0x006013c2 +#define NV_PRMCIO_CRX__COLOR 0x006013d4 +#define NV_PRMCIO_CR__COLOR 0x006013d5 + /* Standard VGA CRTC registers */ +# define NV_CIO_CR_HDT_INDEX 0x00 /* horizontal display total */ +# define NV_CIO_CR_HDE_INDEX 0x01 /* horizontal display end */ +# define NV_CIO_CR_HBS_INDEX 0x02 /* horizontal blanking start */ +# define NV_CIO_CR_HBE_INDEX 0x03 /* horizontal blanking end */ +# define NV_CIO_CR_HBE_4_0 4:0 +# define NV_CIO_CR_HRS_INDEX 0x04 /* horizontal retrace start */ +# define NV_CIO_CR_HRE_INDEX 0x05 /* horizontal retrace end */ +# define NV_CIO_CR_HRE_4_0 4:0 +# define NV_CIO_CR_HRE_HBE_5 7:7 +# define NV_CIO_CR_VDT_INDEX 0x06 /* vertical display total */ +# define NV_CIO_CR_OVL_INDEX 0x07 /* overflow bits */ +# define NV_CIO_CR_OVL_VDT_8 0:0 +# define NV_CIO_CR_OVL_VDE_8 1:1 +# define NV_CIO_CR_OVL_VRS_8 2:2 +# define NV_CIO_CR_OVL_VBS_8 3:3 +# define NV_CIO_CR_OVL_VDT_9 5:5 +# define NV_CIO_CR_OVL_VDE_9 6:6 +# define NV_CIO_CR_OVL_VRS_9 7:7 +# define NV_CIO_CR_RSAL_INDEX 0x08 /* normally "preset row scan" */ +# define NV_CIO_CR_CELL_HT_INDEX 0x09 /* cell height?! normally "max scan line" */ +# define NV_CIO_CR_CELL_HT_VBS_9 5:5 +# define NV_CIO_CR_CELL_HT_SCANDBL 7:7 +# define NV_CIO_CR_CURS_ST_INDEX 0x0a /* cursor start */ +# define NV_CIO_CR_CURS_END_INDEX 0x0b /* cursor end */ +# define NV_CIO_CR_SA_HI_INDEX 0x0c /* screen start address high */ +# define NV_CIO_CR_SA_LO_INDEX 0x0d /* screen start address low */ +# define NV_CIO_CR_TCOFF_HI_INDEX 0x0e /* cursor offset high */ +# define NV_CIO_CR_TCOFF_LO_INDEX 0x0f /* cursor offset low */ +# define NV_CIO_CR_VRS_INDEX 0x10 /* vertical retrace start */ +# define NV_CIO_CR_VRE_INDEX 0x11 /* vertical retrace end */ +# define NV_CIO_CR_VRE_3_0 3:0 +# define NV_CIO_CR_VDE_INDEX 0x12 /* vertical display end */ +# define NV_CIO_CR_OFFSET_INDEX 0x13 /* sets screen pitch */ +# define NV_CIO_CR_ULINE_INDEX 0x14 /* underline location */ +# define NV_CIO_CR_VBS_INDEX 0x15 /* vertical blank start */ +# define NV_CIO_CR_VBE_INDEX 0x16 /* vertical blank end */ +# define NV_CIO_CR_MODE_INDEX 0x17 /* crtc mode control */ +# define NV_CIO_CR_LCOMP_INDEX 0x18 /* line compare */ + /* Extended VGA CRTC registers */ +# define NV_CIO_CRE_RPC0_INDEX 0x19 /* repaint control 0 */ +# define NV_CIO_CRE_RPC0_OFFSET_10_8 7:5 +# define NV_CIO_CRE_RPC1_INDEX 0x1a /* repaint control 1 */ +# define NV_CIO_CRE_RPC1_LARGE 2:2 +# define NV_CIO_CRE_FF_INDEX 0x1b /* fifo control */ +# define NV_CIO_CRE_ENH_INDEX 0x1c /* enhanced? */ +# define NV_CIO_SR_LOCK_INDEX 0x1f /* crtc lock */ +# define NV_CIO_SR_UNLOCK_RW_VALUE 0x57 +# define NV_CIO_SR_LOCK_VALUE 0x99 +# define NV_CIO_CRE_FFLWM__INDEX 0x20 /* fifo low water mark */ +# define NV_CIO_CRE_21 0x21 /* vga shadow crtc lock */ +# define NV_CIO_CRE_LSR_INDEX 0x25 /* ? */ +# define NV_CIO_CRE_LSR_VDT_10 0:0 +# define NV_CIO_CRE_LSR_VDE_10 1:1 +# define NV_CIO_CRE_LSR_VRS_10 2:2 +# define NV_CIO_CRE_LSR_VBS_10 3:3 +# define NV_CIO_CRE_LSR_HBE_6 4:4 +# define NV_CIO_CR_ARX_INDEX 0x26 /* attribute index -- ro copy of 0x60.3c0 */ +# define NV_CIO_CRE_CHIP_ID_INDEX 0x27 /* chip revision */ +# define NV_CIO_CRE_PIXEL_INDEX 0x28 +# define NV_CIO_CRE_PIXEL_FORMAT 1:0 +# define NV_CIO_CRE_HEB__INDEX 0x2d /* horizontal extra bits? */ +# define NV_CIO_CRE_HEB_HDT_8 0:0 +# define NV_CIO_CRE_HEB_HDE_8 1:1 +# define NV_CIO_CRE_HEB_HBS_8 2:2 +# define NV_CIO_CRE_HEB_HRS_8 3:3 +# define NV_CIO_CRE_HEB_ILC_8 4:4 +# define NV_CIO_CRE_2E 0x2e /* some scratch or dummy reg to force writes to sink in */ +# define NV_CIO_CRE_HCUR_ADDR2_INDEX 0x2f /* cursor */ +# define NV_CIO_CRE_HCUR_ADDR0_INDEX 0x30 /* pixmap */ +# define NV_CIO_CRE_HCUR_ADDR0_ADR 6:0 +# define NV_CIO_CRE_HCUR_ASI 7:7 +# define NV_CIO_CRE_HCUR_ADDR1_INDEX 0x31 /* address */ +# define NV_CIO_CRE_HCUR_ADDR1_ENABLE 0:0 +# define NV_CIO_CRE_HCUR_ADDR1_CUR_DBL 1:1 +# define NV_CIO_CRE_HCUR_ADDR1_ADR 7:2 +# define NV_CIO_CRE_LCD__INDEX 0x33 +# define NV_CIO_CRE_LCD_LCD_SELECT 0:0 +# define NV_CIO_CRE_LCD_ROUTE_MASK 0x3b +# define NV_CIO_CRE_DDC0_STATUS__INDEX 0x36 +# define NV_CIO_CRE_DDC0_WR__INDEX 0x37 +# define NV_CIO_CRE_ILACE__INDEX 0x39 /* interlace */ +# define NV_CIO_CRE_SCRATCH3__INDEX 0x3b +# define NV_CIO_CRE_SCRATCH4__INDEX 0x3c +# define NV_CIO_CRE_DDC_STATUS__INDEX 0x3e +# define NV_CIO_CRE_DDC_WR__INDEX 0x3f +# define NV_CIO_CRE_EBR_INDEX 0x41 /* extra bits ? (vertical) */ +# define NV_CIO_CRE_EBR_VDT_11 0:0 +# define NV_CIO_CRE_EBR_VDE_11 2:2 +# define NV_CIO_CRE_EBR_VRS_11 4:4 +# define NV_CIO_CRE_EBR_VBS_11 6:6 +# define NV_CIO_CRE_42 0x42 +# define NV_CIO_CRE_42_OFFSET_11 6:6 +# define NV_CIO_CRE_43 0x43 +# define NV_CIO_CRE_44 0x44 /* head control */ +# define NV_CIO_CRE_CSB 0x45 /* colour saturation boost */ +# define NV_CIO_CRE_RCR 0x46 +# define NV_CIO_CRE_RCR_ENDIAN_BIG 7:7 +# define NV_CIO_CRE_47 0x47 /* extended fifo lwm, used on nv30+ */ +# define NV_CIO_CRE_49 0x49 +# define NV_CIO_CRE_4B 0x4b /* given patterns in 0x[2-3][a-c] regs, probably scratch 6 */ +# define NV_CIO_CRE_TVOUT_LATENCY 0x52 +# define NV_CIO_CRE_53 0x53 /* `fp_htiming' according to Haiku */ +# define NV_CIO_CRE_54 0x54 /* `fp_vtiming' according to Haiku */ +# define NV_CIO_CRE_57 0x57 /* index reg for cr58 */ +# define NV_CIO_CRE_58 0x58 /* data reg for cr57 */ +# define NV_CIO_CRE_59 0x59 /* related to on/off-chip-ness of digital outputs */ +# define NV_CIO_CRE_5B 0x5B /* newer colour saturation reg */ +# define NV_CIO_CRE_85 0x85 +# define NV_CIO_CRE_86 0x86 +#define NV_PRMCIO_INP0__COLOR 0x006013da + +#define NV_PRAMDAC_CU_START_POS 0x00680300 +# define NV_PRAMDAC_CU_START_POS_X 15:0 +# define NV_PRAMDAC_CU_START_POS_Y 31:16 +#define NV_RAMDAC_NV10_CURSYNC 0x00680404 + +#define NV_PRAMDAC_NVPLL_COEFF 0x00680500 +#define NV_PRAMDAC_MPLL_COEFF 0x00680504 +#define NV_PRAMDAC_VPLL_COEFF 0x00680508 +# define NV30_RAMDAC_ENABLE_VCO2 (8 << 4) + +#define NV_PRAMDAC_PLL_COEFF_SELECT 0x0068050c +# define NV_PRAMDAC_PLL_COEFF_SELECT_USE_VPLL2_TRUE (4 << 0) +# define NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_MPLL (1 << 8) +# define NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_VPLL (2 << 8) +# define NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_NVPLL (4 << 8) +# define NV_PRAMDAC_PLL_COEFF_SELECT_PLL_SOURCE_VPLL2 (8 << 8) +# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1 (1 << 16) +# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1 (2 << 16) +# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK2 (4 << 16) +# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK2 (8 << 16) +# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_CLK_SOURCE_VIP (1 << 20) +# define NV_PRAMDAC_PLL_COEFF_SELECT_VCLK_RATIO_DB2 (1 << 28) +# define NV_PRAMDAC_PLL_COEFF_SELECT_VCLK2_RATIO_DB2 (2 << 28) + +#define NV_PRAMDAC_PLL_SETUP_CONTROL 0x00680510 +#define NV_RAMDAC_VPLL2 0x00680520 +#define NV_PRAMDAC_SEL_CLK 0x00680524 +#define NV_RAMDAC_DITHER_NV11 0x00680528 +#define NV_PRAMDAC_DACCLK 0x0068052c +# define NV_PRAMDAC_DACCLK_SEL_DACCLK (1 << 0) + +#define NV_RAMDAC_NVPLL_B 0x00680570 +#define NV_RAMDAC_MPLL_B 0x00680574 +#define NV_RAMDAC_VPLL_B 0x00680578 +#define NV_RAMDAC_VPLL2_B 0x0068057c +# define NV31_RAMDAC_ENABLE_VCO2 (8 << 28) +#define NV_PRAMDAC_580 0x00680580 +# define NV_RAMDAC_580_VPLL1_ACTIVE (1 << 8) +# define NV_RAMDAC_580_VPLL2_ACTIVE (1 << 28) + +#define NV_PRAMDAC_GENERAL_CONTROL 0x00680600 +# define NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON (3 << 4) +# define NV_PRAMDAC_GENERAL_CONTROL_VGA_STATE_SEL (1 << 8) +# define NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL (1 << 12) +# define NV_PRAMDAC_GENERAL_CONTROL_TERMINATION_75OHM (2 << 16) +# define NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS (1 << 20) +# define NV_PRAMDAC_GENERAL_CONTROL_PIPE_LONG (2 << 28) +#define NV_PRAMDAC_TEST_CONTROL 0x00680608 +# define NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED (1 << 12) +# define NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF (1 << 16) +# define NV_PRAMDAC_TEST_CONTROL_SENSEB_ALLHI (1 << 28) +#define NV_PRAMDAC_TESTPOINT_DATA 0x00680610 +# define NV_PRAMDAC_TESTPOINT_DATA_NOTBLANK (8 << 28) +#define NV_PRAMDAC_630 0x00680630 +#define NV_PRAMDAC_634 0x00680634 + +#define NV_PRAMDAC_TV_SETUP 0x00680700 +#define NV_PRAMDAC_TV_VTOTAL 0x00680720 +#define NV_PRAMDAC_TV_VSKEW 0x00680724 +#define NV_PRAMDAC_TV_VSYNC_DELAY 0x00680728 +#define NV_PRAMDAC_TV_HTOTAL 0x0068072c +#define NV_PRAMDAC_TV_HSKEW 0x00680730 +#define NV_PRAMDAC_TV_HSYNC_DELAY 0x00680734 +#define NV_PRAMDAC_TV_HSYNC_DELAY2 0x00680738 + +#define NV_PRAMDAC_TV_SETUP 0x00680700 + +#define NV_PRAMDAC_FP_VDISPLAY_END 0x00680800 +#define NV_PRAMDAC_FP_VTOTAL 0x00680804 +#define NV_PRAMDAC_FP_VCRTC 0x00680808 +#define NV_PRAMDAC_FP_VSYNC_START 0x0068080c +#define NV_PRAMDAC_FP_VSYNC_END 0x00680810 +#define NV_PRAMDAC_FP_VVALID_START 0x00680814 +#define NV_PRAMDAC_FP_VVALID_END 0x00680818 +#define NV_PRAMDAC_FP_HDISPLAY_END 0x00680820 +#define NV_PRAMDAC_FP_HTOTAL 0x00680824 +#define NV_PRAMDAC_FP_HCRTC 0x00680828 +#define NV_PRAMDAC_FP_HSYNC_START 0x0068082c +#define NV_PRAMDAC_FP_HSYNC_END 0x00680830 +#define NV_PRAMDAC_FP_HVALID_START 0x00680834 +#define NV_PRAMDAC_FP_HVALID_END 0x00680838 + +#define NV_RAMDAC_FP_DITHER 0x0068083c +#define NV_PRAMDAC_FP_TG_CONTROL 0x00680848 +# define NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS (1 << 0) +# define NV_PRAMDAC_FP_TG_CONTROL_VSYNC_DISABLE (2 << 0) +# define NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS (1 << 4) +# define NV_PRAMDAC_FP_TG_CONTROL_HSYNC_DISABLE (2 << 4) +# define NV_PRAMDAC_FP_TG_CONTROL_MODE_SCALE (0 << 8) +# define NV_PRAMDAC_FP_TG_CONTROL_MODE_CENTER (1 << 8) +# define NV_PRAMDAC_FP_TG_CONTROL_MODE_NATIVE (2 << 8) +# define NV_PRAMDAC_FP_TG_CONTROL_READ_PROG (1 << 20) +# define NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12 (1 << 24) +# define NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS (1 << 28) +# define NV_PRAMDAC_FP_TG_CONTROL_DISPEN_DISABLE (2 << 28) +#define NV_PRAMDAC_FP_MARGIN_COLOR 0x0068084c +#define NV_PRAMDAC_850 0x00680850 +#define NV_PRAMDAC_85C 0x0068085c +#define NV_PRAMDAC_FP_DEBUG_0 0x00680880 +# define NV_PRAMDAC_FP_DEBUG_0_XSCALE_ENABLE (1 << 0) +# define NV_PRAMDAC_FP_DEBUG_0_YSCALE_ENABLE (1 << 4) +/* This doesn't seem to be essential for tmds, but still often set */ +# define NV_RAMDAC_FP_DEBUG_0_TMDS_ENABLED (8 << 4) +# define NV_PRAMDAC_FP_DEBUG_0_XINTERP_BILINEAR (1 << 8) +# define NV_PRAMDAC_FP_DEBUG_0_YINTERP_BILINEAR (1 << 12) +# define NV_PRAMDAC_FP_DEBUG_0_XWEIGHT_ROUND (1 << 20) +# define NV_PRAMDAC_FP_DEBUG_0_YWEIGHT_ROUND (1 << 24) +# define NV_PRAMDAC_FP_DEBUG_0_PWRDOWN_FPCLK (1 << 28) +#define NV_PRAMDAC_FP_DEBUG_1 0x00680884 +# define NV_PRAMDAC_FP_DEBUG_1_XSCALE_VALUE 11:0 +# define NV_PRAMDAC_FP_DEBUG_1_XSCALE_TESTMODE_ENABLE (1 << 12) +# define NV_PRAMDAC_FP_DEBUG_1_YSCALE_VALUE 27:16 +# define NV_PRAMDAC_FP_DEBUG_1_YSCALE_TESTMODE_ENABLE (1 << 28) +#define NV_PRAMDAC_FP_DEBUG_2 0x00680888 +#define NV_PRAMDAC_FP_DEBUG_3 0x0068088C + +/* see NV_PRAMDAC_INDIR_TMDS in rules.xml */ +#define NV_PRAMDAC_FP_TMDS_CONTROL 0x006808b0 +# define NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE (1 << 16) +#define NV_PRAMDAC_FP_TMDS_DATA 0x006808b4 + +#define NV_PRAMDAC_8C0 0x006808c0 + +/* Some kind of switch */ +#define NV_PRAMDAC_900 0x00680900 +#define NV_PRAMDAC_A20 0x00680A20 +#define NV_PRAMDAC_A24 0x00680A24 +#define NV_PRAMDAC_A34 0x00680A34 + +#define NV_PRAMDAC_CTV 0x00680c00 + +/* names fabricated from NV_USER_DAC info */ +#define NV_PRMDIO_PIXEL_MASK 0x006813c6 +# define NV_PRMDIO_PIXEL_MASK_MASK 0xff +#define NV_PRMDIO_READ_MODE_ADDRESS 0x006813c7 +#define NV_PRMDIO_WRITE_MODE_ADDRESS 0x006813c8 +#define NV_PRMDIO_PALETTE_DATA 0x006813c9 + +#define NV_PGRAPH_DEBUG_0 0x00400080 +#define NV_PGRAPH_DEBUG_1 0x00400084 +#define NV_PGRAPH_DEBUG_2_NV04 0x00400088 +#define NV_PGRAPH_DEBUG_2 0x00400620 +#define NV_PGRAPH_DEBUG_3 0x0040008c +#define NV_PGRAPH_DEBUG_4 0x00400090 +#define NV_PGRAPH_INTR 0x00400100 +#define NV_PGRAPH_INTR_EN 0x00400140 +#define NV_PGRAPH_CTX_CONTROL 0x00400144 +#define NV_PGRAPH_CTX_CONTROL_NV04 0x00400170 +#define NV_PGRAPH_ABS_UCLIP_XMIN 0x0040053C +#define NV_PGRAPH_ABS_UCLIP_YMIN 0x00400540 +#define NV_PGRAPH_ABS_UCLIP_XMAX 0x00400544 +#define NV_PGRAPH_ABS_UCLIP_YMAX 0x00400548 +#define NV_PGRAPH_BETA_AND 0x00400608 +#define NV_PGRAPH_LIMIT_VIOL_PIX 0x00400610 +#define NV_PGRAPH_BOFFSET0 0x00400640 +#define NV_PGRAPH_BOFFSET1 0x00400644 +#define NV_PGRAPH_BOFFSET2 0x00400648 +#define NV_PGRAPH_BLIMIT0 0x00400684 +#define NV_PGRAPH_BLIMIT1 0x00400688 +#define NV_PGRAPH_BLIMIT2 0x0040068c +#define NV_PGRAPH_STATUS 0x00400700 +#define NV_PGRAPH_SURFACE 0x00400710 +#define NV_PGRAPH_STATE 0x00400714 +#define NV_PGRAPH_FIFO 0x00400720 +#define NV_PGRAPH_PATTERN_SHAPE 0x00400810 +#define NV_PGRAPH_TILE 0x00400b00 + +#define NV_PVIDEO_INTR_EN 0x00008140 +#define NV_PVIDEO_BUFFER 0x00008700 +#define NV_PVIDEO_STOP 0x00008704 +#define NV_PVIDEO_UVPLANE_BASE(buff) (0x00008800+(buff)*4) +#define NV_PVIDEO_UVPLANE_LIMIT(buff) (0x00008808+(buff)*4) +#define NV_PVIDEO_UVPLANE_OFFSET_BUFF(buff) (0x00008820+(buff)*4) +#define NV_PVIDEO_BASE(buff) (0x00008900+(buff)*4) +#define NV_PVIDEO_LIMIT(buff) (0x00008908+(buff)*4) +#define NV_PVIDEO_LUMINANCE(buff) (0x00008910+(buff)*4) +#define NV_PVIDEO_CHROMINANCE(buff) (0x00008918+(buff)*4) +#define NV_PVIDEO_OFFSET_BUFF(buff) (0x00008920+(buff)*4) +#define NV_PVIDEO_SIZE_IN(buff) (0x00008928+(buff)*4) +#define NV_PVIDEO_POINT_IN(buff) (0x00008930+(buff)*4) +#define NV_PVIDEO_DS_DX(buff) (0x00008938+(buff)*4) +#define NV_PVIDEO_DT_DY(buff) (0x00008940+(buff)*4) +#define NV_PVIDEO_POINT_OUT(buff) (0x00008948+(buff)*4) +#define NV_PVIDEO_SIZE_OUT(buff) (0x00008950+(buff)*4) +#define NV_PVIDEO_FORMAT(buff) (0x00008958+(buff)*4) +# define NV_PVIDEO_FORMAT_PLANAR (1 << 0) +# define NV_PVIDEO_FORMAT_COLOR_LE_CR8YB8CB8YA8 (1 << 16) +# define NV_PVIDEO_FORMAT_DISPLAY_COLOR_KEY (1 << 20) +# define NV_PVIDEO_FORMAT_MATRIX_ITURBT709 (1 << 24) +#define NV_PVIDEO_COLOR_KEY 0x00008B00 + +/* NV04 overlay defines from VIDIX & Haiku */ +#define NV_PVIDEO_INTR_EN_0 0x00680140 +#define NV_PVIDEO_STEP_SIZE 0x00680200 +#define NV_PVIDEO_CONTROL_Y 0x00680204 +#define NV_PVIDEO_CONTROL_X 0x00680208 +#define NV_PVIDEO_BUFF0_START_ADDRESS 0x0068020c +#define NV_PVIDEO_BUFF0_PITCH_LENGTH 0x00680214 +#define NV_PVIDEO_BUFF0_OFFSET 0x0068021c +#define NV_PVIDEO_BUFF1_START_ADDRESS 0x00680210 +#define NV_PVIDEO_BUFF1_PITCH_LENGTH 0x00680218 +#define NV_PVIDEO_BUFF1_OFFSET 0x00680220 +#define NV_PVIDEO_OE_STATE 0x00680224 +#define NV_PVIDEO_SU_STATE 0x00680228 +#define NV_PVIDEO_RM_STATE 0x0068022c +#define NV_PVIDEO_WINDOW_START 0x00680230 +#define NV_PVIDEO_WINDOW_SIZE 0x00680234 +#define NV_PVIDEO_FIFO_THRES_SIZE 0x00680238 +#define NV_PVIDEO_FIFO_BURST_LENGTH 0x0068023c +#define NV_PVIDEO_KEY 0x00680240 +#define NV_PVIDEO_OVERLAY 0x00680244 +#define NV_PVIDEO_RED_CSC_OFFSET 0x00680280 +#define NV_PVIDEO_GREEN_CSC_OFFSET 0x00680284 +#define NV_PVIDEO_BLUE_CSC_OFFSET 0x00680288 +#define NV_PVIDEO_CSC_ADJUST 0x0068028c + +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/dispnv04/overlay.c b/kernel/drivers/gpu/drm/nouveau/dispnv04/overlay.c new file mode 100644 index 000000000..9f2498571 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/dispnv04/overlay.c @@ -0,0 +1,502 @@ +/* + * 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 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. + * + * Implementation based on the pre-KMS implementation in xf86-video-nouveau, + * written by Arthur Huillet. + */ + +#include +#include +#include + +#include "nouveau_drm.h" + +#include "nouveau_bo.h" +#include "nouveau_connector.h" +#include "nouveau_display.h" +#include "nvreg.h" + + +struct nouveau_plane { + struct drm_plane base; + bool flip; + struct nouveau_bo *cur; + + struct { + struct drm_property *colorkey; + struct drm_property *contrast; + struct drm_property *brightness; + struct drm_property *hue; + struct drm_property *saturation; + struct drm_property *iturbt_709; + } props; + + int colorkey; + int contrast; + int brightness; + int hue; + int saturation; + int iturbt_709; + + void (*set_params)(struct nouveau_plane *); +}; + +static uint32_t formats[] = { + DRM_FORMAT_YUYV, + DRM_FORMAT_UYVY, + DRM_FORMAT_NV12, +}; + +/* Sine can be approximated with + * http://en.wikipedia.org/wiki/Bhaskara_I's_sine_approximation_formula + * sin(x degrees) ~= 4 x (180 - x) / (40500 - x (180 - x) ) + * Note that this only works for the range [0, 180]. + * Also note that sin(x) == -sin(x - 180) + */ +static inline int +sin_mul(int degrees, int factor) +{ + if (degrees > 180) { + degrees -= 180; + factor *= -1; + } + return factor * 4 * degrees * (180 - degrees) / + (40500 - degrees * (180 - degrees)); +} + +/* cos(x) = sin(x + 90) */ +static inline int +cos_mul(int degrees, int factor) +{ + return sin_mul((degrees + 90) % 360, factor); +} + +static int +nv10_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, + struct drm_framebuffer *fb, int crtc_x, int crtc_y, + unsigned int crtc_w, unsigned int crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h) +{ + struct nvif_device *dev = &nouveau_drm(plane->dev)->device; + struct nouveau_plane *nv_plane = + container_of(plane, struct nouveau_plane, base); + struct nouveau_framebuffer *nv_fb = nouveau_framebuffer(fb); + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct nouveau_bo *cur = nv_plane->cur; + bool flip = nv_plane->flip; + int soff = NV_PCRTC0_SIZE * nv_crtc->index; + int soff2 = NV_PCRTC0_SIZE * !nv_crtc->index; + int format, ret; + + /* Source parameters given in 16.16 fixed point, ignore fractional. */ + src_x >>= 16; + src_y >>= 16; + src_w >>= 16; + src_h >>= 16; + + format = ALIGN(src_w * 4, 0x100); + + if (format > 0xffff) + return -ERANGE; + + if (dev->info.chipset >= 0x30) { + if (crtc_w < (src_w >> 1) || crtc_h < (src_h >> 1)) + return -ERANGE; + } else { + if (crtc_w < (src_w >> 3) || crtc_h < (src_h >> 3)) + return -ERANGE; + } + + ret = nouveau_bo_pin(nv_fb->nvbo, TTM_PL_FLAG_VRAM, false); + if (ret) + return ret; + + nv_plane->cur = nv_fb->nvbo; + + nvif_mask(dev, NV_PCRTC_ENGINE_CTRL + soff, NV_CRTC_FSEL_OVERLAY, NV_CRTC_FSEL_OVERLAY); + nvif_mask(dev, NV_PCRTC_ENGINE_CTRL + soff2, NV_CRTC_FSEL_OVERLAY, 0); + + nvif_wr32(dev, NV_PVIDEO_BASE(flip), 0); + nvif_wr32(dev, NV_PVIDEO_OFFSET_BUFF(flip), nv_fb->nvbo->bo.offset); + nvif_wr32(dev, NV_PVIDEO_SIZE_IN(flip), src_h << 16 | src_w); + nvif_wr32(dev, NV_PVIDEO_POINT_IN(flip), src_y << 16 | src_x); + nvif_wr32(dev, NV_PVIDEO_DS_DX(flip), (src_w << 20) / crtc_w); + nvif_wr32(dev, NV_PVIDEO_DT_DY(flip), (src_h << 20) / crtc_h); + nvif_wr32(dev, NV_PVIDEO_POINT_OUT(flip), crtc_y << 16 | crtc_x); + nvif_wr32(dev, NV_PVIDEO_SIZE_OUT(flip), crtc_h << 16 | crtc_w); + + if (fb->pixel_format != DRM_FORMAT_UYVY) + format |= NV_PVIDEO_FORMAT_COLOR_LE_CR8YB8CB8YA8; + if (fb->pixel_format == DRM_FORMAT_NV12) + format |= NV_PVIDEO_FORMAT_PLANAR; + if (nv_plane->iturbt_709) + format |= NV_PVIDEO_FORMAT_MATRIX_ITURBT709; + if (nv_plane->colorkey & (1 << 24)) + format |= NV_PVIDEO_FORMAT_DISPLAY_COLOR_KEY; + + if (fb->pixel_format == DRM_FORMAT_NV12) { + nvif_wr32(dev, NV_PVIDEO_UVPLANE_BASE(flip), 0); + nvif_wr32(dev, NV_PVIDEO_UVPLANE_OFFSET_BUFF(flip), + nv_fb->nvbo->bo.offset + fb->offsets[1]); + } + nvif_wr32(dev, NV_PVIDEO_FORMAT(flip), format); + nvif_wr32(dev, NV_PVIDEO_STOP, 0); + /* TODO: wait for vblank? */ + nvif_wr32(dev, NV_PVIDEO_BUFFER, flip ? 0x10 : 0x1); + nv_plane->flip = !flip; + + if (cur) + nouveau_bo_unpin(cur); + + return 0; +} + +static int +nv10_disable_plane(struct drm_plane *plane) +{ + struct nvif_device *dev = &nouveau_drm(plane->dev)->device; + struct nouveau_plane *nv_plane = + container_of(plane, struct nouveau_plane, base); + + nvif_wr32(dev, NV_PVIDEO_STOP, 1); + if (nv_plane->cur) { + nouveau_bo_unpin(nv_plane->cur); + nv_plane->cur = NULL; + } + + return 0; +} + +static void +nv_destroy_plane(struct drm_plane *plane) +{ + plane->funcs->disable_plane(plane); + drm_plane_cleanup(plane); + kfree(plane); +} + +static void +nv10_set_params(struct nouveau_plane *plane) +{ + struct nvif_device *dev = &nouveau_drm(plane->base.dev)->device; + u32 luma = (plane->brightness - 512) << 16 | plane->contrast; + u32 chroma = ((sin_mul(plane->hue, plane->saturation) & 0xffff) << 16) | + (cos_mul(plane->hue, plane->saturation) & 0xffff); + u32 format = 0; + + nvif_wr32(dev, NV_PVIDEO_LUMINANCE(0), luma); + nvif_wr32(dev, NV_PVIDEO_LUMINANCE(1), luma); + nvif_wr32(dev, NV_PVIDEO_CHROMINANCE(0), chroma); + nvif_wr32(dev, NV_PVIDEO_CHROMINANCE(1), chroma); + nvif_wr32(dev, NV_PVIDEO_COLOR_KEY, plane->colorkey & 0xffffff); + + if (plane->cur) { + if (plane->iturbt_709) + format |= NV_PVIDEO_FORMAT_MATRIX_ITURBT709; + if (plane->colorkey & (1 << 24)) + format |= NV_PVIDEO_FORMAT_DISPLAY_COLOR_KEY; + nvif_mask(dev, NV_PVIDEO_FORMAT(plane->flip), + NV_PVIDEO_FORMAT_MATRIX_ITURBT709 | + NV_PVIDEO_FORMAT_DISPLAY_COLOR_KEY, + format); + } +} + +static int +nv_set_property(struct drm_plane *plane, + struct drm_property *property, + uint64_t value) +{ + struct nouveau_plane *nv_plane = + container_of(plane, struct nouveau_plane, base); + + if (property == nv_plane->props.colorkey) + nv_plane->colorkey = value; + else if (property == nv_plane->props.contrast) + nv_plane->contrast = value; + else if (property == nv_plane->props.brightness) + nv_plane->brightness = value; + else if (property == nv_plane->props.hue) + nv_plane->hue = value; + else if (property == nv_plane->props.saturation) + nv_plane->saturation = value; + else if (property == nv_plane->props.iturbt_709) + nv_plane->iturbt_709 = value; + else + return -EINVAL; + + if (nv_plane->set_params) + nv_plane->set_params(nv_plane); + return 0; +} + +static const struct drm_plane_funcs nv10_plane_funcs = { + .update_plane = nv10_update_plane, + .disable_plane = nv10_disable_plane, + .set_property = nv_set_property, + .destroy = nv_destroy_plane, +}; + +static void +nv10_overlay_init(struct drm_device *device) +{ + struct nouveau_drm *drm = nouveau_drm(device); + struct nouveau_plane *plane = kzalloc(sizeof(struct nouveau_plane), GFP_KERNEL); + int num_formats = ARRAY_SIZE(formats); + int ret; + + if (!plane) + return; + + switch (drm->device.info.chipset) { + case 0x10: + case 0x11: + case 0x15: + case 0x1a: + case 0x20: + num_formats = 2; + break; + } + + ret = drm_plane_init(device, &plane->base, 3 /* both crtc's */, + &nv10_plane_funcs, + formats, num_formats, false); + if (ret) + goto err; + + /* Set up the plane properties */ + plane->props.colorkey = drm_property_create_range( + device, 0, "colorkey", 0, 0x01ffffff); + plane->props.contrast = drm_property_create_range( + device, 0, "contrast", 0, 8192 - 1); + plane->props.brightness = drm_property_create_range( + device, 0, "brightness", 0, 1024); + plane->props.hue = drm_property_create_range( + device, 0, "hue", 0, 359); + plane->props.saturation = drm_property_create_range( + device, 0, "saturation", 0, 8192 - 1); + plane->props.iturbt_709 = drm_property_create_range( + device, 0, "iturbt_709", 0, 1); + if (!plane->props.colorkey || + !plane->props.contrast || + !plane->props.brightness || + !plane->props.hue || + !plane->props.saturation || + !plane->props.iturbt_709) + goto cleanup; + + plane->colorkey = 0; + drm_object_attach_property(&plane->base.base, + plane->props.colorkey, plane->colorkey); + + plane->contrast = 0x1000; + drm_object_attach_property(&plane->base.base, + plane->props.contrast, plane->contrast); + + plane->brightness = 512; + drm_object_attach_property(&plane->base.base, + plane->props.brightness, plane->brightness); + + plane->hue = 0; + drm_object_attach_property(&plane->base.base, + plane->props.hue, plane->hue); + + plane->saturation = 0x1000; + drm_object_attach_property(&plane->base.base, + plane->props.saturation, plane->saturation); + + plane->iturbt_709 = 0; + drm_object_attach_property(&plane->base.base, + plane->props.iturbt_709, plane->iturbt_709); + + plane->set_params = nv10_set_params; + nv10_set_params(plane); + nv10_disable_plane(&plane->base); + return; +cleanup: + drm_plane_cleanup(&plane->base); +err: + kfree(plane); + NV_ERROR(drm, "Failed to create plane\n"); +} + +static int +nv04_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, + struct drm_framebuffer *fb, int crtc_x, int crtc_y, + unsigned int crtc_w, unsigned int crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h) +{ + struct nvif_device *dev = &nouveau_drm(plane->dev)->device; + struct nouveau_plane *nv_plane = + container_of(plane, struct nouveau_plane, base); + struct nouveau_framebuffer *nv_fb = nouveau_framebuffer(fb); + struct nouveau_bo *cur = nv_plane->cur; + uint32_t overlay = 1; + int brightness = (nv_plane->brightness - 512) * 62 / 512; + int pitch, ret, i; + + /* Source parameters given in 16.16 fixed point, ignore fractional. */ + src_x >>= 16; + src_y >>= 16; + src_w >>= 16; + src_h >>= 16; + + pitch = ALIGN(src_w * 4, 0x100); + + if (pitch > 0xffff) + return -ERANGE; + + /* TODO: Compute an offset? Not sure how to do this for YUYV. */ + if (src_x != 0 || src_y != 0) + return -ERANGE; + + if (crtc_w < src_w || crtc_h < src_h) + return -ERANGE; + + ret = nouveau_bo_pin(nv_fb->nvbo, TTM_PL_FLAG_VRAM, false); + if (ret) + return ret; + + nv_plane->cur = nv_fb->nvbo; + + nvif_wr32(dev, NV_PVIDEO_OE_STATE, 0); + nvif_wr32(dev, NV_PVIDEO_SU_STATE, 0); + nvif_wr32(dev, NV_PVIDEO_RM_STATE, 0); + + for (i = 0; i < 2; i++) { + nvif_wr32(dev, NV_PVIDEO_BUFF0_START_ADDRESS + 4 * i, + nv_fb->nvbo->bo.offset); + nvif_wr32(dev, NV_PVIDEO_BUFF0_PITCH_LENGTH + 4 * i, pitch); + nvif_wr32(dev, NV_PVIDEO_BUFF0_OFFSET + 4 * i, 0); + } + nvif_wr32(dev, NV_PVIDEO_WINDOW_START, crtc_y << 16 | crtc_x); + nvif_wr32(dev, NV_PVIDEO_WINDOW_SIZE, crtc_h << 16 | crtc_w); + nvif_wr32(dev, NV_PVIDEO_STEP_SIZE, + (uint32_t)(((src_h - 1) << 11) / (crtc_h - 1)) << 16 | (uint32_t)(((src_w - 1) << 11) / (crtc_w - 1))); + + /* It should be possible to convert hue/contrast to this */ + nvif_wr32(dev, NV_PVIDEO_RED_CSC_OFFSET, 0x69 - brightness); + nvif_wr32(dev, NV_PVIDEO_GREEN_CSC_OFFSET, 0x3e + brightness); + nvif_wr32(dev, NV_PVIDEO_BLUE_CSC_OFFSET, 0x89 - brightness); + nvif_wr32(dev, NV_PVIDEO_CSC_ADJUST, 0); + + nvif_wr32(dev, NV_PVIDEO_CONTROL_Y, 0x001); /* (BLUR_ON, LINE_HALF) */ + nvif_wr32(dev, NV_PVIDEO_CONTROL_X, 0x111); /* (WEIGHT_HEAVY, SHARPENING_ON, SMOOTHING_ON) */ + + nvif_wr32(dev, NV_PVIDEO_FIFO_BURST_LENGTH, 0x03); + nvif_wr32(dev, NV_PVIDEO_FIFO_THRES_SIZE, 0x38); + + nvif_wr32(dev, NV_PVIDEO_KEY, nv_plane->colorkey); + + if (nv_plane->colorkey & (1 << 24)) + overlay |= 0x10; + if (fb->pixel_format == DRM_FORMAT_YUYV) + overlay |= 0x100; + + nvif_wr32(dev, NV_PVIDEO_OVERLAY, overlay); + + nvif_wr32(dev, NV_PVIDEO_SU_STATE, nvif_rd32(dev, NV_PVIDEO_SU_STATE) ^ (1 << 16)); + + if (cur) + nouveau_bo_unpin(cur); + + return 0; +} + +static int +nv04_disable_plane(struct drm_plane *plane) +{ + struct nvif_device *dev = &nouveau_drm(plane->dev)->device; + struct nouveau_plane *nv_plane = + container_of(plane, struct nouveau_plane, base); + + nvif_mask(dev, NV_PVIDEO_OVERLAY, 1, 0); + nvif_wr32(dev, NV_PVIDEO_OE_STATE, 0); + nvif_wr32(dev, NV_PVIDEO_SU_STATE, 0); + nvif_wr32(dev, NV_PVIDEO_RM_STATE, 0); + if (nv_plane->cur) { + nouveau_bo_unpin(nv_plane->cur); + nv_plane->cur = NULL; + } + + return 0; +} + +static const struct drm_plane_funcs nv04_plane_funcs = { + .update_plane = nv04_update_plane, + .disable_plane = nv04_disable_plane, + .set_property = nv_set_property, + .destroy = nv_destroy_plane, +}; + +static void +nv04_overlay_init(struct drm_device *device) +{ + struct nouveau_drm *drm = nouveau_drm(device); + struct nouveau_plane *plane = kzalloc(sizeof(struct nouveau_plane), GFP_KERNEL); + int ret; + + if (!plane) + return; + + ret = drm_plane_init(device, &plane->base, 1 /* single crtc */, + &nv04_plane_funcs, + formats, 2, false); + if (ret) + goto err; + + /* Set up the plane properties */ + plane->props.colorkey = drm_property_create_range( + device, 0, "colorkey", 0, 0x01ffffff); + plane->props.brightness = drm_property_create_range( + device, 0, "brightness", 0, 1024); + if (!plane->props.colorkey || + !plane->props.brightness) + goto cleanup; + + plane->colorkey = 0; + drm_object_attach_property(&plane->base.base, + plane->props.colorkey, plane->colorkey); + + plane->brightness = 512; + drm_object_attach_property(&plane->base.base, + plane->props.brightness, plane->brightness); + + nv04_disable_plane(&plane->base); + return; +cleanup: + drm_plane_cleanup(&plane->base); +err: + kfree(plane); + NV_ERROR(drm, "Failed to create plane\n"); +} + +void +nouveau_overlay_init(struct drm_device *device) +{ + struct nvif_device *dev = &nouveau_drm(device)->device; + if (dev->info.chipset < 0x10) + nv04_overlay_init(device); + else if (dev->info.chipset <= 0x40) + nv10_overlay_init(device); +} diff --git a/kernel/drivers/gpu/drm/nouveau/dispnv04/tvmodesnv17.c b/kernel/drivers/gpu/drm/nouveau/dispnv04/tvmodesnv17.c new file mode 100644 index 000000000..08c6f5e50 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/dispnv04/tvmodesnv17.c @@ -0,0 +1,592 @@ +/* + * 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 +#include +#include "nouveau_drm.h" +#include "nouveau_encoder.h" +#include "nouveau_crtc.h" +#include "hw.h" +#include "tvnv17.h" + +char *nv17_tv_norm_names[NUM_TV_NORMS] = { + [TV_NORM_PAL] = "PAL", + [TV_NORM_PAL_M] = "PAL-M", + [TV_NORM_PAL_N] = "PAL-N", + [TV_NORM_PAL_NC] = "PAL-Nc", + [TV_NORM_NTSC_M] = "NTSC-M", + [TV_NORM_NTSC_J] = "NTSC-J", + [TV_NORM_HD480I] = "hd480i", + [TV_NORM_HD480P] = "hd480p", + [TV_NORM_HD576I] = "hd576i", + [TV_NORM_HD576P] = "hd576p", + [TV_NORM_HD720P] = "hd720p", + [TV_NORM_HD1080I] = "hd1080i" +}; + +/* TV standard specific parameters */ + +struct nv17_tv_norm_params nv17_tv_norms[NUM_TV_NORMS] = { + [TV_NORM_PAL] = { TV_ENC_MODE, { + .tv_enc_mode = { 720, 576, 50000, { + 0x2a, 0x9, 0x8a, 0xcb, 0x0, 0x0, 0xb, 0x18, + 0x7e, 0x40, 0x8a, 0x35, 0x27, 0x0, 0x34, 0x3, + 0x3e, 0x3, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c, + 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x3, + 0xd3, 0x4, 0xd4, 0x1, 0x2, 0x0, 0xa, 0x5, + 0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0, + 0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b, + 0xbd, 0x15, 0x5, 0x15, 0x3e, 0x3, 0x0, 0x0 + } } } }, + + [TV_NORM_PAL_M] = { TV_ENC_MODE, { + .tv_enc_mode = { 720, 480, 59940, { + 0x21, 0xe6, 0xef, 0xe3, 0x0, 0x0, 0xb, 0x18, + 0x7e, 0x44, 0x76, 0x32, 0x25, 0x0, 0x3c, 0x0, + 0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83, + 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1, + 0xc5, 0x4, 0xc5, 0x1, 0x2, 0x0, 0xa, 0x5, + 0x0, 0x18, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0, + 0x0, 0xb4, 0x0, 0x15, 0x40, 0x10, 0x0, 0x9c, + 0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0 + } } } }, + + [TV_NORM_PAL_N] = { TV_ENC_MODE, { + .tv_enc_mode = { 720, 576, 50000, { + 0x2a, 0x9, 0x8a, 0xcb, 0x0, 0x0, 0xb, 0x18, + 0x7e, 0x40, 0x8a, 0x32, 0x25, 0x0, 0x3c, 0x0, + 0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c, + 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1, + 0xc5, 0x4, 0xc5, 0x1, 0x2, 0x0, 0xa, 0x5, + 0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0, + 0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b, + 0xbd, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0 + } } } }, + + [TV_NORM_PAL_NC] = { TV_ENC_MODE, { + .tv_enc_mode = { 720, 576, 50000, { + 0x21, 0xf6, 0x94, 0x46, 0x0, 0x0, 0xb, 0x18, + 0x7e, 0x44, 0x8a, 0x35, 0x27, 0x0, 0x34, 0x3, + 0x3e, 0x3, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c, + 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x3, + 0xd3, 0x4, 0xd4, 0x1, 0x2, 0x0, 0xa, 0x5, + 0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0, + 0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b, + 0xbd, 0x15, 0x5, 0x15, 0x3e, 0x3, 0x0, 0x0 + } } } }, + + [TV_NORM_NTSC_M] = { TV_ENC_MODE, { + .tv_enc_mode = { 720, 480, 59940, { + 0x21, 0xf0, 0x7c, 0x1f, 0x0, 0x0, 0xb, 0x18, + 0x7e, 0x44, 0x76, 0x48, 0x0, 0x0, 0x3c, 0x0, + 0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83, + 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1, + 0xc5, 0x4, 0xc5, 0x1, 0x2, 0x0, 0xa, 0x5, + 0x0, 0x16, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0, + 0x0, 0xb4, 0x0, 0x15, 0x4, 0x10, 0x0, 0x9c, + 0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0 + } } } }, + + [TV_NORM_NTSC_J] = { TV_ENC_MODE, { + .tv_enc_mode = { 720, 480, 59940, { + 0x21, 0xf0, 0x7c, 0x1f, 0x0, 0x0, 0xb, 0x18, + 0x7e, 0x44, 0x76, 0x48, 0x0, 0x0, 0x32, 0x0, + 0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83, + 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1, + 0xcf, 0x4, 0xcf, 0x1, 0x2, 0x0, 0xa, 0x5, + 0x0, 0x16, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0, + 0x0, 0xb4, 0x0, 0x15, 0x4, 0x10, 0x0, 0xa4, + 0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0 + } } } }, + + [TV_NORM_HD480I] = { TV_ENC_MODE, { + .tv_enc_mode = { 720, 480, 59940, { + 0x21, 0xf0, 0x7c, 0x1f, 0x0, 0x0, 0xb, 0x18, + 0x7e, 0x44, 0x76, 0x48, 0x0, 0x0, 0x32, 0x0, + 0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83, + 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1, + 0xcf, 0x4, 0xcf, 0x1, 0x2, 0x0, 0xa, 0x5, + 0x0, 0x16, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0, + 0x0, 0xb4, 0x0, 0x15, 0x4, 0x10, 0x0, 0xa4, + 0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0 + } } } }, + + [TV_NORM_HD576I] = { TV_ENC_MODE, { + .tv_enc_mode = { 720, 576, 50000, { + 0x2a, 0x9, 0x8a, 0xcb, 0x0, 0x0, 0xb, 0x18, + 0x7e, 0x40, 0x8a, 0x35, 0x27, 0x0, 0x34, 0x3, + 0x3e, 0x3, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c, + 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x3, + 0xd3, 0x4, 0xd4, 0x1, 0x2, 0x0, 0xa, 0x5, + 0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0, + 0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b, + 0xbd, 0x15, 0x5, 0x15, 0x3e, 0x3, 0x0, 0x0 + } } } }, + + + [TV_NORM_HD480P] = { CTV_ENC_MODE, { + .ctv_enc_mode = { + .mode = { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, + 720, 735, 743, 858, 0, 480, 490, 494, 525, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + .ctv_regs = { 0x3540000, 0x0, 0x0, 0x314, + 0x354003a, 0x40000, 0x6f0344, 0x18100000, + 0x10160004, 0x10060005, 0x1006000c, 0x10060020, + 0x10060021, 0x140e0022, 0x10060202, 0x1802020a, + 0x1810020b, 0x10000fff, 0x10000fff, 0x10000fff, + 0x10000fff, 0x10000fff, 0x10000fff, 0x70, + 0x3ff0000, 0x57, 0x2e001e, 0x258012c, + 0xa0aa04ec, 0x30, 0x80960019, 0x12c0300, + 0x2019, 0x600, 0x32060019, 0x0, 0x0, 0x400 + } } } }, + + [TV_NORM_HD576P] = { CTV_ENC_MODE, { + .ctv_enc_mode = { + .mode = { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000, + 720, 730, 738, 864, 0, 576, 581, 585, 625, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + .ctv_regs = { 0x3540000, 0x0, 0x0, 0x314, + 0x354003a, 0x40000, 0x6f0344, 0x18100000, + 0x10060001, 0x10060009, 0x10060026, 0x10060027, + 0x140e0028, 0x10060268, 0x1810026d, 0x10000fff, + 0x10000fff, 0x10000fff, 0x10000fff, 0x10000fff, + 0x10000fff, 0x10000fff, 0x10000fff, 0x69, + 0x3ff0000, 0x57, 0x2e001e, 0x258012c, + 0xa0aa04ec, 0x30, 0x80960019, 0x12c0300, + 0x2019, 0x600, 0x32060019, 0x0, 0x0, 0x400 + } } } }, + + [TV_NORM_HD720P] = { CTV_ENC_MODE, { + .ctv_enc_mode = { + .mode = { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, + 1280, 1349, 1357, 1650, 0, 720, 725, 730, 750, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + .ctv_regs = { 0x1260394, 0x0, 0x0, 0x622, + 0x66b0021, 0x6004a, 0x1210626, 0x8170000, + 0x70004, 0x70016, 0x70017, 0x40f0018, + 0x702e8, 0x81702ed, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x0, + 0x2e40001, 0x58, 0x2e001e, 0x258012c, + 0xa0aa04ec, 0x30, 0x810c0039, 0x12c0300, + 0xc0002039, 0x600, 0x32060039, 0x0, 0x0, 0x0 + } } } }, + + [TV_NORM_HD1080I] = { CTV_ENC_MODE, { + .ctv_enc_mode = { + .mode = { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, + 1920, 1961, 2049, 2200, 0, 1080, 1084, 1088, 1125, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC + | DRM_MODE_FLAG_INTERLACE) }, + .ctv_regs = { 0xac0420, 0x44c0478, 0x4a4, 0x4fc0868, + 0x8940028, 0x60054, 0xe80870, 0xbf70000, + 0xbc70004, 0x70005, 0x70012, 0x70013, + 0x40f0014, 0x70230, 0xbf70232, 0xbf70233, + 0x1c70237, 0x70238, 0x70244, 0x70245, + 0x40f0246, 0x70462, 0x1f70464, 0x0, + 0x2e40001, 0x58, 0x2e001e, 0x258012c, + 0xa0aa04ec, 0x30, 0x815f004c, 0x12c0300, + 0xc000204c, 0x600, 0x3206004c, 0x0, 0x0, 0x0 + } } } } +}; + +/* + * The following is some guesswork on how the TV encoder flicker + * filter/rescaler works: + * + * It seems to use some sort of resampling filter, it is controlled + * through the registers at NV_PTV_HFILTER and NV_PTV_VFILTER, they + * control the horizontal and vertical stage respectively, there is + * also NV_PTV_HFILTER2 the blob fills identically to NV_PTV_HFILTER, + * but they seem to do nothing. A rough guess might be that they could + * be used to independently control the filtering of each interlaced + * field, but I don't know how they are enabled. The whole filtering + * process seems to be disabled with bits 26:27 of PTV_200, but we + * aren't doing that. + * + * The layout of both register sets is the same: + * + * A: [BASE+0x18]...[BASE+0x0] [BASE+0x58]..[BASE+0x40] + * B: [BASE+0x34]...[BASE+0x1c] [BASE+0x74]..[BASE+0x5c] + * + * Each coefficient is stored in bits [31],[15:9] in two's complement + * format. They seem to be some kind of weights used in a low-pass + * filter. Both A and B coefficients are applied to the 14 nearest + * samples on each side (Listed from nearest to furthermost. They + * roughly cover 2 framebuffer pixels on each side). They are + * probably multiplied with some more hardwired weights before being + * used: B-coefficients are applied the same on both sides, + * A-coefficients are inverted before being applied to the opposite + * side. + * + * After all the hassle, I got the following formula by empirical + * means... + */ + +#define calc_overscan(o) interpolate(0x100, 0xe1, 0xc1, o) + +#define id1 (1LL << 8) +#define id2 (1LL << 16) +#define id3 (1LL << 24) +#define id4 (1LL << 32) +#define id5 (1LL << 48) + +static struct filter_params{ + int64_t k1; + int64_t ki; + int64_t ki2; + int64_t ki3; + int64_t kr; + int64_t kir; + int64_t ki2r; + int64_t ki3r; + int64_t kf; + int64_t kif; + int64_t ki2f; + int64_t ki3f; + int64_t krf; + int64_t kirf; + int64_t ki2rf; + int64_t ki3rf; +} fparams[2][4] = { + /* Horizontal filter parameters */ + { + {64.311690 * id5, -39.516924 * id5, 6.586143 * id5, 0.000002 * id5, + 0.051285 * id4, 26.168746 * id4, -4.361449 * id4, -0.000001 * id4, + 9.308169 * id3, 78.180965 * id3, -13.030158 * id3, -0.000001 * id3, + -8.801540 * id1, -46.572890 * id1, 7.762145 * id1, -0.000000 * id1}, + {-44.565569 * id5, -68.081246 * id5, 39.812074 * id5, -4.009316 * id5, + 29.832207 * id4, 50.047322 * id4, -25.380017 * id4, 2.546422 * id4, + 104.605622 * id3, 141.908641 * id3, -74.322319 * id3, 7.484316 * id3, + -37.081621 * id1, -90.397510 * id1, 42.784229 * id1, -4.289952 * id1}, + {-56.793244 * id5, 31.153584 * id5, -5.192247 * id5, -0.000003 * id5, + 33.541131 * id4, -34.149302 * id4, 5.691537 * id4, 0.000002 * id4, + 87.196610 * id3, -88.995169 * id3, 14.832456 * id3, 0.000012 * id3, + 17.288138 * id1, 71.864786 * id1, -11.977408 * id1, -0.000009 * id1}, + {51.787796 * id5, 21.211771 * id5, -18.993730 * id5, 1.853310 * id5, + -41.470726 * id4, -17.775823 * id4, 13.057821 * id4, -1.15823 * id4, + -154.235673 * id3, -44.878641 * id3, 40.656077 * id3, -3.695595 * id3, + 112.201065 * id1, 39.992155 * id1, -25.155714 * id1, 2.113984 * id1}, + }, + + /* Vertical filter parameters */ + { + {67.601979 * id5, 0.428319 * id5, -0.071318 * id5, -0.000012 * id5, + -3.402339 * id4, 0.000209 * id4, -0.000092 * id4, 0.000010 * id4, + -9.180996 * id3, 6.111270 * id3, -1.024457 * id3, 0.001043 * id3, + 6.060315 * id1, -0.017425 * id1, 0.007830 * id1, -0.000869 * id1}, + {6.755647 * id5, 5.841348 * id5, 1.469734 * id5, -0.149656 * id5, + 8.293120 * id4, -1.192888 * id4, -0.947652 * id4, 0.094507 * id4, + 37.526655 * id3, 10.257875 * id3, -10.823275 * id3, 1.081497 * id3, + -2.361928 * id1, -2.059432 * id1, 1.840671 * id1, -0.168100 * id1}, + {-14.780391 * id5, -16.042148 * id5, 2.673692 * id5, -0.000000 * id5, + 39.541978 * id4, 5.680053 * id4, -0.946676 * id4, 0.000000 * id4, + 152.994486 * id3, 12.625439 * id3, -2.119579 * id3, 0.002708 * id3, + -38.125089 * id1, -0.855880 * id1, 0.155359 * id1, -0.002245 * id1}, + {-27.476193 * id5, -1.454976 * id5, 1.286557 * id5, 0.025346 * id5, + 20.687300 * id4, 3.014003 * id4, -0.557786 * id4, -0.01311 * id4, + 60.008737 * id3, -0.738273 * id3, 5.408217 * id3, -0.796798 * id3, + -17.296835 * id1, 4.438577 * id1, -2.809420 * id1, 0.385491 * id1}, + } +}; + +static void tv_setup_filter(struct drm_encoder *encoder) +{ + struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder); + struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); + struct drm_display_mode *mode = &encoder->crtc->mode; + uint32_t (*filters[])[4][7] = {&tv_enc->state.hfilter, + &tv_enc->state.vfilter}; + int i, j, k; + int32_t overscan = calc_overscan(tv_enc->overscan); + int64_t flicker = (tv_enc->flicker - 50) * (id3 / 100); + uint64_t rs[] = {mode->hdisplay * id3, + mode->vdisplay * id3}; + + do_div(rs[0], overscan * tv_norm->tv_enc_mode.hdisplay); + do_div(rs[1], overscan * tv_norm->tv_enc_mode.vdisplay); + + for (k = 0; k < 2; k++) { + rs[k] = max((int64_t)rs[k], id2); + + for (j = 0; j < 4; j++) { + struct filter_params *p = &fparams[k][j]; + + for (i = 0; i < 7; i++) { + int64_t c = (p->k1 + p->ki*i + p->ki2*i*i + + p->ki3*i*i*i) + + (p->kr + p->kir*i + p->ki2r*i*i + + p->ki3r*i*i*i) * rs[k] + + (p->kf + p->kif*i + p->ki2f*i*i + + p->ki3f*i*i*i) * flicker + + (p->krf + p->kirf*i + p->ki2rf*i*i + + p->ki3rf*i*i*i) * flicker * rs[k]; + + (*filters[k])[j][i] = (c + id5/2) >> 39 + & (0x1 << 31 | 0x7f << 9); + } + } + } +} + +/* Hardware state saving/restoring */ + +static void tv_save_filter(struct drm_device *dev, uint32_t base, + uint32_t regs[4][7]) +{ + int i, j; + uint32_t offsets[] = { base, base + 0x1c, base + 0x40, base + 0x5c }; + + for (i = 0; i < 4; i++) { + for (j = 0; j < 7; j++) + regs[i][j] = nv_read_ptv(dev, offsets[i]+4*j); + } +} + +static void tv_load_filter(struct drm_device *dev, uint32_t base, + uint32_t regs[4][7]) +{ + int i, j; + uint32_t offsets[] = { base, base + 0x1c, base + 0x40, base + 0x5c }; + + for (i = 0; i < 4; i++) { + for (j = 0; j < 7; j++) + nv_write_ptv(dev, offsets[i]+4*j, regs[i][j]); + } +} + +void nv17_tv_state_save(struct drm_device *dev, struct nv17_tv_state *state) +{ + int i; + + for (i = 0; i < 0x40; i++) + state->tv_enc[i] = nv_read_tv_enc(dev, i); + + tv_save_filter(dev, NV_PTV_HFILTER, state->hfilter); + tv_save_filter(dev, NV_PTV_HFILTER2, state->hfilter2); + tv_save_filter(dev, NV_PTV_VFILTER, state->vfilter); + + nv_save_ptv(dev, state, 200); + nv_save_ptv(dev, state, 204); + nv_save_ptv(dev, state, 208); + nv_save_ptv(dev, state, 20c); + nv_save_ptv(dev, state, 304); + nv_save_ptv(dev, state, 500); + nv_save_ptv(dev, state, 504); + nv_save_ptv(dev, state, 508); + nv_save_ptv(dev, state, 600); + nv_save_ptv(dev, state, 604); + nv_save_ptv(dev, state, 608); + nv_save_ptv(dev, state, 60c); + nv_save_ptv(dev, state, 610); + nv_save_ptv(dev, state, 614); +} + +void nv17_tv_state_load(struct drm_device *dev, struct nv17_tv_state *state) +{ + int i; + + for (i = 0; i < 0x40; i++) + nv_write_tv_enc(dev, i, state->tv_enc[i]); + + tv_load_filter(dev, NV_PTV_HFILTER, state->hfilter); + tv_load_filter(dev, NV_PTV_HFILTER2, state->hfilter2); + tv_load_filter(dev, NV_PTV_VFILTER, state->vfilter); + + nv_load_ptv(dev, state, 200); + nv_load_ptv(dev, state, 204); + nv_load_ptv(dev, state, 208); + nv_load_ptv(dev, state, 20c); + nv_load_ptv(dev, state, 304); + nv_load_ptv(dev, state, 500); + nv_load_ptv(dev, state, 504); + nv_load_ptv(dev, state, 508); + nv_load_ptv(dev, state, 600); + nv_load_ptv(dev, state, 604); + nv_load_ptv(dev, state, 608); + nv_load_ptv(dev, state, 60c); + nv_load_ptv(dev, state, 610); + nv_load_ptv(dev, state, 614); + + /* This is required for some settings to kick in. */ + nv_write_tv_enc(dev, 0x3e, 1); + nv_write_tv_enc(dev, 0x3e, 0); +} + +/* Timings similar to the ones the blob sets */ + +const struct drm_display_mode nv17_tv_modes[] = { + { DRM_MODE("320x200", DRM_MODE_TYPE_DRIVER, 0, + 320, 344, 392, 560, 0, 200, 200, 202, 220, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC + | DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_CLKDIV2) }, + { DRM_MODE("320x240", DRM_MODE_TYPE_DRIVER, 0, + 320, 344, 392, 560, 0, 240, 240, 246, 263, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC + | DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_CLKDIV2) }, + { DRM_MODE("400x300", DRM_MODE_TYPE_DRIVER, 0, + 400, 432, 496, 640, 0, 300, 300, 303, 314, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC + | DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_CLKDIV2) }, + { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 0, + 640, 672, 768, 880, 0, 480, 480, 492, 525, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, + { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 0, + 720, 752, 872, 960, 0, 480, 480, 493, 525, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, + { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 0, + 720, 776, 856, 960, 0, 576, 576, 588, 597, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, + { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 0, + 800, 840, 920, 1040, 0, 600, 600, 604, 618, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 0, + 1024, 1064, 1200, 1344, 0, 768, 768, 777, 806, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, + {} +}; + +void nv17_tv_update_properties(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder); + struct nv17_tv_state *regs = &tv_enc->state; + struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); + int subconnector = tv_enc->select_subconnector ? + tv_enc->select_subconnector : + tv_enc->subconnector; + + switch (subconnector) { + case DRM_MODE_SUBCONNECTOR_Composite: + { + regs->ptv_204 = 0x2; + + /* The composite connector may be found on either pin. */ + if (tv_enc->pin_mask & 0x4) + regs->ptv_204 |= 0x010000; + else if (tv_enc->pin_mask & 0x2) + regs->ptv_204 |= 0x100000; + else + regs->ptv_204 |= 0x110000; + + regs->tv_enc[0x7] = 0x10; + break; + } + case DRM_MODE_SUBCONNECTOR_SVIDEO: + regs->ptv_204 = 0x11012; + regs->tv_enc[0x7] = 0x18; + break; + + case DRM_MODE_SUBCONNECTOR_Component: + regs->ptv_204 = 0x111333; + regs->tv_enc[0x7] = 0x14; + break; + + case DRM_MODE_SUBCONNECTOR_SCART: + regs->ptv_204 = 0x111012; + regs->tv_enc[0x7] = 0x18; + break; + } + + regs->tv_enc[0x20] = interpolate(0, tv_norm->tv_enc_mode.tv_enc[0x20], + 255, tv_enc->saturation); + regs->tv_enc[0x22] = interpolate(0, tv_norm->tv_enc_mode.tv_enc[0x22], + 255, tv_enc->saturation); + regs->tv_enc[0x25] = tv_enc->hue * 255 / 100; + + nv_load_ptv(dev, regs, 204); + nv_load_tv_enc(dev, regs, 7); + nv_load_tv_enc(dev, regs, 20); + nv_load_tv_enc(dev, regs, 22); + nv_load_tv_enc(dev, regs, 25); +} + +void nv17_tv_update_rescaler(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder); + struct nv17_tv_state *regs = &tv_enc->state; + + regs->ptv_208 = 0x40 | (calc_overscan(tv_enc->overscan) << 8); + + tv_setup_filter(encoder); + + nv_load_ptv(dev, regs, 208); + tv_load_filter(dev, NV_PTV_HFILTER, regs->hfilter); + tv_load_filter(dev, NV_PTV_HFILTER2, regs->hfilter2); + tv_load_filter(dev, NV_PTV_VFILTER, regs->vfilter); +} + +void nv17_ctv_update_rescaler(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder); + int head = nouveau_crtc(encoder->crtc)->index; + struct nv04_crtc_reg *regs = &nv04_display(dev)->mode_reg.crtc_reg[head]; + struct drm_display_mode *crtc_mode = &encoder->crtc->mode; + struct drm_display_mode *output_mode = + &get_tv_norm(encoder)->ctv_enc_mode.mode; + int overscan, hmargin, vmargin, hratio, vratio; + + /* The rescaler doesn't do the right thing for interlaced modes. */ + if (output_mode->flags & DRM_MODE_FLAG_INTERLACE) + overscan = 100; + else + overscan = tv_enc->overscan; + + hmargin = (output_mode->hdisplay - crtc_mode->hdisplay) / 2; + vmargin = (output_mode->vdisplay - crtc_mode->vdisplay) / 2; + + hmargin = interpolate(0, min(hmargin, output_mode->hdisplay/20), + hmargin, overscan); + vmargin = interpolate(0, min(vmargin, output_mode->vdisplay/20), + vmargin, overscan); + + hratio = crtc_mode->hdisplay * 0x800 / + (output_mode->hdisplay - 2*hmargin); + vratio = crtc_mode->vdisplay * 0x800 / + (output_mode->vdisplay - 2*vmargin) & ~3; + + regs->fp_horiz_regs[FP_VALID_START] = hmargin; + regs->fp_horiz_regs[FP_VALID_END] = output_mode->hdisplay - hmargin - 1; + regs->fp_vert_regs[FP_VALID_START] = vmargin; + regs->fp_vert_regs[FP_VALID_END] = output_mode->vdisplay - vmargin - 1; + + regs->fp_debug_1 = NV_PRAMDAC_FP_DEBUG_1_YSCALE_TESTMODE_ENABLE | + XLATE(vratio, 0, NV_PRAMDAC_FP_DEBUG_1_YSCALE_VALUE) | + NV_PRAMDAC_FP_DEBUG_1_XSCALE_TESTMODE_ENABLE | + XLATE(hratio, 0, NV_PRAMDAC_FP_DEBUG_1_XSCALE_VALUE); + + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HVALID_START, + regs->fp_horiz_regs[FP_VALID_START]); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HVALID_END, + regs->fp_horiz_regs[FP_VALID_END]); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_VVALID_START, + regs->fp_vert_regs[FP_VALID_START]); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_VVALID_END, + regs->fp_vert_regs[FP_VALID_END]); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_1, regs->fp_debug_1); +} diff --git a/kernel/drivers/gpu/drm/nouveau/dispnv04/tvnv04.c b/kernel/drivers/gpu/drm/nouveau/dispnv04/tvnv04.c new file mode 100644 index 000000000..70e95cf6f --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/dispnv04/tvnv04.c @@ -0,0 +1,249 @@ +/* + * 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 +#include "nouveau_drm.h" +#include "nouveau_reg.h" +#include "nouveau_encoder.h" +#include "nouveau_connector.h" +#include "nouveau_crtc.h" +#include "hw.h" +#include + +#include + +static struct nvkm_i2c_board_info nv04_tv_encoder_info[] = { + { + { + I2C_BOARD_INFO("ch7006", 0x75), + .platform_data = &(struct ch7006_encoder_params) { + CH7006_FORMAT_RGB24m12I, CH7006_CLOCK_MASTER, + 0, 0, 0, + CH7006_SYNC_SLAVE, CH7006_SYNC_SEPARATED, + CH7006_POUT_3_3V, CH7006_ACTIVE_HSYNC + } + }, + 0 + }, + { } +}; + +int nv04_tv_identify(struct drm_device *dev, int i2c_index) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_i2c *i2c = nvxx_i2c(&drm->device); + + return i2c->identify(i2c, i2c_index, "TV encoder", + nv04_tv_encoder_info, NULL, NULL); +} + + +#define PLLSEL_TV_CRTC1_MASK \ + (NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1 \ + | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1) +#define PLLSEL_TV_CRTC2_MASK \ + (NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK2 \ + | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK2) + +static void nv04_tv_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nv04_mode_state *state = &nv04_display(dev)->mode_reg; + uint8_t crtc1A; + + NV_DEBUG(drm, "Setting dpms mode %d on TV encoder (output %d)\n", + mode, nv_encoder->dcb->index); + + state->pllsel &= ~(PLLSEL_TV_CRTC1_MASK | PLLSEL_TV_CRTC2_MASK); + + if (mode == DRM_MODE_DPMS_ON) { + int head = nouveau_crtc(encoder->crtc)->index; + crtc1A = NVReadVgaCrtc(dev, head, NV_CIO_CRE_RPC1_INDEX); + + state->pllsel |= head ? PLLSEL_TV_CRTC2_MASK : + PLLSEL_TV_CRTC1_MASK; + + /* Inhibit hsync */ + crtc1A |= 0x80; + + NVWriteVgaCrtc(dev, head, NV_CIO_CRE_RPC1_INDEX, crtc1A); + } + + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_PLL_COEFF_SELECT, state->pllsel); + + get_slave_funcs(encoder)->dpms(encoder, mode); +} + +static void nv04_tv_bind(struct drm_device *dev, int head, bool bind) +{ + struct nv04_crtc_reg *state = &nv04_display(dev)->mode_reg.crtc_reg[head]; + + state->tv_setup = 0; + + if (bind) + state->CRTC[NV_CIO_CRE_49] |= 0x10; + else + state->CRTC[NV_CIO_CRE_49] &= ~0x10; + + NVWriteVgaCrtc(dev, head, NV_CIO_CRE_LCD__INDEX, + state->CRTC[NV_CIO_CRE_LCD__INDEX]); + NVWriteVgaCrtc(dev, head, NV_CIO_CRE_49, + state->CRTC[NV_CIO_CRE_49]); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_SETUP, + state->tv_setup); +} + +static void nv04_tv_prepare(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + int head = nouveau_crtc(encoder->crtc)->index; + const struct drm_encoder_helper_funcs *helper = encoder->helper_private; + + helper->dpms(encoder, DRM_MODE_DPMS_OFF); + + nv04_dfp_disable(dev, head); + + if (nv_two_heads(dev)) + nv04_tv_bind(dev, head ^ 1, false); + + nv04_tv_bind(dev, head, true); +} + +static void nv04_tv_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); + struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index]; + + regp->tv_htotal = adjusted_mode->htotal; + regp->tv_vtotal = adjusted_mode->vtotal; + + /* These delay the TV signals with respect to the VGA port, + * they might be useful if we ever allow a CRTC to drive + * multiple outputs. + */ + regp->tv_hskew = 1; + regp->tv_hsync_delay = 1; + regp->tv_hsync_delay2 = 64; + regp->tv_vskew = 1; + regp->tv_vsync_delay = 1; + + get_slave_funcs(encoder)->mode_set(encoder, mode, adjusted_mode); +} + +static void nv04_tv_commit(struct drm_encoder *encoder) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct drm_device *dev = encoder->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); + const struct drm_encoder_helper_funcs *helper = encoder->helper_private; + + helper->dpms(encoder, DRM_MODE_DPMS_ON); + + NV_DEBUG(drm, "Output %s is running on CRTC %d using output %c\n", + nouveau_encoder_connector_get(nv_encoder)->base.name, + nv_crtc->index, '@' + ffs(nv_encoder->dcb->or)); +} + +static void nv04_tv_destroy(struct drm_encoder *encoder) +{ + get_slave_funcs(encoder)->destroy(encoder); + drm_encoder_cleanup(encoder); + + kfree(encoder->helper_private); + kfree(nouveau_encoder(encoder)); +} + +static const struct drm_encoder_funcs nv04_tv_funcs = { + .destroy = nv04_tv_destroy, +}; + +static const struct drm_encoder_helper_funcs nv04_tv_helper_funcs = { + .dpms = nv04_tv_dpms, + .save = drm_i2c_encoder_save, + .restore = drm_i2c_encoder_restore, + .mode_fixup = drm_i2c_encoder_mode_fixup, + .prepare = nv04_tv_prepare, + .commit = nv04_tv_commit, + .mode_set = nv04_tv_mode_set, + .detect = drm_i2c_encoder_detect, +}; + +int +nv04_tv_create(struct drm_connector *connector, struct dcb_output *entry) +{ + struct nouveau_encoder *nv_encoder; + struct drm_encoder *encoder; + struct drm_device *dev = connector->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_i2c *i2c = nvxx_i2c(&drm->device); + struct nvkm_i2c_port *port = i2c->find(i2c, entry->i2c_index); + int type, ret; + + /* Ensure that we can talk to this encoder */ + type = nv04_tv_identify(dev, entry->i2c_index); + if (type < 0) + return type; + + /* Allocate the necessary memory */ + nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); + if (!nv_encoder) + return -ENOMEM; + + /* Initialize the common members */ + encoder = to_drm_encoder(nv_encoder); + + drm_encoder_init(dev, encoder, &nv04_tv_funcs, DRM_MODE_ENCODER_TVDAC); + drm_encoder_helper_add(encoder, &nv04_tv_helper_funcs); + + encoder->possible_crtcs = entry->heads; + encoder->possible_clones = 0; + nv_encoder->dcb = entry; + nv_encoder->or = ffs(entry->or) - 1; + + /* Run the slave-specific initialization */ + ret = drm_i2c_encoder_init(dev, to_encoder_slave(encoder), + &port->adapter, + &nv04_tv_encoder_info[type].dev); + if (ret < 0) + goto fail_cleanup; + + /* Attach it to the specified connector. */ + get_slave_funcs(encoder)->create_resources(encoder, connector); + drm_mode_connector_attach_encoder(connector, encoder); + + return 0; + +fail_cleanup: + drm_encoder_cleanup(encoder); + kfree(nv_encoder); + return ret; +} diff --git a/kernel/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c b/kernel/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c new file mode 100644 index 000000000..d9720dda8 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c @@ -0,0 +1,837 @@ +/* + * 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 +#include +#include "nouveau_drm.h" +#include "nouveau_reg.h" +#include "nouveau_encoder.h" +#include "nouveau_connector.h" +#include "nouveau_crtc.h" +#include "hw.h" +#include "tvnv17.h" + +MODULE_PARM_DESC(tv_norm, "Default TV norm.\n" + "\t\tSupported: PAL, PAL-M, PAL-N, PAL-Nc, NTSC-M, NTSC-J,\n" + "\t\t\thd480i, hd480p, hd576i, hd576p, hd720p, hd1080i.\n" + "\t\tDefault: PAL\n" + "\t\t*NOTE* Ignored for cards with external TV encoders."); +static char *nouveau_tv_norm; +module_param_named(tv_norm, nouveau_tv_norm, charp, 0400); + +static uint32_t nv42_tv_sample_load(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_gpio *gpio = nvxx_gpio(&drm->device); + uint32_t testval, regoffset = nv04_dac_output_offset(encoder); + uint32_t gpio0, gpio1, fp_htotal, fp_hsync_start, fp_hsync_end, + fp_control, test_ctrl, dacclk, ctv_14, ctv_1c, ctv_6c; + uint32_t sample = 0; + int head; + +#define RGB_TEST_DATA(r, g, b) (r << 0 | g << 10 | b << 20) + testval = RGB_TEST_DATA(0x82, 0xeb, 0x82); + if (drm->vbios.tvdactestval) + testval = drm->vbios.tvdactestval; + + dacclk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset); + head = (dacclk & 0x100) >> 8; + + /* Save the previous state. */ + gpio1 = gpio->get(gpio, 0, DCB_GPIO_TVDAC1, 0xff); + gpio0 = gpio->get(gpio, 0, DCB_GPIO_TVDAC0, 0xff); + fp_htotal = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_HTOTAL); + fp_hsync_start = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_START); + fp_hsync_end = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_END); + fp_control = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL); + test_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset); + ctv_1c = NVReadRAMDAC(dev, head, 0x680c1c); + ctv_14 = NVReadRAMDAC(dev, head, 0x680c14); + ctv_6c = NVReadRAMDAC(dev, head, 0x680c6c); + + /* Prepare the DAC for load detection. */ + gpio->set(gpio, 0, DCB_GPIO_TVDAC1, 0xff, true); + gpio->set(gpio, 0, DCB_GPIO_TVDAC0, 0xff, true); + + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HTOTAL, 1343); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_START, 1047); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_END, 1183); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL, + NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS | + NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12 | + NV_PRAMDAC_FP_TG_CONTROL_READ_PROG | + NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS | + NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS); + + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, 0); + + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, + (dacclk & ~0xff) | 0x22); + msleep(1); + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, + (dacclk & ~0xff) | 0x21); + + NVWriteRAMDAC(dev, head, 0x680c1c, 1 << 20); + NVWriteRAMDAC(dev, head, 0x680c14, 4 << 16); + + /* Sample pin 0x4 (usually S-video luma). */ + NVWriteRAMDAC(dev, head, 0x680c6c, testval >> 10 & 0x3ff); + msleep(20); + sample |= NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset) + & 0x4 << 28; + + /* Sample the remaining pins. */ + NVWriteRAMDAC(dev, head, 0x680c6c, testval & 0x3ff); + msleep(20); + sample |= NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset) + & 0xa << 28; + + /* Restore the previous state. */ + NVWriteRAMDAC(dev, head, 0x680c1c, ctv_1c); + NVWriteRAMDAC(dev, head, 0x680c14, ctv_14); + NVWriteRAMDAC(dev, head, 0x680c6c, ctv_6c); + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, dacclk); + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, test_ctrl); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL, fp_control); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_END, fp_hsync_end); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_START, fp_hsync_start); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HTOTAL, fp_htotal); + gpio->set(gpio, 0, DCB_GPIO_TVDAC1, 0xff, gpio1); + gpio->set(gpio, 0, DCB_GPIO_TVDAC0, 0xff, gpio0); + + return sample; +} + +static bool +get_tv_detect_quirks(struct drm_device *dev, uint32_t *pin_mask) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvif_device *device = &drm->device; + + /* Zotac FX5200 */ + if (nv_device_match(nvxx_object(device), 0x0322, 0x19da, 0x1035) || + nv_device_match(nvxx_object(device), 0x0322, 0x19da, 0x2035)) { + *pin_mask = 0xc; + return false; + } + + /* MSI nForce2 IGP */ + if (nv_device_match(nvxx_object(device), 0x01f0, 0x1462, 0x5710)) { + *pin_mask = 0xc; + return false; + } + + return true; +} + +static enum drm_connector_status +nv17_tv_detect(struct drm_encoder *encoder, struct drm_connector *connector) +{ + struct drm_device *dev = encoder->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct drm_mode_config *conf = &dev->mode_config; + struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder); + struct dcb_output *dcb = tv_enc->base.dcb; + bool reliable = get_tv_detect_quirks(dev, &tv_enc->pin_mask); + + if (nv04_dac_in_use(encoder)) + return connector_status_disconnected; + + if (reliable) { + if (drm->device.info.chipset == 0x42 || + drm->device.info.chipset == 0x43) + tv_enc->pin_mask = + nv42_tv_sample_load(encoder) >> 28 & 0xe; + else + tv_enc->pin_mask = + nv17_dac_sample_load(encoder) >> 28 & 0xe; + } + + switch (tv_enc->pin_mask) { + case 0x2: + case 0x4: + tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Composite; + break; + case 0xc: + tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_SVIDEO; + break; + case 0xe: + if (dcb->tvconf.has_component_output) + tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Component; + else + tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_SCART; + break; + default: + tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Unknown; + break; + } + + drm_object_property_set_value(&connector->base, + conf->tv_subconnector_property, + tv_enc->subconnector); + + if (!reliable) { + return connector_status_unknown; + } else if (tv_enc->subconnector) { + NV_INFO(drm, "Load detected on output %c\n", + '@' + ffs(dcb->or)); + return connector_status_connected; + } else { + return connector_status_disconnected; + } +} + +static int nv17_tv_get_ld_modes(struct drm_encoder *encoder, + struct drm_connector *connector) +{ + struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); + const struct drm_display_mode *tv_mode; + int n = 0; + + for (tv_mode = nv17_tv_modes; tv_mode->hdisplay; tv_mode++) { + struct drm_display_mode *mode; + + mode = drm_mode_duplicate(encoder->dev, tv_mode); + + mode->clock = tv_norm->tv_enc_mode.vrefresh * + mode->htotal / 1000 * + mode->vtotal / 1000; + + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + mode->clock *= 2; + + if (mode->hdisplay == tv_norm->tv_enc_mode.hdisplay && + mode->vdisplay == tv_norm->tv_enc_mode.vdisplay) + mode->type |= DRM_MODE_TYPE_PREFERRED; + + drm_mode_probed_add(connector, mode); + n++; + } + + return n; +} + +static int nv17_tv_get_hd_modes(struct drm_encoder *encoder, + struct drm_connector *connector) +{ + struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); + struct drm_display_mode *output_mode = &tv_norm->ctv_enc_mode.mode; + struct drm_display_mode *mode; + const struct { + int hdisplay; + int vdisplay; + } modes[] = { + { 640, 400 }, + { 640, 480 }, + { 720, 480 }, + { 720, 576 }, + { 800, 600 }, + { 1024, 768 }, + { 1280, 720 }, + { 1280, 1024 }, + { 1920, 1080 } + }; + int i, n = 0; + + for (i = 0; i < ARRAY_SIZE(modes); i++) { + if (modes[i].hdisplay > output_mode->hdisplay || + modes[i].vdisplay > output_mode->vdisplay) + continue; + + if (modes[i].hdisplay == output_mode->hdisplay && + modes[i].vdisplay == output_mode->vdisplay) { + mode = drm_mode_duplicate(encoder->dev, output_mode); + mode->type |= DRM_MODE_TYPE_PREFERRED; + + } else { + mode = drm_cvt_mode(encoder->dev, modes[i].hdisplay, + modes[i].vdisplay, 60, false, + (output_mode->flags & + DRM_MODE_FLAG_INTERLACE), false); + } + + /* CVT modes are sometimes unsuitable... */ + if (output_mode->hdisplay <= 720 + || output_mode->hdisplay >= 1920) { + mode->htotal = output_mode->htotal; + mode->hsync_start = (mode->hdisplay + (mode->htotal + - mode->hdisplay) * 9 / 10) & ~7; + mode->hsync_end = mode->hsync_start + 8; + } + + if (output_mode->vdisplay >= 1024) { + mode->vtotal = output_mode->vtotal; + mode->vsync_start = output_mode->vsync_start; + mode->vsync_end = output_mode->vsync_end; + } + + mode->type |= DRM_MODE_TYPE_DRIVER; + drm_mode_probed_add(connector, mode); + n++; + } + + return n; +} + +static int nv17_tv_get_modes(struct drm_encoder *encoder, + struct drm_connector *connector) +{ + struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); + + if (tv_norm->kind == CTV_ENC_MODE) + return nv17_tv_get_hd_modes(encoder, connector); + else + return nv17_tv_get_ld_modes(encoder, connector); +} + +static int nv17_tv_mode_valid(struct drm_encoder *encoder, + struct drm_display_mode *mode) +{ + struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); + + if (tv_norm->kind == CTV_ENC_MODE) { + struct drm_display_mode *output_mode = + &tv_norm->ctv_enc_mode.mode; + + if (mode->clock > 400000) + return MODE_CLOCK_HIGH; + + if (mode->hdisplay > output_mode->hdisplay || + mode->vdisplay > output_mode->vdisplay) + return MODE_BAD; + + if ((mode->flags & DRM_MODE_FLAG_INTERLACE) != + (output_mode->flags & DRM_MODE_FLAG_INTERLACE)) + return MODE_NO_INTERLACE; + + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + return MODE_NO_DBLESCAN; + + } else { + const int vsync_tolerance = 600; + + if (mode->clock > 70000) + return MODE_CLOCK_HIGH; + + if (abs(drm_mode_vrefresh(mode) * 1000 - + tv_norm->tv_enc_mode.vrefresh) > vsync_tolerance) + return MODE_VSYNC; + + /* The encoder takes care of the actual interlacing */ + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + return MODE_NO_INTERLACE; + } + + return MODE_OK; +} + +static bool nv17_tv_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); + + if (nv04_dac_in_use(encoder)) + return false; + + if (tv_norm->kind == CTV_ENC_MODE) + adjusted_mode->clock = tv_norm->ctv_enc_mode.mode.clock; + else + adjusted_mode->clock = 90000; + + return true; +} + +static void nv17_tv_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_gpio *gpio = nvxx_gpio(&drm->device); + struct nv17_tv_state *regs = &to_tv_enc(encoder)->state; + struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); + + if (nouveau_encoder(encoder)->last_dpms == mode) + return; + nouveau_encoder(encoder)->last_dpms = mode; + + NV_INFO(drm, "Setting dpms mode %d on TV encoder (output %d)\n", + mode, nouveau_encoder(encoder)->dcb->index); + + regs->ptv_200 &= ~1; + + if (tv_norm->kind == CTV_ENC_MODE) { + nv04_dfp_update_fp_control(encoder, mode); + + } else { + nv04_dfp_update_fp_control(encoder, DRM_MODE_DPMS_OFF); + + if (mode == DRM_MODE_DPMS_ON) + regs->ptv_200 |= 1; + } + + nv_load_ptv(dev, regs, 200); + + gpio->set(gpio, 0, DCB_GPIO_TVDAC1, 0xff, mode == DRM_MODE_DPMS_ON); + gpio->set(gpio, 0, DCB_GPIO_TVDAC0, 0xff, mode == DRM_MODE_DPMS_ON); + + nv04_dac_update_dacclk(encoder, mode == DRM_MODE_DPMS_ON); +} + +static void nv17_tv_prepare(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + const struct drm_encoder_helper_funcs *helper = encoder->helper_private; + struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); + int head = nouveau_crtc(encoder->crtc)->index; + uint8_t *cr_lcd = &nv04_display(dev)->mode_reg.crtc_reg[head].CRTC[ + NV_CIO_CRE_LCD__INDEX]; + uint32_t dacclk_off = NV_PRAMDAC_DACCLK + + nv04_dac_output_offset(encoder); + uint32_t dacclk; + + helper->dpms(encoder, DRM_MODE_DPMS_OFF); + + nv04_dfp_disable(dev, head); + + /* Unbind any FP encoders from this head if we need the FP + * stuff enabled. */ + if (tv_norm->kind == CTV_ENC_MODE) { + struct drm_encoder *enc; + + list_for_each_entry(enc, &dev->mode_config.encoder_list, head) { + struct dcb_output *dcb = nouveau_encoder(enc)->dcb; + + if ((dcb->type == DCB_OUTPUT_TMDS || + dcb->type == DCB_OUTPUT_LVDS) && + !enc->crtc && + nv04_dfp_get_bound_head(dev, dcb) == head) { + nv04_dfp_bind_head(dev, dcb, head ^ 1, + drm->vbios.fp.dual_link); + } + } + + } + + if (tv_norm->kind == CTV_ENC_MODE) + *cr_lcd |= 0x1 | (head ? 0x0 : 0x8); + + /* Set the DACCLK register */ + dacclk = (NVReadRAMDAC(dev, 0, dacclk_off) & ~0x30) | 0x1; + + if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE) + dacclk |= 0x1a << 16; + + if (tv_norm->kind == CTV_ENC_MODE) { + dacclk |= 0x20; + + if (head) + dacclk |= 0x100; + else + dacclk &= ~0x100; + + } else { + dacclk |= 0x10; + + } + + NVWriteRAMDAC(dev, 0, dacclk_off, dacclk); +} + +static void nv17_tv_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *drm_mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + int head = nouveau_crtc(encoder->crtc)->index; + struct nv04_crtc_reg *regs = &nv04_display(dev)->mode_reg.crtc_reg[head]; + struct nv17_tv_state *tv_regs = &to_tv_enc(encoder)->state; + struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); + int i; + + regs->CRTC[NV_CIO_CRE_53] = 0x40; /* FP_HTIMING */ + regs->CRTC[NV_CIO_CRE_54] = 0; /* FP_VTIMING */ + regs->ramdac_630 = 0x2; /* turn off green mode (tv test pattern?) */ + regs->tv_setup = 1; + regs->ramdac_8c0 = 0x0; + + if (tv_norm->kind == TV_ENC_MODE) { + tv_regs->ptv_200 = 0x13111100; + if (head) + tv_regs->ptv_200 |= 0x10; + + tv_regs->ptv_20c = 0x808010; + tv_regs->ptv_304 = 0x2d00000; + tv_regs->ptv_600 = 0x0; + tv_regs->ptv_60c = 0x0; + tv_regs->ptv_610 = 0x1e00000; + + if (tv_norm->tv_enc_mode.vdisplay == 576) { + tv_regs->ptv_508 = 0x1200000; + tv_regs->ptv_614 = 0x33; + + } else if (tv_norm->tv_enc_mode.vdisplay == 480) { + tv_regs->ptv_508 = 0xf00000; + tv_regs->ptv_614 = 0x13; + } + + if (drm->device.info.family >= NV_DEVICE_INFO_V0_RANKINE) { + tv_regs->ptv_500 = 0xe8e0; + tv_regs->ptv_504 = 0x1710; + tv_regs->ptv_604 = 0x0; + tv_regs->ptv_608 = 0x0; + } else { + if (tv_norm->tv_enc_mode.vdisplay == 576) { + tv_regs->ptv_604 = 0x20; + tv_regs->ptv_608 = 0x10; + tv_regs->ptv_500 = 0x19710; + tv_regs->ptv_504 = 0x68f0; + + } else if (tv_norm->tv_enc_mode.vdisplay == 480) { + tv_regs->ptv_604 = 0x10; + tv_regs->ptv_608 = 0x20; + tv_regs->ptv_500 = 0x4b90; + tv_regs->ptv_504 = 0x1b480; + } + } + + for (i = 0; i < 0x40; i++) + tv_regs->tv_enc[i] = tv_norm->tv_enc_mode.tv_enc[i]; + + } else { + struct drm_display_mode *output_mode = + &tv_norm->ctv_enc_mode.mode; + + /* The registers in PRAMDAC+0xc00 control some timings and CSC + * parameters for the CTV encoder (It's only used for "HD" TV + * modes, I don't think I have enough working to guess what + * they exactly mean...), it's probably connected at the + * output of the FP encoder, but it also needs the analog + * encoder in its OR enabled and routed to the head it's + * using. It's enabled with the DACCLK register, bits [5:4]. + */ + for (i = 0; i < 38; i++) + regs->ctv_regs[i] = tv_norm->ctv_enc_mode.ctv_regs[i]; + + regs->fp_horiz_regs[FP_DISPLAY_END] = output_mode->hdisplay - 1; + regs->fp_horiz_regs[FP_TOTAL] = output_mode->htotal - 1; + regs->fp_horiz_regs[FP_SYNC_START] = + output_mode->hsync_start - 1; + regs->fp_horiz_regs[FP_SYNC_END] = output_mode->hsync_end - 1; + regs->fp_horiz_regs[FP_CRTC] = output_mode->hdisplay + + max((output_mode->hdisplay-600)/40 - 1, 1); + + regs->fp_vert_regs[FP_DISPLAY_END] = output_mode->vdisplay - 1; + regs->fp_vert_regs[FP_TOTAL] = output_mode->vtotal - 1; + regs->fp_vert_regs[FP_SYNC_START] = + output_mode->vsync_start - 1; + regs->fp_vert_regs[FP_SYNC_END] = output_mode->vsync_end - 1; + regs->fp_vert_regs[FP_CRTC] = output_mode->vdisplay - 1; + + regs->fp_control = NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS | + NV_PRAMDAC_FP_TG_CONTROL_READ_PROG | + NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12; + + if (output_mode->flags & DRM_MODE_FLAG_PVSYNC) + regs->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS; + if (output_mode->flags & DRM_MODE_FLAG_PHSYNC) + regs->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS; + + regs->fp_debug_0 = NV_PRAMDAC_FP_DEBUG_0_YWEIGHT_ROUND | + NV_PRAMDAC_FP_DEBUG_0_XWEIGHT_ROUND | + NV_PRAMDAC_FP_DEBUG_0_YINTERP_BILINEAR | + NV_PRAMDAC_FP_DEBUG_0_XINTERP_BILINEAR | + NV_RAMDAC_FP_DEBUG_0_TMDS_ENABLED | + NV_PRAMDAC_FP_DEBUG_0_YSCALE_ENABLE | + NV_PRAMDAC_FP_DEBUG_0_XSCALE_ENABLE; + + regs->fp_debug_2 = 0; + + regs->fp_margin_color = 0x801080; + + } +} + +static void nv17_tv_commit(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + const struct drm_encoder_helper_funcs *helper = encoder->helper_private; + + if (get_tv_norm(encoder)->kind == TV_ENC_MODE) { + nv17_tv_update_rescaler(encoder); + nv17_tv_update_properties(encoder); + } else { + nv17_ctv_update_rescaler(encoder); + } + + nv17_tv_state_load(dev, &to_tv_enc(encoder)->state); + + /* This could use refinement for flatpanels, but it should work */ + if (drm->device.info.chipset < 0x44) + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + + nv04_dac_output_offset(encoder), + 0xf0000000); + else + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + + nv04_dac_output_offset(encoder), + 0x00100000); + + helper->dpms(encoder, DRM_MODE_DPMS_ON); + + NV_INFO(drm, "Output %s is running on CRTC %d using output %c\n", + nouveau_encoder_connector_get(nv_encoder)->base.name, + nv_crtc->index, '@' + ffs(nv_encoder->dcb->or)); +} + +static void nv17_tv_save(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder); + + nouveau_encoder(encoder)->restore.output = + NVReadRAMDAC(dev, 0, + NV_PRAMDAC_DACCLK + + nv04_dac_output_offset(encoder)); + + nv17_tv_state_save(dev, &tv_enc->saved_state); + + tv_enc->state.ptv_200 = tv_enc->saved_state.ptv_200; +} + +static void nv17_tv_restore(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + + nv04_dac_output_offset(encoder), + nouveau_encoder(encoder)->restore.output); + + nv17_tv_state_load(dev, &to_tv_enc(encoder)->saved_state); + + nouveau_encoder(encoder)->last_dpms = NV_DPMS_CLEARED; +} + +static int nv17_tv_create_resources(struct drm_encoder *encoder, + struct drm_connector *connector) +{ + struct drm_device *dev = encoder->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct drm_mode_config *conf = &dev->mode_config; + struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder); + struct dcb_output *dcb = nouveau_encoder(encoder)->dcb; + int num_tv_norms = dcb->tvconf.has_component_output ? NUM_TV_NORMS : + NUM_LD_TV_NORMS; + int i; + + if (nouveau_tv_norm) { + for (i = 0; i < num_tv_norms; i++) { + if (!strcmp(nv17_tv_norm_names[i], nouveau_tv_norm)) { + tv_enc->tv_norm = i; + break; + } + } + + if (i == num_tv_norms) + NV_WARN(drm, "Invalid TV norm setting \"%s\"\n", + nouveau_tv_norm); + } + + drm_mode_create_tv_properties(dev, num_tv_norms, nv17_tv_norm_names); + + drm_object_attach_property(&connector->base, + conf->tv_select_subconnector_property, + tv_enc->select_subconnector); + drm_object_attach_property(&connector->base, + conf->tv_subconnector_property, + tv_enc->subconnector); + drm_object_attach_property(&connector->base, + conf->tv_mode_property, + tv_enc->tv_norm); + drm_object_attach_property(&connector->base, + conf->tv_flicker_reduction_property, + tv_enc->flicker); + drm_object_attach_property(&connector->base, + conf->tv_saturation_property, + tv_enc->saturation); + drm_object_attach_property(&connector->base, + conf->tv_hue_property, + tv_enc->hue); + drm_object_attach_property(&connector->base, + conf->tv_overscan_property, + tv_enc->overscan); + + return 0; +} + +static int nv17_tv_set_property(struct drm_encoder *encoder, + struct drm_connector *connector, + struct drm_property *property, + uint64_t val) +{ + struct drm_mode_config *conf = &encoder->dev->mode_config; + struct drm_crtc *crtc = encoder->crtc; + struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder); + struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); + bool modes_changed = false; + + if (property == conf->tv_overscan_property) { + tv_enc->overscan = val; + if (encoder->crtc) { + if (tv_norm->kind == CTV_ENC_MODE) + nv17_ctv_update_rescaler(encoder); + else + nv17_tv_update_rescaler(encoder); + } + + } else if (property == conf->tv_saturation_property) { + if (tv_norm->kind != TV_ENC_MODE) + return -EINVAL; + + tv_enc->saturation = val; + nv17_tv_update_properties(encoder); + + } else if (property == conf->tv_hue_property) { + if (tv_norm->kind != TV_ENC_MODE) + return -EINVAL; + + tv_enc->hue = val; + nv17_tv_update_properties(encoder); + + } else if (property == conf->tv_flicker_reduction_property) { + if (tv_norm->kind != TV_ENC_MODE) + return -EINVAL; + + tv_enc->flicker = val; + if (encoder->crtc) + nv17_tv_update_rescaler(encoder); + + } else if (property == conf->tv_mode_property) { + if (connector->dpms != DRM_MODE_DPMS_OFF) + return -EINVAL; + + tv_enc->tv_norm = val; + + modes_changed = true; + + } else if (property == conf->tv_select_subconnector_property) { + if (tv_norm->kind != TV_ENC_MODE) + return -EINVAL; + + tv_enc->select_subconnector = val; + nv17_tv_update_properties(encoder); + + } else { + return -EINVAL; + } + + if (modes_changed) { + drm_helper_probe_single_connector_modes(connector, 0, 0); + + /* Disable the crtc to ensure a full modeset is + * performed whenever it's turned on again. */ + if (crtc) { + struct drm_mode_set modeset = { + .crtc = crtc, + }; + + drm_mode_set_config_internal(&modeset); + } + } + + return 0; +} + +static void nv17_tv_destroy(struct drm_encoder *encoder) +{ + struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder); + + drm_encoder_cleanup(encoder); + kfree(tv_enc); +} + +static struct drm_encoder_helper_funcs nv17_tv_helper_funcs = { + .dpms = nv17_tv_dpms, + .save = nv17_tv_save, + .restore = nv17_tv_restore, + .mode_fixup = nv17_tv_mode_fixup, + .prepare = nv17_tv_prepare, + .commit = nv17_tv_commit, + .mode_set = nv17_tv_mode_set, + .detect = nv17_tv_detect, +}; + +static struct drm_encoder_slave_funcs nv17_tv_slave_funcs = { + .get_modes = nv17_tv_get_modes, + .mode_valid = nv17_tv_mode_valid, + .create_resources = nv17_tv_create_resources, + .set_property = nv17_tv_set_property, +}; + +static struct drm_encoder_funcs nv17_tv_funcs = { + .destroy = nv17_tv_destroy, +}; + +int +nv17_tv_create(struct drm_connector *connector, struct dcb_output *entry) +{ + struct drm_device *dev = connector->dev; + struct drm_encoder *encoder; + struct nv17_tv_encoder *tv_enc = NULL; + + tv_enc = kzalloc(sizeof(*tv_enc), GFP_KERNEL); + if (!tv_enc) + return -ENOMEM; + + tv_enc->overscan = 50; + tv_enc->flicker = 50; + tv_enc->saturation = 50; + tv_enc->hue = 0; + tv_enc->tv_norm = TV_NORM_PAL; + tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Unknown; + tv_enc->select_subconnector = DRM_MODE_SUBCONNECTOR_Automatic; + tv_enc->pin_mask = 0; + + encoder = to_drm_encoder(&tv_enc->base); + + tv_enc->base.dcb = entry; + tv_enc->base.or = ffs(entry->or) - 1; + + drm_encoder_init(dev, encoder, &nv17_tv_funcs, DRM_MODE_ENCODER_TVDAC); + drm_encoder_helper_add(encoder, &nv17_tv_helper_funcs); + to_encoder_slave(encoder)->slave_funcs = &nv17_tv_slave_funcs; + + encoder->possible_crtcs = entry->heads; + encoder->possible_clones = 0; + + nv17_tv_create_resources(encoder, connector); + drm_mode_connector_attach_encoder(connector, encoder); + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/dispnv04/tvnv17.h b/kernel/drivers/gpu/drm/nouveau/dispnv04/tvnv17.h new file mode 100644 index 000000000..225894cdc --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/dispnv04/tvnv17.h @@ -0,0 +1,163 @@ +/* + * 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. + * + */ + +#ifndef __NV17_TV_H__ +#define __NV17_TV_H__ + +struct nv17_tv_state { + uint8_t tv_enc[0x40]; + + uint32_t hfilter[4][7]; + uint32_t hfilter2[4][7]; + uint32_t vfilter[4][7]; + + uint32_t ptv_200; + uint32_t ptv_204; + uint32_t ptv_208; + uint32_t ptv_20c; + uint32_t ptv_304; + uint32_t ptv_500; + uint32_t ptv_504; + uint32_t ptv_508; + uint32_t ptv_600; + uint32_t ptv_604; + uint32_t ptv_608; + uint32_t ptv_60c; + uint32_t ptv_610; + uint32_t ptv_614; +}; + +enum nv17_tv_norm{ + TV_NORM_PAL, + TV_NORM_PAL_M, + TV_NORM_PAL_N, + TV_NORM_PAL_NC, + TV_NORM_NTSC_M, + TV_NORM_NTSC_J, + NUM_LD_TV_NORMS, + TV_NORM_HD480I = NUM_LD_TV_NORMS, + TV_NORM_HD480P, + TV_NORM_HD576I, + TV_NORM_HD576P, + TV_NORM_HD720P, + TV_NORM_HD1080I, + NUM_TV_NORMS +}; + +struct nv17_tv_encoder { + struct nouveau_encoder base; + + struct nv17_tv_state state; + struct nv17_tv_state saved_state; + + int overscan; + int flicker; + int saturation; + int hue; + enum nv17_tv_norm tv_norm; + int subconnector; + int select_subconnector; + uint32_t pin_mask; +}; +#define to_tv_enc(x) container_of(nouveau_encoder(x), \ + struct nv17_tv_encoder, base) + +extern char *nv17_tv_norm_names[NUM_TV_NORMS]; + +extern struct nv17_tv_norm_params { + enum { + TV_ENC_MODE, + CTV_ENC_MODE, + } kind; + + union { + struct { + int hdisplay; + int vdisplay; + int vrefresh; /* mHz */ + + uint8_t tv_enc[0x40]; + } tv_enc_mode; + + struct { + struct drm_display_mode mode; + + uint32_t ctv_regs[38]; + } ctv_enc_mode; + }; + +} nv17_tv_norms[NUM_TV_NORMS]; +#define get_tv_norm(enc) (&nv17_tv_norms[to_tv_enc(enc)->tv_norm]) + +extern const struct drm_display_mode nv17_tv_modes[]; + +static inline int interpolate(int y0, int y1, int y2, int x) +{ + return y1 + (x < 50 ? y1 - y0 : y2 - y1) * (x - 50) / 50; +} + +void nv17_tv_state_save(struct drm_device *dev, struct nv17_tv_state *state); +void nv17_tv_state_load(struct drm_device *dev, struct nv17_tv_state *state); +void nv17_tv_update_properties(struct drm_encoder *encoder); +void nv17_tv_update_rescaler(struct drm_encoder *encoder); +void nv17_ctv_update_rescaler(struct drm_encoder *encoder); + +/* TV hardware access functions */ + +static inline void nv_write_ptv(struct drm_device *dev, uint32_t reg, + uint32_t val) +{ + struct nvif_device *device = &nouveau_drm(dev)->device; + nvif_wr32(device, reg, val); +} + +static inline uint32_t nv_read_ptv(struct drm_device *dev, uint32_t reg) +{ + struct nvif_device *device = &nouveau_drm(dev)->device; + return nvif_rd32(device, reg); +} + +static inline void nv_write_tv_enc(struct drm_device *dev, uint8_t reg, + uint8_t val) +{ + nv_write_ptv(dev, NV_PTV_TV_INDEX, reg); + nv_write_ptv(dev, NV_PTV_TV_DATA, val); +} + +static inline uint8_t nv_read_tv_enc(struct drm_device *dev, uint8_t reg) +{ + nv_write_ptv(dev, NV_PTV_TV_INDEX, reg); + return nv_read_ptv(dev, NV_PTV_TV_DATA); +} + +#define nv_load_ptv(dev, state, reg) \ + nv_write_ptv(dev, NV_PTV_OFFSET + 0x##reg, state->ptv_##reg) +#define nv_save_ptv(dev, state, reg) \ + state->ptv_##reg = nv_read_ptv(dev, NV_PTV_OFFSET + 0x##reg) +#define nv_load_tv_enc(dev, state, reg) \ + nv_write_tv_enc(dev, 0x##reg, state->tv_enc[0x##reg]) + +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvif/class.h b/kernel/drivers/gpu/drm/nouveau/include/nvif/class.h new file mode 100644 index 000000000..64f8b2f68 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvif/class.h @@ -0,0 +1,583 @@ +#ifndef __NVIF_CLASS_H__ +#define __NVIF_CLASS_H__ + +/******************************************************************************* + * class identifiers + ******************************************************************************/ + +/* the below match nvidia-assigned (either in hw, or sw) class numbers */ +#define NV_DEVICE 0x00000080 + +#define NV_DMA_FROM_MEMORY 0x00000002 +#define NV_DMA_TO_MEMORY 0x00000003 +#define NV_DMA_IN_MEMORY 0x0000003d + +#define FERMI_TWOD_A 0x0000902d + +#define FERMI_MEMORY_TO_MEMORY_FORMAT_A 0x00009039 + +#define KEPLER_INLINE_TO_MEMORY_A 0x0000a040 +#define KEPLER_INLINE_TO_MEMORY_B 0x0000a140 + +#define NV04_DISP 0x00000046 + +#define NV03_CHANNEL_DMA 0x0000006b +#define NV10_CHANNEL_DMA 0x0000006e +#define NV17_CHANNEL_DMA 0x0000176e +#define NV40_CHANNEL_DMA 0x0000406e +#define NV50_CHANNEL_DMA 0x0000506e +#define G82_CHANNEL_DMA 0x0000826e + +#define NV50_CHANNEL_GPFIFO 0x0000506f +#define G82_CHANNEL_GPFIFO 0x0000826f +#define FERMI_CHANNEL_GPFIFO 0x0000906f +#define KEPLER_CHANNEL_GPFIFO_A 0x0000a06f +#define MAXWELL_CHANNEL_GPFIFO_A 0x0000b06f + +#define NV50_DISP 0x00005070 +#define G82_DISP 0x00008270 +#define GT200_DISP 0x00008370 +#define GT214_DISP 0x00008570 +#define GT206_DISP 0x00008870 +#define GF110_DISP 0x00009070 +#define GK104_DISP 0x00009170 +#define GK110_DISP 0x00009270 +#define GM107_DISP 0x00009470 +#define GM204_DISP 0x00009570 + +#define NV50_DISP_CURSOR 0x0000507a +#define G82_DISP_CURSOR 0x0000827a +#define GT214_DISP_CURSOR 0x0000857a +#define GF110_DISP_CURSOR 0x0000907a +#define GK104_DISP_CURSOR 0x0000917a + +#define NV50_DISP_OVERLAY 0x0000507b +#define G82_DISP_OVERLAY 0x0000827b +#define GT214_DISP_OVERLAY 0x0000857b +#define GF110_DISP_OVERLAY 0x0000907b +#define GK104_DISP_OVERLAY 0x0000917b + +#define NV50_DISP_BASE_CHANNEL_DMA 0x0000507c +#define G82_DISP_BASE_CHANNEL_DMA 0x0000827c +#define GT200_DISP_BASE_CHANNEL_DMA 0x0000837c +#define GT214_DISP_BASE_CHANNEL_DMA 0x0000857c +#define GF110_DISP_BASE_CHANNEL_DMA 0x0000907c +#define GK104_DISP_BASE_CHANNEL_DMA 0x0000917c +#define GK110_DISP_BASE_CHANNEL_DMA 0x0000927c + +#define NV50_DISP_CORE_CHANNEL_DMA 0x0000507d +#define G82_DISP_CORE_CHANNEL_DMA 0x0000827d +#define GT200_DISP_CORE_CHANNEL_DMA 0x0000837d +#define GT214_DISP_CORE_CHANNEL_DMA 0x0000857d +#define GT206_DISP_CORE_CHANNEL_DMA 0x0000887d +#define GF110_DISP_CORE_CHANNEL_DMA 0x0000907d +#define GK104_DISP_CORE_CHANNEL_DMA 0x0000917d +#define GK110_DISP_CORE_CHANNEL_DMA 0x0000927d +#define GM107_DISP_CORE_CHANNEL_DMA 0x0000947d +#define GM204_DISP_CORE_CHANNEL_DMA 0x0000957d + +#define NV50_DISP_OVERLAY_CHANNEL_DMA 0x0000507e +#define G82_DISP_OVERLAY_CHANNEL_DMA 0x0000827e +#define GT200_DISP_OVERLAY_CHANNEL_DMA 0x0000837e +#define GT214_DISP_OVERLAY_CHANNEL_DMA 0x0000857e +#define GF110_DISP_OVERLAY_CONTROL_DMA 0x0000907e +#define GK104_DISP_OVERLAY_CONTROL_DMA 0x0000917e + +#define FERMI_A 0x00009097 +#define FERMI_B 0x00009197 +#define FERMI_C 0x00009297 + +#define KEPLER_A 0x0000a097 +#define KEPLER_B 0x0000a197 +#define KEPLER_C 0x0000a297 + +#define MAXWELL_A 0x0000b097 +#define MAXWELL_B 0x0000b197 + +#define FERMI_COMPUTE_A 0x000090c0 +#define FERMI_COMPUTE_B 0x000091c0 + +#define KEPLER_COMPUTE_A 0x0000a0c0 +#define KEPLER_COMPUTE_B 0x0000a1c0 + +#define MAXWELL_COMPUTE_A 0x0000b0c0 +#define MAXWELL_COMPUTE_B 0x0000b1c0 + + +/******************************************************************************* + * client + ******************************************************************************/ + +#define NV_CLIENT_DEVLIST 0x00 + +struct nv_client_devlist_v0 { + __u8 version; + __u8 count; + __u8 pad02[6]; + __u64 device[]; +}; + + +/******************************************************************************* + * device + ******************************************************************************/ + +struct nv_device_v0 { + __u8 version; + __u8 pad01[7]; + __u64 device; /* device identifier, ~0 for client default */ +#define NV_DEVICE_V0_DISABLE_IDENTIFY 0x0000000000000001ULL +#define NV_DEVICE_V0_DISABLE_MMIO 0x0000000000000002ULL +#define NV_DEVICE_V0_DISABLE_VBIOS 0x0000000000000004ULL +#define NV_DEVICE_V0_DISABLE_CORE 0x0000000000000008ULL +#define NV_DEVICE_V0_DISABLE_DISP 0x0000000000010000ULL +#define NV_DEVICE_V0_DISABLE_FIFO 0x0000000000020000ULL +#define NV_DEVICE_V0_DISABLE_GR 0x0000000100000000ULL +#define NV_DEVICE_V0_DISABLE_MPEG 0x0000000200000000ULL +#define NV_DEVICE_V0_DISABLE_ME 0x0000000400000000ULL +#define NV_DEVICE_V0_DISABLE_VP 0x0000000800000000ULL +#define NV_DEVICE_V0_DISABLE_CIPHER 0x0000001000000000ULL +#define NV_DEVICE_V0_DISABLE_BSP 0x0000002000000000ULL +#define NV_DEVICE_V0_DISABLE_MSPPP 0x0000004000000000ULL +#define NV_DEVICE_V0_DISABLE_CE0 0x0000008000000000ULL +#define NV_DEVICE_V0_DISABLE_CE1 0x0000010000000000ULL +#define NV_DEVICE_V0_DISABLE_VIC 0x0000020000000000ULL +#define NV_DEVICE_V0_DISABLE_MSENC 0x0000040000000000ULL +#define NV_DEVICE_V0_DISABLE_CE2 0x0000080000000000ULL +#define NV_DEVICE_V0_DISABLE_MSVLD 0x0000100000000000ULL +#define NV_DEVICE_V0_DISABLE_SEC 0x0000200000000000ULL +#define NV_DEVICE_V0_DISABLE_MSPDEC 0x0000400000000000ULL + __u64 disable; /* disable particular subsystems */ + __u64 debug0; /* as above, but *internal* ids, and *NOT* ABI */ +}; + +#define NV_DEVICE_V0_INFO 0x00 + +struct nv_device_info_v0 { + __u8 version; +#define NV_DEVICE_INFO_V0_IGP 0x00 +#define NV_DEVICE_INFO_V0_PCI 0x01 +#define NV_DEVICE_INFO_V0_AGP 0x02 +#define NV_DEVICE_INFO_V0_PCIE 0x03 +#define NV_DEVICE_INFO_V0_SOC 0x04 + __u8 platform; + __u16 chipset; /* from NV_PMC_BOOT_0 */ + __u8 revision; /* from NV_PMC_BOOT_0 */ +#define NV_DEVICE_INFO_V0_TNT 0x01 +#define NV_DEVICE_INFO_V0_CELSIUS 0x02 +#define NV_DEVICE_INFO_V0_KELVIN 0x03 +#define NV_DEVICE_INFO_V0_RANKINE 0x04 +#define NV_DEVICE_INFO_V0_CURIE 0x05 +#define NV_DEVICE_INFO_V0_TESLA 0x06 +#define NV_DEVICE_INFO_V0_FERMI 0x07 +#define NV_DEVICE_INFO_V0_KEPLER 0x08 +#define NV_DEVICE_INFO_V0_MAXWELL 0x09 + __u8 family; + __u8 pad06[2]; + __u64 ram_size; + __u64 ram_user; +}; + + +/******************************************************************************* + * context dma + ******************************************************************************/ + +struct nv_dma_v0 { + __u8 version; +#define NV_DMA_V0_TARGET_VM 0x00 +#define NV_DMA_V0_TARGET_VRAM 0x01 +#define NV_DMA_V0_TARGET_PCI 0x02 +#define NV_DMA_V0_TARGET_PCI_US 0x03 +#define NV_DMA_V0_TARGET_AGP 0x04 + __u8 target; +#define NV_DMA_V0_ACCESS_VM 0x00 +#define NV_DMA_V0_ACCESS_RD 0x01 +#define NV_DMA_V0_ACCESS_WR 0x02 +#define NV_DMA_V0_ACCESS_RDWR (NV_DMA_V0_ACCESS_RD | NV_DMA_V0_ACCESS_WR) + __u8 access; + __u8 pad03[5]; + __u64 start; + __u64 limit; + /* ... chipset-specific class data */ +}; + +struct nv50_dma_v0 { + __u8 version; +#define NV50_DMA_V0_PRIV_VM 0x00 +#define NV50_DMA_V0_PRIV_US 0x01 +#define NV50_DMA_V0_PRIV__S 0x02 + __u8 priv; +#define NV50_DMA_V0_PART_VM 0x00 +#define NV50_DMA_V0_PART_256 0x01 +#define NV50_DMA_V0_PART_1KB 0x02 + __u8 part; +#define NV50_DMA_V0_COMP_NONE 0x00 +#define NV50_DMA_V0_COMP_1 0x01 +#define NV50_DMA_V0_COMP_2 0x02 +#define NV50_DMA_V0_COMP_VM 0x03 + __u8 comp; +#define NV50_DMA_V0_KIND_PITCH 0x00 +#define NV50_DMA_V0_KIND_VM 0x7f + __u8 kind; + __u8 pad05[3]; +}; + +struct gf100_dma_v0 { + __u8 version; +#define GF100_DMA_V0_PRIV_VM 0x00 +#define GF100_DMA_V0_PRIV_US 0x01 +#define GF100_DMA_V0_PRIV__S 0x02 + __u8 priv; +#define GF100_DMA_V0_KIND_PITCH 0x00 +#define GF100_DMA_V0_KIND_VM 0xff + __u8 kind; + __u8 pad03[5]; +}; + +struct gf110_dma_v0 { + __u8 version; +#define GF110_DMA_V0_PAGE_LP 0x00 +#define GF110_DMA_V0_PAGE_SP 0x01 + __u8 page; +#define GF110_DMA_V0_KIND_PITCH 0x00 +#define GF110_DMA_V0_KIND_VM 0xff + __u8 kind; + __u8 pad03[5]; +}; + + +/******************************************************************************* + * perfmon + ******************************************************************************/ + +struct nvif_perfctr_v0 { + __u8 version; + __u8 pad01[1]; + __u16 logic_op; + __u8 pad04[4]; + char name[4][64]; +}; + +#define NVIF_PERFCTR_V0_QUERY 0x00 +#define NVIF_PERFCTR_V0_SAMPLE 0x01 +#define NVIF_PERFCTR_V0_READ 0x02 + +struct nvif_perfctr_query_v0 { + __u8 version; + __u8 pad01[3]; + __u32 iter; + char name[64]; +}; + +struct nvif_perfctr_sample { +}; + +struct nvif_perfctr_read_v0 { + __u8 version; + __u8 pad01[7]; + __u32 ctr; + __u32 clk; +}; + + +/******************************************************************************* + * device control + ******************************************************************************/ + +#define NVIF_CONTROL_PSTATE_INFO 0x00 +#define NVIF_CONTROL_PSTATE_ATTR 0x01 +#define NVIF_CONTROL_PSTATE_USER 0x02 + +struct nvif_control_pstate_info_v0 { + __u8 version; + __u8 count; /* out: number of power states */ +#define NVIF_CONTROL_PSTATE_INFO_V0_USTATE_DISABLE (-1) +#define NVIF_CONTROL_PSTATE_INFO_V0_USTATE_PERFMON (-2) + __s8 ustate_ac; /* out: target pstate index */ + __s8 ustate_dc; /* out: target pstate index */ + __s8 pwrsrc; /* out: current power source */ +#define NVIF_CONTROL_PSTATE_INFO_V0_PSTATE_UNKNOWN (-1) +#define NVIF_CONTROL_PSTATE_INFO_V0_PSTATE_PERFMON (-2) + __s8 pstate; /* out: current pstate index */ + __u8 pad06[2]; +}; + +struct nvif_control_pstate_attr_v0 { + __u8 version; +#define NVIF_CONTROL_PSTATE_ATTR_V0_STATE_CURRENT (-1) + __s8 state; /* in: index of pstate to query + * out: pstate identifier + */ + __u8 index; /* in: index of attribute to query + * out: index of next attribute, or 0 if no more + */ + __u8 pad03[5]; + __u32 min; + __u32 max; + char name[32]; + char unit[16]; +}; + +struct nvif_control_pstate_user_v0 { + __u8 version; +#define NVIF_CONTROL_PSTATE_USER_V0_STATE_UNKNOWN (-1) +#define NVIF_CONTROL_PSTATE_USER_V0_STATE_PERFMON (-2) + __s8 ustate; /* in: pstate identifier */ + __s8 pwrsrc; /* in: target power source */ + __u8 pad03[5]; +}; + + +/******************************************************************************* + * DMA FIFO channels + ******************************************************************************/ + +struct nv03_channel_dma_v0 { + __u8 version; + __u8 chid; + __u8 pad02[2]; + __u32 pushbuf; + __u64 offset; +}; + +#define G82_CHANNEL_DMA_V0_NTFY_UEVENT 0x00 + +/******************************************************************************* + * GPFIFO channels + ******************************************************************************/ + +struct nv50_channel_gpfifo_v0 { + __u8 version; + __u8 chid; + __u8 pad01[6]; + __u32 pushbuf; + __u32 ilength; + __u64 ioffset; +}; + +struct kepler_channel_gpfifo_a_v0 { + __u8 version; +#define KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_GR 0x01 +#define KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_MSPDEC 0x02 +#define KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_MSPPP 0x04 +#define KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_MSVLD 0x08 +#define KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_CE0 0x10 +#define KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_CE1 0x20 +#define KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_ENC 0x40 + __u8 engine; + __u16 chid; + __u8 pad04[4]; + __u32 pushbuf; + __u32 ilength; + __u64 ioffset; +}; + +/******************************************************************************* + * legacy display + ******************************************************************************/ + +#define NV04_DISP_NTFY_VBLANK 0x00 +#define NV04_DISP_NTFY_CONN 0x01 + +struct nv04_disp_mthd_v0 { + __u8 version; +#define NV04_DISP_SCANOUTPOS 0x00 + __u8 method; + __u8 head; + __u8 pad03[5]; +}; + +struct nv04_disp_scanoutpos_v0 { + __u8 version; + __u8 pad01[7]; + __s64 time[2]; + __u16 vblanks; + __u16 vblanke; + __u16 vtotal; + __u16 vline; + __u16 hblanks; + __u16 hblanke; + __u16 htotal; + __u16 hline; +}; + +/******************************************************************************* + * display + ******************************************************************************/ + +#define NV50_DISP_MTHD 0x00 + +struct nv50_disp_mthd_v0 { + __u8 version; +#define NV50_DISP_SCANOUTPOS 0x00 + __u8 method; + __u8 head; + __u8 pad03[5]; +}; + +struct nv50_disp_mthd_v1 { + __u8 version; +#define NV50_DISP_MTHD_V1_DAC_PWR 0x10 +#define NV50_DISP_MTHD_V1_DAC_LOAD 0x11 +#define NV50_DISP_MTHD_V1_SOR_PWR 0x20 +#define NV50_DISP_MTHD_V1_SOR_HDA_ELD 0x21 +#define NV50_DISP_MTHD_V1_SOR_HDMI_PWR 0x22 +#define NV50_DISP_MTHD_V1_SOR_LVDS_SCRIPT 0x23 +#define NV50_DISP_MTHD_V1_SOR_DP_PWR 0x24 +#define NV50_DISP_MTHD_V1_PIOR_PWR 0x30 + __u8 method; + __u16 hasht; + __u16 hashm; + __u8 pad06[2]; +}; + +struct nv50_disp_dac_pwr_v0 { + __u8 version; + __u8 state; + __u8 data; + __u8 vsync; + __u8 hsync; + __u8 pad05[3]; +}; + +struct nv50_disp_dac_load_v0 { + __u8 version; + __u8 load; + __u8 pad02[2]; + __u32 data; +}; + +struct nv50_disp_sor_pwr_v0 { + __u8 version; + __u8 state; + __u8 pad02[6]; +}; + +struct nv50_disp_sor_hda_eld_v0 { + __u8 version; + __u8 pad01[7]; + __u8 data[]; +}; + +struct nv50_disp_sor_hdmi_pwr_v0 { + __u8 version; + __u8 state; + __u8 max_ac_packet; + __u8 rekey; + __u8 pad04[4]; +}; + +struct nv50_disp_sor_lvds_script_v0 { + __u8 version; + __u8 pad01[1]; + __u16 script; + __u8 pad04[4]; +}; + +struct nv50_disp_sor_dp_pwr_v0 { + __u8 version; + __u8 state; + __u8 pad02[6]; +}; + +struct nv50_disp_pior_pwr_v0 { + __u8 version; + __u8 state; + __u8 type; + __u8 pad03[5]; +}; + +/* core */ +struct nv50_disp_core_channel_dma_v0 { + __u8 version; + __u8 pad01[3]; + __u32 pushbuf; +}; + +#define NV50_DISP_CORE_CHANNEL_DMA_V0_NTFY_UEVENT 0x00 + +/* cursor immediate */ +struct nv50_disp_cursor_v0 { + __u8 version; + __u8 head; + __u8 pad02[6]; +}; + +#define NV50_DISP_CURSOR_V0_NTFY_UEVENT 0x00 + +/* base */ +struct nv50_disp_base_channel_dma_v0 { + __u8 version; + __u8 pad01[2]; + __u8 head; + __u32 pushbuf; +}; + +#define NV50_DISP_BASE_CHANNEL_DMA_V0_NTFY_UEVENT 0x00 + +/* overlay */ +struct nv50_disp_overlay_channel_dma_v0 { + __u8 version; + __u8 pad01[2]; + __u8 head; + __u32 pushbuf; +}; + +#define NV50_DISP_OVERLAY_CHANNEL_DMA_V0_NTFY_UEVENT 0x00 + +/* overlay immediate */ +struct nv50_disp_overlay_v0 { + __u8 version; + __u8 head; + __u8 pad02[6]; +}; + +#define NV50_DISP_OVERLAY_V0_NTFY_UEVENT 0x00 + +/******************************************************************************* + * fermi + ******************************************************************************/ + +#define FERMI_A_ZBC_COLOR 0x00 +#define FERMI_A_ZBC_DEPTH 0x01 + +struct fermi_a_zbc_color_v0 { + __u8 version; +#define FERMI_A_ZBC_COLOR_V0_FMT_ZERO 0x01 +#define FERMI_A_ZBC_COLOR_V0_FMT_UNORM_ONE 0x02 +#define FERMI_A_ZBC_COLOR_V0_FMT_RF32_GF32_BF32_AF32 0x04 +#define FERMI_A_ZBC_COLOR_V0_FMT_R16_G16_B16_A16 0x08 +#define FERMI_A_ZBC_COLOR_V0_FMT_RN16_GN16_BN16_AN16 0x0c +#define FERMI_A_ZBC_COLOR_V0_FMT_RS16_GS16_BS16_AS16 0x10 +#define FERMI_A_ZBC_COLOR_V0_FMT_RU16_GU16_BU16_AU16 0x14 +#define FERMI_A_ZBC_COLOR_V0_FMT_RF16_GF16_BF16_AF16 0x16 +#define FERMI_A_ZBC_COLOR_V0_FMT_A8R8G8B8 0x18 +#define FERMI_A_ZBC_COLOR_V0_FMT_A8RL8GL8BL8 0x1c +#define FERMI_A_ZBC_COLOR_V0_FMT_A2B10G10R10 0x20 +#define FERMI_A_ZBC_COLOR_V0_FMT_AU2BU10GU10RU10 0x24 +#define FERMI_A_ZBC_COLOR_V0_FMT_A8B8G8R8 0x28 +#define FERMI_A_ZBC_COLOR_V0_FMT_A8BL8GL8RL8 0x2c +#define FERMI_A_ZBC_COLOR_V0_FMT_AN8BN8GN8RN8 0x30 +#define FERMI_A_ZBC_COLOR_V0_FMT_AS8BS8GS8RS8 0x34 +#define FERMI_A_ZBC_COLOR_V0_FMT_AU8BU8GU8RU8 0x38 +#define FERMI_A_ZBC_COLOR_V0_FMT_A2R10G10B10 0x3c +#define FERMI_A_ZBC_COLOR_V0_FMT_BF10GF11RF11 0x40 + __u8 format; + __u8 index; + __u8 pad03[5]; + __u32 ds[4]; + __u32 l2[4]; +}; + +struct fermi_a_zbc_depth_v0 { + __u8 version; +#define FERMI_A_ZBC_DEPTH_V0_FMT_FP32 0x01 + __u8 format; + __u8 index; + __u8 pad03[5]; + __u32 ds; + __u32 l2; +}; + +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvif/client.h b/kernel/drivers/gpu/drm/nouveau/include/nvif/client.h new file mode 100644 index 000000000..eca648ef0 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvif/client.h @@ -0,0 +1,39 @@ +#ifndef __NVIF_CLIENT_H__ +#define __NVIF_CLIENT_H__ + +#include + +struct nvif_client { + struct nvif_object base; + struct nvif_object *object; /*XXX: hack for nvif_object() */ + const struct nvif_driver *driver; + bool super; +}; + +static inline struct nvif_client * +nvif_client(struct nvif_object *object) +{ + while (object && object->parent != object) + object = object->parent; + return (void *)object; +} + +int nvif_client_init(void (*dtor)(struct nvif_client *), const char *, + const char *, u64, const char *, const char *, + struct nvif_client *); +void nvif_client_fini(struct nvif_client *); +int nvif_client_new(const char *, const char *, u64, const char *, + const char *, struct nvif_client **); +void nvif_client_ref(struct nvif_client *, struct nvif_client **); +int nvif_client_ioctl(struct nvif_client *, void *, u32); +int nvif_client_suspend(struct nvif_client *); +int nvif_client_resume(struct nvif_client *); + +/*XXX*/ +#include +#define nvxx_client(a) ({ \ + struct nvif_client *_client = nvif_client(nvif_object(a)); \ + nvkm_client(_client->base.priv); \ +}) + +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvif/device.h b/kernel/drivers/gpu/drm/nouveau/include/nvif/device.h new file mode 100644 index 000000000..88553a741 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvif/device.h @@ -0,0 +1,61 @@ +#ifndef __NVIF_DEVICE_H__ +#define __NVIF_DEVICE_H__ + +#include +#include + +struct nvif_device { + struct nvif_object base; + struct nvif_object *object; /*XXX: hack for nvif_object() */ + struct nv_device_info_v0 info; +}; + +static inline struct nvif_device * +nvif_device(struct nvif_object *object) +{ + while (object && object->oclass != 0x0080 /*XXX: NV_DEVICE_CLASS*/ ) + object = object->parent; + return (void *)object; +} + +int nvif_device_init(struct nvif_object *, void (*dtor)(struct nvif_device *), + u32 handle, u32 oclass, void *, u32, + struct nvif_device *); +void nvif_device_fini(struct nvif_device *); +int nvif_device_new(struct nvif_object *, u32 handle, u32 oclass, + void *, u32, struct nvif_device **); +void nvif_device_ref(struct nvif_device *, struct nvif_device **); + +/*XXX*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define nvxx_device(a) nv_device(nvxx_object((a))) +#define nvxx_bios(a) nvkm_bios(nvxx_device(a)) +#define nvxx_fb(a) nvkm_fb(nvxx_device(a)) +#define nvxx_mmu(a) nvkm_mmu(nvxx_device(a)) +#define nvxx_bar(a) nvkm_bar(nvxx_device(a)) +#define nvxx_gpio(a) nvkm_gpio(nvxx_device(a)) +#define nvxx_clk(a) nvkm_clk(nvxx_device(a)) +#define nvxx_i2c(a) nvkm_i2c(nvxx_device(a)) +#define nvxx_timer(a) nvkm_timer(nvxx_device(a)) +#define nvxx_wait(a,b,c,d) nv_wait(nvxx_timer(a), (b), (c), (d)) +#define nvxx_wait_cb(a,b,c) nv_wait_cb(nvxx_timer(a), (b), (c)) +#define nvxx_therm(a) nvkm_therm(nvxx_device(a)) + +#include +#include +#include +#include + +#define nvxx_fifo(a) nvkm_fifo(nvxx_device(a)) +#define nvxx_fifo_chan(a) ((struct nvkm_fifo_chan *)nvxx_object(a)) +#define nvxx_gr(a) ((struct nvkm_gr *)nvkm_engine(nvxx_object(a), NVDEV_ENGINE_GR)) +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvif/driver.h b/kernel/drivers/gpu/drm/nouveau/include/nvif/driver.h new file mode 100644 index 000000000..8bd39e692 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvif/driver.h @@ -0,0 +1,22 @@ +#ifndef __NVIF_DRIVER_H__ +#define __NVIF_DRIVER_H__ + +struct nvif_driver { + const char *name; + int (*init)(const char *name, u64 device, const char *cfg, + const char *dbg, void **priv); + void (*fini)(void *priv); + int (*suspend)(void *priv); + int (*resume)(void *priv); + int (*ioctl)(void *priv, bool super, void *data, u32 size, void **hack); + void __iomem *(*map)(void *priv, u64 handle, u32 size); + void (*unmap)(void *priv, void __iomem *ptr, u32 size); + bool keep; +}; + +extern const struct nvif_driver nvif_driver_nvkm; +extern const struct nvif_driver nvif_driver_drm; +extern const struct nvif_driver nvif_driver_lib; +extern const struct nvif_driver nvif_driver_null; + +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvif/event.h b/kernel/drivers/gpu/drm/nouveau/include/nvif/event.h new file mode 100644 index 000000000..21764499b --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvif/event.h @@ -0,0 +1,62 @@ +#ifndef __NVIF_EVENT_H__ +#define __NVIF_EVENT_H__ + +struct nvif_notify_req_v0 { + __u8 version; + __u8 reply; + __u8 pad02[5]; +#define NVIF_NOTIFY_V0_ROUTE_NVIF 0x00 + __u8 route; + __u64 token; /* must be unique */ + __u8 data[]; /* request data (below) */ +}; + +struct nvif_notify_rep_v0 { + __u8 version; + __u8 pad01[6]; + __u8 route; + __u64 token; + __u8 data[]; /* reply data (below) */ +}; + +struct nvif_notify_head_req_v0 { + /* nvif_notify_req ... */ + __u8 version; + __u8 head; + __u8 pad02[6]; +}; + +struct nvif_notify_head_rep_v0 { + /* nvif_notify_rep ... */ + __u8 version; + __u8 pad01[7]; +}; + +struct nvif_notify_conn_req_v0 { + /* nvif_notify_req ... */ + __u8 version; +#define NVIF_NOTIFY_CONN_V0_PLUG 0x01 +#define NVIF_NOTIFY_CONN_V0_UNPLUG 0x02 +#define NVIF_NOTIFY_CONN_V0_IRQ 0x04 +#define NVIF_NOTIFY_CONN_V0_ANY 0x07 + __u8 mask; + __u8 conn; + __u8 pad03[5]; +}; + +struct nvif_notify_conn_rep_v0 { + /* nvif_notify_rep ... */ + __u8 version; + __u8 mask; + __u8 pad02[6]; +}; + +struct nvif_notify_uevent_req { + /* nvif_notify_req ... */ +}; + +struct nvif_notify_uevent_rep { + /* nvif_notify_rep ... */ +}; + +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvif/ioctl.h b/kernel/drivers/gpu/drm/nouveau/include/nvif/ioctl.h new file mode 100644 index 000000000..4cd8e323b --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvif/ioctl.h @@ -0,0 +1,128 @@ +#ifndef __NVIF_IOCTL_H__ +#define __NVIF_IOCTL_H__ + +struct nvif_ioctl_v0 { + __u8 version; +#define NVIF_IOCTL_V0_OWNER_NVIF 0x00 +#define NVIF_IOCTL_V0_OWNER_ANY 0xff + __u8 owner; +#define NVIF_IOCTL_V0_NOP 0x00 +#define NVIF_IOCTL_V0_SCLASS 0x01 +#define NVIF_IOCTL_V0_NEW 0x02 +#define NVIF_IOCTL_V0_DEL 0x03 +#define NVIF_IOCTL_V0_MTHD 0x04 +#define NVIF_IOCTL_V0_RD 0x05 +#define NVIF_IOCTL_V0_WR 0x06 +#define NVIF_IOCTL_V0_MAP 0x07 +#define NVIF_IOCTL_V0_UNMAP 0x08 +#define NVIF_IOCTL_V0_NTFY_NEW 0x09 +#define NVIF_IOCTL_V0_NTFY_DEL 0x0a +#define NVIF_IOCTL_V0_NTFY_GET 0x0b +#define NVIF_IOCTL_V0_NTFY_PUT 0x0c + __u8 type; + __u8 path_nr; +#define NVIF_IOCTL_V0_ROUTE_NVIF 0x00 +#define NVIF_IOCTL_V0_ROUTE_HIDDEN 0xff + __u8 pad04[3]; + __u8 route; + __u64 token; + __u32 path[8]; /* in reverse */ + __u8 data[]; /* ioctl data (below) */ +}; + +struct nvif_ioctl_nop { +}; + +struct nvif_ioctl_sclass_v0 { + /* nvif_ioctl ... */ + __u8 version; + __u8 count; + __u8 pad02[6]; + __u32 oclass[]; +}; + +struct nvif_ioctl_new_v0 { + /* nvif_ioctl ... */ + __u8 version; + __u8 pad01[6]; + __u8 route; + __u64 token; + __u32 handle; +/* these class numbers are made up by us, and not nvidia-assigned */ +#define NVIF_IOCTL_NEW_V0_PERFCTR 0x0000ffff +#define NVIF_IOCTL_NEW_V0_CONTROL 0x0000fffe + __u32 oclass; + __u8 data[]; /* class data (class.h) */ +}; + +struct nvif_ioctl_del { +}; + +struct nvif_ioctl_rd_v0 { + /* nvif_ioctl ... */ + __u8 version; + __u8 size; + __u8 pad02[2]; + __u32 data; + __u64 addr; +}; + +struct nvif_ioctl_wr_v0 { + /* nvif_ioctl ... */ + __u8 version; + __u8 size; + __u8 pad02[2]; + __u32 data; + __u64 addr; +}; + +struct nvif_ioctl_map_v0 { + /* nvif_ioctl ... */ + __u8 version; + __u8 pad01[3]; + __u32 length; + __u64 handle; +}; + +struct nvif_ioctl_unmap { +}; + +struct nvif_ioctl_ntfy_new_v0 { + /* nvif_ioctl ... */ + __u8 version; + __u8 event; + __u8 index; + __u8 pad03[5]; + __u8 data[]; /* event request data (event.h) */ +}; + +struct nvif_ioctl_ntfy_del_v0 { + /* nvif_ioctl ... */ + __u8 version; + __u8 index; + __u8 pad02[6]; +}; + +struct nvif_ioctl_ntfy_get_v0 { + /* nvif_ioctl ... */ + __u8 version; + __u8 index; + __u8 pad02[6]; +}; + +struct nvif_ioctl_ntfy_put_v0 { + /* nvif_ioctl ... */ + __u8 version; + __u8 index; + __u8 pad02[6]; +}; + +struct nvif_ioctl_mthd_v0 { + /* nvif_ioctl ... */ + __u8 version; + __u8 method; + __u8 pad02[6]; + __u8 data[]; /* method data (class.h) */ +}; + +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvif/list.h b/kernel/drivers/gpu/drm/nouveau/include/nvif/list.h new file mode 100644 index 000000000..8af5d144e --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvif/list.h @@ -0,0 +1,353 @@ +/* + * Copyright © 2010 Intel Corporation + * Copyright © 2010 Francisco Jerez + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to 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 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. + * + */ + +/* Modified by Ben Skeggs to match kernel list APIs */ + +#ifndef _XORG_LIST_H_ +#define _XORG_LIST_H_ + +/** + * @file Classic doubly-link circular list implementation. + * For real usage examples of the linked list, see the file test/list.c + * + * Example: + * We need to keep a list of struct foo in the parent struct bar, i.e. what + * we want is something like this. + * + * struct bar { + * ... + * struct foo *list_of_foos; -----> struct foo {}, struct foo {}, struct foo{} + * ... + * } + * + * We need one list head in bar and a list element in all list_of_foos (both are of + * data type 'struct list_head'). + * + * struct bar { + * ... + * struct list_head list_of_foos; + * ... + * } + * + * struct foo { + * ... + * struct list_head entry; + * ... + * } + * + * Now we initialize the list head: + * + * struct bar bar; + * ... + * INIT_LIST_HEAD(&bar.list_of_foos); + * + * Then we create the first element and add it to this list: + * + * struct foo *foo = malloc(...); + * .... + * list_add(&foo->entry, &bar.list_of_foos); + * + * Repeat the above for each element you want to add to the list. Deleting + * works with the element itself. + * list_del(&foo->entry); + * free(foo); + * + * Note: calling list_del(&bar.list_of_foos) will set bar.list_of_foos to an empty + * list again. + * + * Looping through the list requires a 'struct foo' as iterator and the + * name of the field the subnodes use. + * + * struct foo *iterator; + * list_for_each_entry(iterator, &bar.list_of_foos, entry) { + * if (iterator->something == ...) + * ... + * } + * + * Note: You must not call list_del() on the iterator if you continue the + * loop. You need to run the safe for-each loop instead: + * + * struct foo *iterator, *next; + * list_for_each_entry_safe(iterator, next, &bar.list_of_foos, entry) { + * if (...) + * list_del(&iterator->entry); + * } + * + */ + +/** + * The linkage struct for list nodes. This struct must be part of your + * to-be-linked struct. struct list_head is required for both the head of the + * list and for each list node. + * + * Position and name of the struct list_head field is irrelevant. + * There are no requirements that elements of a list are of the same type. + * There are no requirements for a list head, any struct list_head can be a list + * head. + */ +struct list_head { + struct list_head *next, *prev; +}; + +/** + * Initialize the list as an empty list. + * + * Example: + * INIT_LIST_HEAD(&bar->list_of_foos); + * + * @param The list to initialized. + */ +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +static inline void +INIT_LIST_HEAD(struct list_head *list) +{ + list->next = list->prev = list; +} + +static inline void +__list_add(struct list_head *entry, + struct list_head *prev, struct list_head *next) +{ + next->prev = entry; + entry->next = next; + entry->prev = prev; + prev->next = entry; +} + +/** + * Insert a new element after the given list head. The new element does not + * need to be initialised as empty list. + * The list changes from: + * head → some element → ... + * to + * head → new element → older element → ... + * + * Example: + * struct foo *newfoo = malloc(...); + * list_add(&newfoo->entry, &bar->list_of_foos); + * + * @param entry The new element to prepend to the list. + * @param head The existing list. + */ +static inline void +list_add(struct list_head *entry, struct list_head *head) +{ + __list_add(entry, head, head->next); +} + +/** + * Append a new element to the end of the list given with this list head. + * + * The list changes from: + * head → some element → ... → lastelement + * to + * head → some element → ... → lastelement → new element + * + * Example: + * struct foo *newfoo = malloc(...); + * list_add_tail(&newfoo->entry, &bar->list_of_foos); + * + * @param entry The new element to prepend to the list. + * @param head The existing list. + */ +static inline void +list_add_tail(struct list_head *entry, struct list_head *head) +{ + __list_add(entry, head->prev, head); +} + +static inline void +__list_del(struct list_head *prev, struct list_head *next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * Remove the element from the list it is in. Using this function will reset + * the pointers to/from this element so it is removed from the list. It does + * NOT free the element itself or manipulate it otherwise. + * + * Using list_del on a pure list head (like in the example at the top of + * this file) will NOT remove the first element from + * the list but rather reset the list as empty list. + * + * Example: + * list_del(&foo->entry); + * + * @param entry The element to remove. + */ +static inline void +list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + +static inline void +list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add_tail(list, head); +} + +/** + * Check if the list is empty. + * + * Example: + * list_empty(&bar->list_of_foos); + * + * @return True if the list contains one or more elements or False otherwise. + */ +static inline bool +list_empty(struct list_head *head) +{ + return head->next == head; +} + +/** + * Returns a pointer to the container of this list element. + * + * Example: + * struct foo* f; + * f = container_of(&foo->entry, struct foo, entry); + * assert(f == foo); + * + * @param ptr Pointer to the struct list_head. + * @param type Data type of the list element. + * @param member Member name of the struct list_head field in the list element. + * @return A pointer to the data struct containing the list head. + */ +#ifndef container_of +#define container_of(ptr, type, member) \ + (type *)((char *)(ptr) - (char *) &((type *)0)->member) +#endif + +/** + * Alias of container_of + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * Retrieve the first list entry for the given list pointer. + * + * Example: + * struct foo *first; + * first = list_first_entry(&bar->list_of_foos, struct foo, list_of_foos); + * + * @param ptr The list head + * @param type Data type of the list element to retrieve + * @param member Member name of the struct list_head field in the list element. + * @return A pointer to the first list element. + */ +#define list_first_entry(ptr, type, member) \ + list_entry((ptr)->next, type, member) + +/** + * Retrieve the last list entry for the given listpointer. + * + * Example: + * struct foo *first; + * first = list_last_entry(&bar->list_of_foos, struct foo, list_of_foos); + * + * @param ptr The list head + * @param type Data type of the list element to retrieve + * @param member Member name of the struct list_head field in the list element. + * @return A pointer to the last list element. + */ +#define list_last_entry(ptr, type, member) \ + list_entry((ptr)->prev, type, member) + +#define __container_of(ptr, sample, member) \ + (void *)container_of((ptr), typeof(*(sample)), member) + +/** + * Loop through the list given by head and set pos to struct in the list. + * + * Example: + * struct foo *iterator; + * list_for_each_entry(iterator, &bar->list_of_foos, entry) { + * [modify iterator] + * } + * + * This macro is not safe for node deletion. Use list_for_each_entry_safe + * instead. + * + * @param pos Iterator variable of the type of the list elements. + * @param head List head + * @param member Member name of the struct list_head in the list elements. + * + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = __container_of((head)->next, pos, member); \ + &pos->member != (head); \ + pos = __container_of(pos->member.next, pos, member)) + +/** + * Loop through the list, keeping a backup pointer to the element. This + * macro allows for the deletion of a list element while looping through the + * list. + * + * See list_for_each_entry for more details. + */ +#define list_for_each_entry_safe(pos, tmp, head, member) \ + for (pos = __container_of((head)->next, pos, member), \ + tmp = __container_of(pos->member.next, pos, member); \ + &pos->member != (head); \ + pos = tmp, tmp = __container_of(pos->member.next, tmp, member)) + + +#define list_for_each_entry_reverse(pos, head, member) \ + for (pos = __container_of((head)->prev, pos, member); \ + &pos->member != (head); \ + pos = __container_of(pos->member.prev, pos, member)) + +#define list_for_each_entry_continue(pos, head, member) \ + for (pos = __container_of(pos->member.next, pos, member); \ + &pos->member != (head); \ + pos = __container_of(pos->member.next, pos, member)) + +#define list_for_each_entry_continue_reverse(pos, head, member) \ + for (pos = __container_of(pos->member.prev, pos, member); \ + &pos->member != (head); \ + pos = __container_of(pos->member.prev, pos, member)) + +#define list_for_each_entry_from(pos, head, member) \ + for (; \ + &pos->member != (head); \ + pos = __container_of(pos->member.next, pos, member)) + +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvif/notify.h b/kernel/drivers/gpu/drm/nouveau/include/nvif/notify.h new file mode 100644 index 000000000..9ebfa3b45 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvif/notify.h @@ -0,0 +1,39 @@ +#ifndef __NVIF_NOTIFY_H__ +#define __NVIF_NOTIFY_H__ + +struct nvif_notify { + struct nvif_object *object; + int index; + +#define NVIF_NOTIFY_USER 0 +#define NVIF_NOTIFY_WORK 1 + unsigned long flags; + atomic_t putcnt; + void (*dtor)(struct nvif_notify *); +#define NVIF_NOTIFY_DROP 0 +#define NVIF_NOTIFY_KEEP 1 + int (*func)(struct nvif_notify *); + + /* this is const for a *very* good reason - the data might be on the + * stack from an irq handler. if you're not nvif/notify.c then you + * should probably think twice before casting it away... + */ + const void *data; + u32 size; + struct work_struct work; +}; + +int nvif_notify_init(struct nvif_object *, void (*dtor)(struct nvif_notify *), + int (*func)(struct nvif_notify *), bool work, u8 type, + void *data, u32 size, u32 reply, struct nvif_notify *); +int nvif_notify_fini(struct nvif_notify *); +int nvif_notify_get(struct nvif_notify *); +int nvif_notify_put(struct nvif_notify *); +int nvif_notify(const void *, u32, const void *, u32); + +int nvif_notify_new(struct nvif_object *, int (*func)(struct nvif_notify *), + bool work, u8 type, void *data, u32 size, u32 reply, + struct nvif_notify **); +void nvif_notify_ref(struct nvif_notify *, struct nvif_notify **); + +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvif/object.h b/kernel/drivers/gpu/drm/nouveau/include/nvif/object.h new file mode 100644 index 000000000..04c874707 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvif/object.h @@ -0,0 +1,75 @@ +#ifndef __NVIF_OBJECT_H__ +#define __NVIF_OBJECT_H__ + +#include + +struct nvif_object { + struct nvif_object *parent; + struct nvif_object *object; /*XXX: hack for nvif_object() */ + struct kref refcount; + u32 handle; + u32 oclass; + void *data; + u32 size; + void *priv; /*XXX: hack */ + void (*dtor)(struct nvif_object *); + struct { + void __iomem *ptr; + u32 size; + } map; +}; + +int nvif_object_init(struct nvif_object *, void (*dtor)(struct nvif_object *), + u32 handle, u32 oclass, void *, u32, + struct nvif_object *); +void nvif_object_fini(struct nvif_object *); +int nvif_object_new(struct nvif_object *, u32 handle, u32 oclass, + void *, u32, struct nvif_object **); +void nvif_object_ref(struct nvif_object *, struct nvif_object **); +int nvif_object_ioctl(struct nvif_object *, void *, u32, void **); +int nvif_object_sclass(struct nvif_object *, u32 *, int); +u32 nvif_object_rd(struct nvif_object *, int, u64); +void nvif_object_wr(struct nvif_object *, int, u64, u32); +int nvif_object_mthd(struct nvif_object *, u32, void *, u32); +int nvif_object_map(struct nvif_object *); +void nvif_object_unmap(struct nvif_object *); + +#define nvif_object(a) (a)->object + +#define ioread8_native ioread8 +#define iowrite8_native iowrite8 +#define nvif_rd(a,b,c) ({ \ + struct nvif_object *_object = nvif_object(a); \ + u32 _data; \ + if (likely(_object->map.ptr)) \ + _data = ioread##b##_native((u8 __iomem *)_object->map.ptr + (c)); \ + else \ + _data = nvif_object_rd(_object, (b) / 8, (c)); \ + _data; \ +}) +#define nvif_wr(a,b,c,d) ({ \ + struct nvif_object *_object = nvif_object(a); \ + if (likely(_object->map.ptr)) \ + iowrite##b##_native((d), (u8 __iomem *)_object->map.ptr + (c)); \ + else \ + nvif_object_wr(_object, (b) / 8, (c), (d)); \ +}) +#define nvif_rd08(a,b) ({ u8 _v = nvif_rd((a), 8, (b)); _v; }) +#define nvif_rd16(a,b) ({ u16 _v = nvif_rd((a), 16, (b)); _v; }) +#define nvif_rd32(a,b) ({ u32 _v = nvif_rd((a), 32, (b)); _v; }) +#define nvif_wr08(a,b,c) nvif_wr((a), 8, (b), (u8)(c)) +#define nvif_wr16(a,b,c) nvif_wr((a), 16, (b), (u16)(c)) +#define nvif_wr32(a,b,c) nvif_wr((a), 32, (b), (u32)(c)) +#define nvif_mask(a,b,c,d) ({ \ + u32 _v = nvif_rd32(nvif_object(a), (b)); \ + nvif_wr32(nvif_object(a), (b), (_v & ~(c)) | (d)); \ + _v; \ +}) + +#define nvif_mthd(a,b,c,d) nvif_object_mthd(nvif_object(a), (b), (c), (d)) + +/*XXX*/ +#include +#define nvxx_object(a) ((struct nvkm_object *)nvif_object(a)->priv) + +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvif/os.h b/kernel/drivers/gpu/drm/nouveau/include/nvif/os.h new file mode 100644 index 000000000..bdd05ee7e --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvif/os.h @@ -0,0 +1,44 @@ +#ifndef __NOUVEAU_OS_H__ +#define __NOUVEAU_OS_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 + +#ifndef ioread32_native +#ifdef __BIG_ENDIAN +#define ioread16_native ioread16be +#define iowrite16_native iowrite16be +#define ioread32_native ioread32be +#define iowrite32_native iowrite32be +#else /* def __BIG_ENDIAN */ +#define ioread16_native ioread16 +#define iowrite16_native iowrite16 +#define ioread32_native ioread32 +#define iowrite32_native iowrite32 +#endif /* def __BIG_ENDIAN else */ +#endif /* !ioread32_native */ + +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvif/unpack.h b/kernel/drivers/gpu/drm/nouveau/include/nvif/unpack.h new file mode 100644 index 000000000..5933188b4 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvif/unpack.h @@ -0,0 +1,24 @@ +#ifndef __NVIF_UNPACK_H__ +#define __NVIF_UNPACK_H__ + +#define nvif_unvers(d) ({ \ + ret = (size == sizeof(d)) ? 0 : -ENOSYS; \ + (ret == 0); \ +}) + +#define nvif_unpack(d,vl,vh,m) ({ \ + if ((vl) == 0 || ret == -ENOSYS) { \ + int _size = sizeof(d); \ + if (_size <= size && (d).version >= (vl) && \ + (d).version <= (vh)) { \ + data = (u8 *)data + _size; \ + size = size - _size; \ + ret = ((m) || !size) ? 0 : -E2BIG; \ + } else { \ + ret = -ENOSYS; \ + } \ + } \ + (ret == 0); \ +}) + +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/client.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/client.h new file mode 100644 index 000000000..a35b38244 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/client.h @@ -0,0 +1,55 @@ +#ifndef __NVKM_CLIENT_H__ +#define __NVKM_CLIENT_H__ +#include + +struct nvkm_client { + struct nvkm_namedb namedb; + struct nvkm_handle *root; + struct nvkm_object *device; + char name[32]; + u32 debug; + struct nvkm_vm *vm; + bool super; + void *data; + + int (*ntfy)(const void *, u32, const void *, u32); + struct nvkm_client_notify *notify[16]; +}; + +static inline struct nvkm_client * +nv_client(void *obj) +{ +#if CONFIG_NOUVEAU_DEBUG >= NV_DBG_PARANOIA + if (unlikely(!nv_iclass(obj, NV_CLIENT_CLASS))) + nv_assert("BAD CAST -> NvClient, %08x", nv_hclass(obj)); +#endif + return obj; +} + +static inline struct nvkm_client * +nvkm_client(void *obj) +{ + struct nvkm_object *client = nv_object(obj); + while (client && !(nv_iclass(client, NV_CLIENT_CLASS))) + client = client->parent; + return (void *)client; +} + +#define nvkm_client_create(n,c,oc,od,d) \ + nvkm_client_create_((n), (c), (oc), (od), sizeof(**d), (void **)d) + +int nvkm_client_create_(const char *name, u64 device, const char *cfg, + const char *dbg, int, void **); +#define nvkm_client_destroy(p) \ + nvkm_namedb_destroy(&(p)->base) + +int nvkm_client_init(struct nvkm_client *); +int nvkm_client_fini(struct nvkm_client *, bool suspend); +const char *nvkm_client_name(void *obj); + +int nvkm_client_notify_new(struct nvkm_object *, struct nvkm_event *, + void *data, u32 size); +int nvkm_client_notify_del(struct nvkm_client *, int index); +int nvkm_client_notify_get(struct nvkm_client *, int index); +int nvkm_client_notify_put(struct nvkm_client *, int index); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/debug.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/debug.h new file mode 100644 index 000000000..d07cb860b --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/debug.h @@ -0,0 +1,18 @@ +#ifndef __NVKM_DEBUG_H__ +#define __NVKM_DEBUG_H__ +extern int nv_info_debug_level; + +#define NV_DBG_FATAL 0 +#define NV_DBG_ERROR 1 +#define NV_DBG_WARN 2 +#define NV_DBG_INFO nv_info_debug_level +#define NV_DBG_DEBUG 4 +#define NV_DBG_TRACE 5 +#define NV_DBG_PARANOIA 6 +#define NV_DBG_SPAM 7 + +#define NV_DBG_INFO_NORMAL 3 +#define NV_DBG_INFO_SILENT NV_DBG_DEBUG + +#define nv_debug_level(a) nv_info_debug_level = NV_DBG_INFO_##a +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/device.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/device.h new file mode 100644 index 000000000..333db33a1 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/device.h @@ -0,0 +1,101 @@ +#ifndef __NVKM_DEVICE_H__ +#define __NVKM_DEVICE_H__ +#include +#include + +struct nvkm_device { + struct nvkm_engine engine; + struct list_head head; + + struct pci_dev *pdev; + struct platform_device *platformdev; + u64 handle; + + struct nvkm_event event; + + const char *cfgopt; + const char *dbgopt; + const char *name; + const char *cname; + u64 disable_mask; + + enum { + NV_04 = 0x04, + NV_10 = 0x10, + NV_11 = 0x11, + NV_20 = 0x20, + NV_30 = 0x30, + NV_40 = 0x40, + NV_50 = 0x50, + NV_C0 = 0xc0, + NV_E0 = 0xe0, + GM100 = 0x110, + } card_type; + u32 chipset; + u8 chiprev; + u32 crystal; + + struct nvkm_oclass *oclass[NVDEV_SUBDEV_NR]; + struct nvkm_object *subdev[NVDEV_SUBDEV_NR]; + + struct { + struct notifier_block nb; + } acpi; +}; + +struct nvkm_device *nvkm_device_find(u64 name); +int nvkm_device_list(u64 *name, int size); + +struct nvkm_device *nv_device(void *obj); + +static inline bool +nv_device_match(struct nvkm_object *object, u16 dev, u16 ven, u16 sub) +{ + struct nvkm_device *device = nv_device(object); + return device->pdev->device == dev && + device->pdev->subsystem_vendor == ven && + device->pdev->subsystem_device == sub; +} + +static inline bool +nv_device_is_pci(struct nvkm_device *device) +{ + return device->pdev != NULL; +} + +static inline bool +nv_device_is_cpu_coherent(struct nvkm_device *device) +{ + return (!IS_ENABLED(CONFIG_ARM) && nv_device_is_pci(device)); +} + +static inline struct device * +nv_device_base(struct nvkm_device *device) +{ + return nv_device_is_pci(device) ? &device->pdev->dev : + &device->platformdev->dev; +} + +resource_size_t +nv_device_resource_start(struct nvkm_device *device, unsigned int bar); + +resource_size_t +nv_device_resource_len(struct nvkm_device *device, unsigned int bar); + +int +nv_device_get_irq(struct nvkm_device *device, bool stall); + +struct platform_device; + +enum nv_bus_type { + NVKM_BUS_PCI, + NVKM_BUS_PLATFORM, +}; + +#define nvkm_device_create(p,t,n,s,c,d,u) \ + nvkm_device_create_((void *)(p), (t), (n), (s), (c), (d), \ + sizeof(**u), (void **)u) +int nvkm_device_create_(void *, enum nv_bus_type type, u64 name, + const char *sname, const char *cfg, const char *dbg, + int, void **); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/devidx.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/devidx.h new file mode 100644 index 000000000..60c5888b5 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/devidx.h @@ -0,0 +1,62 @@ +#ifndef __NVKM_DEVIDX_H__ +#define __NVKM_DEVIDX_H__ +enum nvkm_devidx { + NVDEV_ENGINE_DEVICE, + NVDEV_SUBDEV_VBIOS, + + /* All subdevs from DEVINIT to DEVINIT_LAST will be created before + * *any* of them are initialised. This subdev category is used + * for any subdevs that the VBIOS init table parsing may call out + * to during POST. + */ + NVDEV_SUBDEV_DEVINIT, + NVDEV_SUBDEV_IBUS, + NVDEV_SUBDEV_GPIO, + NVDEV_SUBDEV_I2C, + NVDEV_SUBDEV_DEVINIT_LAST = NVDEV_SUBDEV_I2C, + + /* This grouping of subdevs are initialised right after they've + * been created, and are allowed to assume any subdevs in the + * list above them exist and have been initialised. + */ + NVDEV_SUBDEV_FUSE, + NVDEV_SUBDEV_MXM, + NVDEV_SUBDEV_MC, + NVDEV_SUBDEV_BUS, + NVDEV_SUBDEV_TIMER, + NVDEV_SUBDEV_FB, + NVDEV_SUBDEV_LTC, + NVDEV_SUBDEV_INSTMEM, + NVDEV_SUBDEV_MMU, + NVDEV_SUBDEV_BAR, + NVDEV_SUBDEV_PMU, + NVDEV_SUBDEV_VOLT, + NVDEV_SUBDEV_THERM, + NVDEV_SUBDEV_CLK, + + NVDEV_ENGINE_FIRST, + NVDEV_ENGINE_DMAOBJ = NVDEV_ENGINE_FIRST, + NVDEV_ENGINE_IFB, + NVDEV_ENGINE_FIFO, + NVDEV_ENGINE_SW, + NVDEV_ENGINE_GR, + NVDEV_ENGINE_MPEG, + NVDEV_ENGINE_ME, + NVDEV_ENGINE_VP, + NVDEV_ENGINE_CIPHER, + NVDEV_ENGINE_BSP, + NVDEV_ENGINE_MSPPP, + NVDEV_ENGINE_CE0, + NVDEV_ENGINE_CE1, + NVDEV_ENGINE_CE2, + NVDEV_ENGINE_VIC, + NVDEV_ENGINE_MSENC, + NVDEV_ENGINE_DISP, + NVDEV_ENGINE_PM, + NVDEV_ENGINE_MSVLD, + NVDEV_ENGINE_SEC, + NVDEV_ENGINE_MSPDEC, + + NVDEV_SUBDEV_NR, +}; +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/engctx.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/engctx.h new file mode 100644 index 000000000..1bf2e8eb4 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/engctx.h @@ -0,0 +1,51 @@ +#ifndef __NVKM_ENGCTX_H__ +#define __NVKM_ENGCTX_H__ +#include + +#include + +#define NV_ENGCTX_(eng,var) (NV_ENGCTX_CLASS | ((var) << 8) | (eng)) +#define NV_ENGCTX(name,var) NV_ENGCTX_(NVDEV_ENGINE_##name, (var)) + +struct nvkm_engctx { + struct nvkm_gpuobj gpuobj; + struct nvkm_vma vma; + struct list_head head; + unsigned long save; + u64 addr; +}; + +static inline struct nvkm_engctx * +nv_engctx(void *obj) +{ +#if CONFIG_NOUVEAU_DEBUG >= NV_DBG_PARANOIA + if (unlikely(!nv_iclass(obj, NV_ENGCTX_CLASS))) + nv_assert("BAD CAST -> NvEngCtx, %08x", nv_hclass(obj)); +#endif + return obj; +} + +#define nvkm_engctx_create(p,e,c,g,s,a,f,d) \ + nvkm_engctx_create_((p), (e), (c), (g), (s), (a), (f), \ + sizeof(**d), (void **)d) + +int nvkm_engctx_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, struct nvkm_object *, + u32 size, u32 align, u32 flags, + int length, void **data); +void nvkm_engctx_destroy(struct nvkm_engctx *); +int nvkm_engctx_init(struct nvkm_engctx *); +int nvkm_engctx_fini(struct nvkm_engctx *, bool suspend); + +int _nvkm_engctx_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); +void _nvkm_engctx_dtor(struct nvkm_object *); +int _nvkm_engctx_init(struct nvkm_object *); +int _nvkm_engctx_fini(struct nvkm_object *, bool suspend); +#define _nvkm_engctx_rd32 _nvkm_gpuobj_rd32 +#define _nvkm_engctx_wr32 _nvkm_gpuobj_wr32 + +struct nvkm_object *nvkm_engctx_get(struct nvkm_engine *, u64 addr); +void nvkm_engctx_put(struct nvkm_object *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/engine.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/engine.h new file mode 100644 index 000000000..faf0fd2f0 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/engine.h @@ -0,0 +1,56 @@ +#ifndef __NVKM_ENGINE_H__ +#define __NVKM_ENGINE_H__ +#include + +#define NV_ENGINE_(eng,var) (NV_ENGINE_CLASS | ((var) << 8) | (eng)) +#define NV_ENGINE(name,var) NV_ENGINE_(NVDEV_ENGINE_##name, (var)) + +struct nvkm_engine { + struct nvkm_subdev subdev; + struct nvkm_oclass *cclass; + struct nvkm_oclass *sclass; + + struct list_head contexts; + spinlock_t lock; + + void (*tile_prog)(struct nvkm_engine *, int region); + int (*tlb_flush)(struct nvkm_engine *); +}; + +static inline struct nvkm_engine * +nv_engine(void *obj) +{ +#if CONFIG_NOUVEAU_DEBUG >= NV_DBG_PARANOIA + if (unlikely(!nv_iclass(obj, NV_ENGINE_CLASS))) + nv_assert("BAD CAST -> NvEngine, %08x", nv_hclass(obj)); +#endif + return obj; +} + +static inline int +nv_engidx(struct nvkm_engine *engine) +{ + return nv_subidx(&engine->subdev); +} + +struct nvkm_engine *nvkm_engine(void *obj, int idx); + +#define nvkm_engine_create(p,e,c,d,i,f,r) \ + nvkm_engine_create_((p), (e), (c), (d), (i), (f), \ + sizeof(**r),(void **)r) + +#define nvkm_engine_destroy(p) \ + nvkm_subdev_destroy(&(p)->subdev) +#define nvkm_engine_init(p) \ + nvkm_subdev_init(&(p)->subdev) +#define nvkm_engine_fini(p,s) \ + nvkm_subdev_fini(&(p)->subdev, (s)) + +int nvkm_engine_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, bool, const char *, + const char *, int, void **); + +#define _nvkm_engine_dtor _nvkm_subdev_dtor +#define _nvkm_engine_init _nvkm_subdev_init +#define _nvkm_engine_fini _nvkm_subdev_fini +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/enum.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/enum.h new file mode 100644 index 000000000..e76f76f11 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/enum.h @@ -0,0 +1,21 @@ +#ifndef __NVKM_ENUM_H__ +#define __NVKM_ENUM_H__ +#include + +struct nvkm_enum { + u32 value; + const char *name; + const void *data; + u32 data2; +}; + +const struct nvkm_enum *nvkm_enum_find(const struct nvkm_enum *, u32 value); +const struct nvkm_enum *nvkm_enum_print(const struct nvkm_enum *, u32 value); + +struct nvkm_bitfield { + u32 mask; + const char *name; +}; + +void nvkm_bitfield_print(const struct nvkm_bitfield *, u32 value); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/event.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/event.h new file mode 100644 index 000000000..b98fe2de5 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/event.h @@ -0,0 +1,34 @@ +#ifndef __NVKM_EVENT_H__ +#define __NVKM_EVENT_H__ +#include +struct nvkm_notify; +struct nvkm_object; + +struct nvkm_event { + const struct nvkm_event_func *func; + + int types_nr; + int index_nr; + + spinlock_t refs_lock; + spinlock_t list_lock; + struct list_head list; + int *refs; +}; + +struct nvkm_event_func { + int (*ctor)(struct nvkm_object *, void *data, u32 size, + struct nvkm_notify *); + void (*send)(void *data, u32 size, struct nvkm_notify *); + void (*init)(struct nvkm_event *, int type, int index); + void (*fini)(struct nvkm_event *, int type, int index); +}; + +int nvkm_event_init(const struct nvkm_event_func *func, int types_nr, + int index_nr, struct nvkm_event *); +void nvkm_event_fini(struct nvkm_event *); +void nvkm_event_get(struct nvkm_event *, u32 types, int index); +void nvkm_event_put(struct nvkm_event *, u32 types, int index); +void nvkm_event_send(struct nvkm_event *, u32 types, int index, + void *data, u32 size); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/gpuobj.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/gpuobj.h new file mode 100644 index 000000000..e0187e7ab --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/gpuobj.h @@ -0,0 +1,64 @@ +#ifndef __NVKM_GPUOBJ_H__ +#define __NVKM_GPUOBJ_H__ +#include +#include +struct nvkm_vma; +struct nvkm_vm; + +#define NVOBJ_FLAG_ZERO_ALLOC 0x00000001 +#define NVOBJ_FLAG_ZERO_FREE 0x00000002 +#define NVOBJ_FLAG_HEAP 0x00000004 + +struct nvkm_gpuobj { + struct nvkm_object object; + struct nvkm_object *parent; + struct nvkm_mm_node *node; + struct nvkm_mm heap; + + u32 flags; + u64 addr; + u32 size; +}; + +static inline struct nvkm_gpuobj * +nv_gpuobj(void *obj) +{ +#if CONFIG_NOUVEAU_DEBUG >= NV_DBG_PARANOIA + if (unlikely(!nv_iclass(obj, NV_GPUOBJ_CLASS))) + nv_assert("BAD CAST -> NvGpuObj, %08x", nv_hclass(obj)); +#endif + return obj; +} + +#define nvkm_gpuobj_create(p,e,c,v,g,s,a,f,d) \ + nvkm_gpuobj_create_((p), (e), (c), (v), (g), (s), (a), (f), \ + sizeof(**d), (void **)d) +#define nvkm_gpuobj_init(p) nvkm_object_init(&(p)->object) +#define nvkm_gpuobj_fini(p,s) nvkm_object_fini(&(p)->object, (s)) +int nvkm_gpuobj_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, u32 pclass, + struct nvkm_object *, u32 size, u32 align, + u32 flags, int length, void **); +void nvkm_gpuobj_destroy(struct nvkm_gpuobj *); + +int nvkm_gpuobj_new(struct nvkm_object *, struct nvkm_object *, u32 size, + u32 align, u32 flags, struct nvkm_gpuobj **); +int nvkm_gpuobj_dup(struct nvkm_object *, struct nvkm_gpuobj *, + struct nvkm_gpuobj **); +int nvkm_gpuobj_map(struct nvkm_gpuobj *, u32 acc, struct nvkm_vma *); +int nvkm_gpuobj_map_vm(struct nvkm_gpuobj *, struct nvkm_vm *, u32 access, + struct nvkm_vma *); +void nvkm_gpuobj_unmap(struct nvkm_vma *); + +static inline void +nvkm_gpuobj_ref(struct nvkm_gpuobj *obj, struct nvkm_gpuobj **ref) +{ + nvkm_object_ref(&obj->object, (struct nvkm_object **)ref); +} + +void _nvkm_gpuobj_dtor(struct nvkm_object *); +int _nvkm_gpuobj_init(struct nvkm_object *); +int _nvkm_gpuobj_fini(struct nvkm_object *, bool); +u32 _nvkm_gpuobj_rd32(struct nvkm_object *, u64); +void _nvkm_gpuobj_wr32(struct nvkm_object *, u64, u32); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/handle.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/handle.h new file mode 100644 index 000000000..67f384d09 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/handle.h @@ -0,0 +1,34 @@ +#ifndef __NVKM_HANDLE_H__ +#define __NVKM_HANDLE_H__ +#include +struct nvkm_object; + +struct nvkm_handle { + struct nvkm_namedb *namedb; + struct list_head node; + + struct list_head head; + struct list_head tree; + u32 name; + u32 priv; + + u8 route; + u64 token; + + struct nvkm_handle *parent; + struct nvkm_object *object; +}; + +int nvkm_handle_create(struct nvkm_object *, u32 parent, u32 handle, + struct nvkm_object *, struct nvkm_handle **); +void nvkm_handle_destroy(struct nvkm_handle *); +int nvkm_handle_init(struct nvkm_handle *); +int nvkm_handle_fini(struct nvkm_handle *, bool suspend); + +struct nvkm_object *nvkm_handle_ref(struct nvkm_object *, u32 name); + +struct nvkm_handle *nvkm_handle_get_class(struct nvkm_object *, u16); +struct nvkm_handle *nvkm_handle_get_vinst(struct nvkm_object *, u64); +struct nvkm_handle *nvkm_handle_get_cinst(struct nvkm_object *, u32); +void nvkm_handle_put(struct nvkm_handle *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/ioctl.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/ioctl.h new file mode 100644 index 000000000..88971eb37 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/ioctl.h @@ -0,0 +1,7 @@ +#ifndef __NVKM_IOCTL_H__ +#define __NVKM_IOCTL_H__ +#include +struct nvkm_client; + +int nvkm_ioctl(struct nvkm_client *, bool, void *, u32, void **); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/mm.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/mm.h new file mode 100644 index 000000000..096eb1a62 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/mm.h @@ -0,0 +1,40 @@ +#ifndef __NVKM_MM_H__ +#define __NVKM_MM_H__ +#include + +struct nvkm_mm_node { + struct list_head nl_entry; + struct list_head fl_entry; + struct list_head rl_entry; + +#define NVKM_MM_HEAP_ANY 0x00 + u8 heap; +#define NVKM_MM_TYPE_NONE 0x00 +#define NVKM_MM_TYPE_HOLE 0xff + u8 type; + u32 offset; + u32 length; +}; + +struct nvkm_mm { + struct list_head nodes; + struct list_head free; + + u32 block_size; + int heap_nodes; +}; + +static inline bool +nvkm_mm_initialised(struct nvkm_mm *mm) +{ + return mm->block_size != 0; +} + +int nvkm_mm_init(struct nvkm_mm *, u32 offset, u32 length, u32 block); +int nvkm_mm_fini(struct nvkm_mm *); +int nvkm_mm_head(struct nvkm_mm *, u8 heap, u8 type, u32 size_max, + u32 size_min, u32 align, struct nvkm_mm_node **); +int nvkm_mm_tail(struct nvkm_mm *, u8 heap, u8 type, u32 size_max, + u32 size_min, u32 align, struct nvkm_mm_node **); +void nvkm_mm_free(struct nvkm_mm *, struct nvkm_mm_node **); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/namedb.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/namedb.h new file mode 100644 index 000000000..4cfe16fcd --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/namedb.h @@ -0,0 +1,53 @@ +#ifndef __NVKM_NAMEDB_H__ +#define __NVKM_NAMEDB_H__ +#include +struct nvkm_handle; + +struct nvkm_namedb { + struct nvkm_parent parent; + rwlock_t lock; + struct list_head list; +}; + +static inline struct nvkm_namedb * +nv_namedb(void *obj) +{ +#if CONFIG_NOUVEAU_DEBUG >= NV_DBG_PARANOIA + if (unlikely(!nv_iclass(obj, NV_NAMEDB_CLASS))) + nv_assert("BAD CAST -> NvNameDB, %08x", nv_hclass(obj)); +#endif + return obj; +} + +#define nvkm_namedb_create(p,e,c,v,s,m,d) \ + nvkm_namedb_create_((p), (e), (c), (v), (s), (m), \ + sizeof(**d), (void **)d) +#define nvkm_namedb_init(p) \ + nvkm_parent_init(&(p)->parent) +#define nvkm_namedb_fini(p,s) \ + nvkm_parent_fini(&(p)->parent, (s)) +#define nvkm_namedb_destroy(p) \ + nvkm_parent_destroy(&(p)->parent) + +int nvkm_namedb_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, u32 pclass, + struct nvkm_oclass *, u64 engcls, + int size, void **); + +int _nvkm_namedb_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); +#define _nvkm_namedb_dtor _nvkm_parent_dtor +#define _nvkm_namedb_init _nvkm_parent_init +#define _nvkm_namedb_fini _nvkm_parent_fini + +int nvkm_namedb_insert(struct nvkm_namedb *, u32 name, struct nvkm_object *, + struct nvkm_handle *); +void nvkm_namedb_remove(struct nvkm_handle *); + +struct nvkm_handle *nvkm_namedb_get(struct nvkm_namedb *, u32); +struct nvkm_handle *nvkm_namedb_get_class(struct nvkm_namedb *, u16); +struct nvkm_handle *nvkm_namedb_get_vinst(struct nvkm_namedb *, u64); +struct nvkm_handle *nvkm_namedb_get_cinst(struct nvkm_namedb *, u32); +void nvkm_namedb_put(struct nvkm_handle *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/notify.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/notify.h new file mode 100644 index 000000000..753d08c17 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/notify.h @@ -0,0 +1,38 @@ +#ifndef __NVKM_NOTIFY_H__ +#define __NVKM_NOTIFY_H__ +#include +struct nvkm_object; + +struct nvkm_notify { + struct nvkm_event *event; + struct list_head head; +#define NVKM_NOTIFY_USER 0 +#define NVKM_NOTIFY_WORK 1 + unsigned long flags; + int block; +#define NVKM_NOTIFY_DROP 0 +#define NVKM_NOTIFY_KEEP 1 + int (*func)(struct nvkm_notify *); + + /* set by nvkm_event ctor */ + u32 types; + int index; + u32 size; + + struct work_struct work; + /* this is const for a *very* good reason - the data might be on the + * stack from an irq handler. if you're not core/notify.c then you + * should probably think twice before casting it away... + */ + const void *data; +}; + +int nvkm_notify_init(struct nvkm_object *, struct nvkm_event *, + int (*func)(struct nvkm_notify *), bool work, + void *data, u32 size, u32 reply, + struct nvkm_notify *); +void nvkm_notify_fini(struct nvkm_notify *); +void nvkm_notify_get(struct nvkm_notify *); +void nvkm_notify_put(struct nvkm_notify *); +void nvkm_notify_send(struct nvkm_notify *, void *data, u32 size); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/object.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/object.h new file mode 100644 index 000000000..6e3cd3908 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/object.h @@ -0,0 +1,203 @@ +#ifndef __NVKM_OBJECT_H__ +#define __NVKM_OBJECT_H__ +#include +#include + +#define NV_PARENT_CLASS 0x80000000 +#define NV_NAMEDB_CLASS 0x40000000 +#define NV_CLIENT_CLASS 0x20000000 +#define NV_SUBDEV_CLASS 0x10000000 +#define NV_ENGINE_CLASS 0x08000000 +#define NV_MEMOBJ_CLASS 0x04000000 +#define NV_GPUOBJ_CLASS 0x02000000 +#define NV_ENGCTX_CLASS 0x01000000 +#define NV_OBJECT_CLASS 0x0000ffff + +struct nvkm_object { + struct nvkm_oclass *oclass; + struct nvkm_object *parent; + struct nvkm_engine *engine; + atomic_t refcount; + atomic_t usecount; +#if CONFIG_NOUVEAU_DEBUG >= NV_DBG_PARANOIA +#define NVKM_OBJECT_MAGIC 0x75ef0bad + struct list_head list; + u32 _magic; +#endif +}; + +static inline struct nvkm_object * +nv_object(void *obj) +{ +#if CONFIG_NOUVEAU_DEBUG >= NV_DBG_PARANOIA + if (likely(obj)) { + struct nvkm_object *object = obj; + if (unlikely(object->_magic != NVKM_OBJECT_MAGIC)) + nv_assert("BAD CAST -> NvObject, invalid magic"); + } +#endif + return obj; +} + +#define nvkm_object_create(p,e,c,s,d) \ + nvkm_object_create_((p), (e), (c), (s), sizeof(**d), (void **)d) +int nvkm_object_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, u32, int size, void **); +void nvkm_object_destroy(struct nvkm_object *); +int nvkm_object_init(struct nvkm_object *); +int nvkm_object_fini(struct nvkm_object *, bool suspend); + +int _nvkm_object_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); + +extern struct nvkm_ofuncs nvkm_object_ofuncs; + +/* Don't allocate dynamically, because lockdep needs lock_class_keys to be in + * ".data". */ +struct nvkm_oclass { + u32 handle; + struct nvkm_ofuncs * const ofuncs; + struct nvkm_omthds * const omthds; + struct lock_class_key lock_class_key; +}; + +#define nv_oclass(o) nv_object(o)->oclass +#define nv_hclass(o) nv_oclass(o)->handle +#define nv_iclass(o,i) (nv_hclass(o) & (i)) +#define nv_mclass(o) nv_iclass(o, NV_OBJECT_CLASS) + +static inline struct nvkm_object * +nv_pclass(struct nvkm_object *parent, u32 oclass) +{ + while (parent && !nv_iclass(parent, oclass)) + parent = parent->parent; + return parent; +} + +struct nvkm_omthds { + u32 start; + u32 limit; + int (*call)(struct nvkm_object *, u32, void *, u32); +}; + +struct nvkm_event; +struct nvkm_ofuncs { + int (*ctor)(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *data, u32 size, + struct nvkm_object **); + void (*dtor)(struct nvkm_object *); + int (*init)(struct nvkm_object *); + int (*fini)(struct nvkm_object *, bool suspend); + int (*mthd)(struct nvkm_object *, u32, void *, u32); + int (*ntfy)(struct nvkm_object *, u32, struct nvkm_event **); + int (* map)(struct nvkm_object *, u64 *, u32 *); + u8 (*rd08)(struct nvkm_object *, u64 offset); + u16 (*rd16)(struct nvkm_object *, u64 offset); + u32 (*rd32)(struct nvkm_object *, u64 offset); + void (*wr08)(struct nvkm_object *, u64 offset, u8 data); + void (*wr16)(struct nvkm_object *, u64 offset, u16 data); + void (*wr32)(struct nvkm_object *, u64 offset, u32 data); +}; + +static inline struct nvkm_ofuncs * +nv_ofuncs(void *obj) +{ + return nv_oclass(obj)->ofuncs; +} + +int nvkm_object_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); +void nvkm_object_ref(struct nvkm_object *, struct nvkm_object **); +int nvkm_object_inc(struct nvkm_object *); +int nvkm_object_dec(struct nvkm_object *, bool suspend); +void nvkm_object_debug(void); + +static inline int +nv_exec(void *obj, u32 mthd, void *data, u32 size) +{ + struct nvkm_omthds *method = nv_oclass(obj)->omthds; + + while (method && method->call) { + if (mthd >= method->start && mthd <= method->limit) + return method->call(obj, mthd, data, size); + method++; + } + + return -EINVAL; +} + +static inline int +nv_call(void *obj, u32 mthd, u32 data) +{ + return nv_exec(obj, mthd, &data, sizeof(data)); +} + +static inline u8 +nv_ro08(void *obj, u64 addr) +{ + u8 data = nv_ofuncs(obj)->rd08(obj, addr); + nv_spam(obj, "nv_ro08 0x%08llx 0x%02x\n", addr, data); + return data; +} + +static inline u16 +nv_ro16(void *obj, u64 addr) +{ + u16 data = nv_ofuncs(obj)->rd16(obj, addr); + nv_spam(obj, "nv_ro16 0x%08llx 0x%04x\n", addr, data); + return data; +} + +static inline u32 +nv_ro32(void *obj, u64 addr) +{ + u32 data = nv_ofuncs(obj)->rd32(obj, addr); + nv_spam(obj, "nv_ro32 0x%08llx 0x%08x\n", addr, data); + return data; +} + +static inline void +nv_wo08(void *obj, u64 addr, u8 data) +{ + nv_spam(obj, "nv_wo08 0x%08llx 0x%02x\n", addr, data); + nv_ofuncs(obj)->wr08(obj, addr, data); +} + +static inline void +nv_wo16(void *obj, u64 addr, u16 data) +{ + nv_spam(obj, "nv_wo16 0x%08llx 0x%04x\n", addr, data); + nv_ofuncs(obj)->wr16(obj, addr, data); +} + +static inline void +nv_wo32(void *obj, u64 addr, u32 data) +{ + nv_spam(obj, "nv_wo32 0x%08llx 0x%08x\n", addr, data); + nv_ofuncs(obj)->wr32(obj, addr, data); +} + +static inline u32 +nv_mo32(void *obj, u64 addr, u32 mask, u32 data) +{ + u32 temp = nv_ro32(obj, addr); + nv_wo32(obj, addr, (temp & ~mask) | data); + return temp; +} + +static inline int +nv_memcmp(void *obj, u32 addr, const char *str, u32 len) +{ + unsigned char c1, c2; + + while (len--) { + c1 = nv_ro08(obj, addr++); + c2 = *(str++); + if (c1 != c2) + return c1 - c2; + } + return 0; +} +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/option.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/option.h new file mode 100644 index 000000000..532bfa8e3 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/option.h @@ -0,0 +1,17 @@ +#ifndef __NVKM_OPTION_H__ +#define __NVKM_OPTION_H__ +#include + +const char *nvkm_stropt(const char *optstr, const char *opt, int *len); +bool nvkm_boolopt(const char *optstr, const char *opt, bool value); +int nvkm_dbgopt(const char *optstr, const char *sub); + +/* compares unterminated string 'str' with zero-terminated string 'cmp' */ +static inline int +strncasecmpz(const char *str, const char *cmp, size_t len) +{ + if (strlen(cmp) != len) + return len; + return strncasecmp(str, cmp, len); +} +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/os.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/os.h new file mode 100644 index 000000000..cd57e238d --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/os.h @@ -0,0 +1,4 @@ +#ifndef __NVKM_OS_H__ +#define __NVKM_OS_H__ +#include +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/parent.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/parent.h new file mode 100644 index 000000000..837e4fe96 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/parent.h @@ -0,0 +1,58 @@ +#ifndef __NVKM_PARENT_H__ +#define __NVKM_PARENT_H__ +#include + +struct nvkm_sclass { + struct nvkm_sclass *sclass; + struct nvkm_engine *engine; + struct nvkm_oclass *oclass; +}; + +struct nvkm_parent { + struct nvkm_object object; + + struct nvkm_sclass *sclass; + u64 engine; + + int (*context_attach)(struct nvkm_object *, struct nvkm_object *); + int (*context_detach)(struct nvkm_object *, bool suspend, + struct nvkm_object *); + + int (*object_attach)(struct nvkm_object *parent, + struct nvkm_object *object, u32 name); + void (*object_detach)(struct nvkm_object *parent, int cookie); +}; + +static inline struct nvkm_parent * +nv_parent(void *obj) +{ +#if CONFIG_NOUVEAU_DEBUG >= NV_DBG_PARANOIA + if (unlikely(!(nv_iclass(obj, NV_PARENT_CLASS)))) + nv_assert("BAD CAST -> NvParent, %08x", nv_hclass(obj)); +#endif + return obj; +} + +#define nvkm_parent_create(p,e,c,v,s,m,d) \ + nvkm_parent_create_((p), (e), (c), (v), (s), (m), \ + sizeof(**d), (void **)d) +#define nvkm_parent_init(p) \ + nvkm_object_init(&(p)->object) +#define nvkm_parent_fini(p,s) \ + nvkm_object_fini(&(p)->object, (s)) + +int nvkm_parent_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, u32 pclass, + struct nvkm_oclass *, u64 engcls, + int size, void **); +void nvkm_parent_destroy(struct nvkm_parent *); + +void _nvkm_parent_dtor(struct nvkm_object *); +#define _nvkm_parent_init nvkm_object_init +#define _nvkm_parent_fini nvkm_object_fini + +int nvkm_parent_sclass(struct nvkm_object *, u16 handle, + struct nvkm_object **pengine, + struct nvkm_oclass **poclass); +int nvkm_parent_lclass(struct nvkm_object *, u32 *, int); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/printk.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/printk.h new file mode 100644 index 000000000..836481770 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/printk.h @@ -0,0 +1,29 @@ +#ifndef __NVKM_PRINTK_H__ +#define __NVKM_PRINTK_H__ +#include +#include +struct nvkm_object; + +void __printf(3, 4) +nv_printk_(struct nvkm_object *, int, const char *, ...); + +#define nv_printk(o,l,f,a...) do { \ + if (NV_DBG_##l <= CONFIG_NOUVEAU_DEBUG) \ + nv_printk_(nv_object(o), NV_DBG_##l, f, ##a); \ +} while(0) + +#define nv_fatal(o,f,a...) nv_printk((o), FATAL, f, ##a) +#define nv_error(o,f,a...) nv_printk((o), ERROR, f, ##a) +#define nv_warn(o,f,a...) nv_printk((o), WARN, f, ##a) +#define nv_info(o,f,a...) nv_printk((o), INFO, f, ##a) +#define nv_debug(o,f,a...) nv_printk((o), DEBUG, f, ##a) +#define nv_trace(o,f,a...) nv_printk((o), TRACE, f, ##a) +#define nv_spam(o,f,a...) nv_printk((o), SPAM, f, ##a) +#define nv_ioctl(o,f,a...) nv_trace(nvkm_client(o), "ioctl: "f, ##a) + +#define nv_assert(f,a...) do { \ + if (NV_DBG_FATAL <= CONFIG_NOUVEAU_DEBUG) \ + nv_printk_(NULL, NV_DBG_FATAL, f "\n", ##a); \ + BUG_ON(1); \ +} while(0) +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/ramht.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/ramht.h new file mode 100644 index 000000000..cc132eaa1 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/ramht.h @@ -0,0 +1,20 @@ +#ifndef __NVKM_RAMHT_H__ +#define __NVKM_RAMHT_H__ +#include + +struct nvkm_ramht { + struct nvkm_gpuobj gpuobj; + int bits; +}; + +int nvkm_ramht_insert(struct nvkm_ramht *, int chid, u32 handle, u32 context); +void nvkm_ramht_remove(struct nvkm_ramht *, int cookie); +int nvkm_ramht_new(struct nvkm_object *, struct nvkm_object *, u32 size, + u32 align, struct nvkm_ramht **); + +static inline void +nvkm_ramht_ref(struct nvkm_ramht *obj, struct nvkm_ramht **ref) +{ + nvkm_gpuobj_ref(&obj->gpuobj, (struct nvkm_gpuobj **)ref); +} +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/subdev.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/subdev.h new file mode 100644 index 000000000..6fdc39116 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/core/subdev.h @@ -0,0 +1,119 @@ +#ifndef __NVKM_SUBDEV_H__ +#define __NVKM_SUBDEV_H__ +#include +#include + +#define NV_SUBDEV_(sub,var) (NV_SUBDEV_CLASS | ((var) << 8) | (sub)) +#define NV_SUBDEV(name,var) NV_SUBDEV_(NVDEV_SUBDEV_##name, (var)) + +struct nvkm_subdev { + struct nvkm_object object; + struct mutex mutex; + const char *name; + void __iomem *mmio; + u32 debug; + u32 unit; + + void (*intr)(struct nvkm_subdev *); +}; + +static inline struct nvkm_subdev * +nv_subdev(void *obj) +{ +#if CONFIG_NOUVEAU_DEBUG >= NV_DBG_PARANOIA + if (unlikely(!nv_iclass(obj, NV_SUBDEV_CLASS))) + nv_assert("BAD CAST -> NvSubDev, %08x", nv_hclass(obj)); +#endif + return obj; +} + +static inline int +nv_subidx(struct nvkm_subdev *subdev) +{ + return nv_hclass(subdev) & 0xff; +} + +struct nvkm_subdev *nvkm_subdev(void *obj, int idx); + +#define nvkm_subdev_create(p,e,o,v,s,f,d) \ + nvkm_subdev_create_((p), (e), (o), (v), (s), (f), \ + sizeof(**d),(void **)d) + +int nvkm_subdev_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, u32 pclass, + const char *sname, const char *fname, + int size, void **); +void nvkm_subdev_destroy(struct nvkm_subdev *); +int nvkm_subdev_init(struct nvkm_subdev *); +int nvkm_subdev_fini(struct nvkm_subdev *, bool suspend); +void nvkm_subdev_reset(struct nvkm_object *); + +void _nvkm_subdev_dtor(struct nvkm_object *); +int _nvkm_subdev_init(struct nvkm_object *); +int _nvkm_subdev_fini(struct nvkm_object *, bool suspend); + +#define s_printk(s,l,f,a...) do { \ + if ((s)->debug >= OS_DBG_##l) { \ + nv_printk((s)->base.parent, (s)->name, l, f, ##a); \ + } \ +} while(0) + +static inline u8 +nv_rd08(void *obj, u32 addr) +{ + struct nvkm_subdev *subdev = nv_subdev(obj); + u8 data = ioread8(subdev->mmio + addr); + nv_spam(subdev, "nv_rd08 0x%06x 0x%02x\n", addr, data); + return data; +} + +static inline u16 +nv_rd16(void *obj, u32 addr) +{ + struct nvkm_subdev *subdev = nv_subdev(obj); + u16 data = ioread16_native(subdev->mmio + addr); + nv_spam(subdev, "nv_rd16 0x%06x 0x%04x\n", addr, data); + return data; +} + +static inline u32 +nv_rd32(void *obj, u32 addr) +{ + struct nvkm_subdev *subdev = nv_subdev(obj); + u32 data = ioread32_native(subdev->mmio + addr); + nv_spam(subdev, "nv_rd32 0x%06x 0x%08x\n", addr, data); + return data; +} + +static inline void +nv_wr08(void *obj, u32 addr, u8 data) +{ + struct nvkm_subdev *subdev = nv_subdev(obj); + nv_spam(subdev, "nv_wr08 0x%06x 0x%02x\n", addr, data); + iowrite8(data, subdev->mmio + addr); +} + +static inline void +nv_wr16(void *obj, u32 addr, u16 data) +{ + struct nvkm_subdev *subdev = nv_subdev(obj); + nv_spam(subdev, "nv_wr16 0x%06x 0x%04x\n", addr, data); + iowrite16_native(data, subdev->mmio + addr); +} + +static inline void +nv_wr32(void *obj, u32 addr, u32 data) +{ + struct nvkm_subdev *subdev = nv_subdev(obj); + nv_spam(subdev, "nv_wr32 0x%06x 0x%08x\n", addr, data); + iowrite32_native(data, subdev->mmio + addr); +} + +static inline u32 +nv_mask(void *obj, u32 addr, u32 mask, u32 data) +{ + u32 temp = nv_rd32(obj, addr); + nv_wr32(obj, addr, (temp & ~mask) | data); + return temp; +} +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/bsp.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/bsp.h new file mode 100644 index 000000000..e489beef2 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/bsp.h @@ -0,0 +1,5 @@ +#ifndef __NVKM_BSP_H__ +#define __NVKM_BSP_H__ +#include +extern struct nvkm_oclass g84_bsp_oclass; +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/ce.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/ce.h new file mode 100644 index 000000000..e832f729e --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/ce.h @@ -0,0 +1,16 @@ +#ifndef __NVKM_CE_H__ +#define __NVKM_CE_H__ +#include + +void gt215_ce_intr(struct nvkm_subdev *); + +extern struct nvkm_oclass gt215_ce_oclass; +extern struct nvkm_oclass gf100_ce0_oclass; +extern struct nvkm_oclass gf100_ce1_oclass; +extern struct nvkm_oclass gk104_ce0_oclass; +extern struct nvkm_oclass gk104_ce1_oclass; +extern struct nvkm_oclass gk104_ce2_oclass; +extern struct nvkm_oclass gm204_ce0_oclass; +extern struct nvkm_oclass gm204_ce1_oclass; +extern struct nvkm_oclass gm204_ce2_oclass; +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/cipher.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/cipher.h new file mode 100644 index 000000000..57c29e91b --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/cipher.h @@ -0,0 +1,5 @@ +#ifndef __NVKM_CIPHER_H__ +#define __NVKM_CIPHER_H__ +#include +extern struct nvkm_oclass g84_cipher_oclass; +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/device.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/device.h new file mode 100644 index 000000000..5d4805e67 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/device.h @@ -0,0 +1,30 @@ +#ifndef __NOUVEAU_SUBDEV_DEVICE_H__ +#define __NOUVEAU_SUBDEV_DEVICE_H__ + +#include + +struct platform_device; + +enum nv_bus_type { + NOUVEAU_BUS_PCI, + NOUVEAU_BUS_PLATFORM, +}; + +#define nouveau_device_create(p,t,n,s,c,d,u) \ + nouveau_device_create_((void *)(p), (t), (n), (s), (c), (d), \ + sizeof(**u), (void **)u) + +int nouveau_device_create_(void *, enum nv_bus_type type, u64 name, + const char *sname, const char *cfg, const char *dbg, + int, void **); + +int nv04_identify(struct nouveau_device *); +int nv10_identify(struct nouveau_device *); +int nv20_identify(struct nouveau_device *); +int nv30_identify(struct nouveau_device *); +int nv40_identify(struct nouveau_device *); +int nv50_identify(struct nouveau_device *); +int nvc0_identify(struct nouveau_device *); +int nve0_identify(struct nouveau_device *); +int gm100_identify(struct nouveau_device *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/disp.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/disp.h new file mode 100644 index 000000000..a5e1ed813 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/disp.h @@ -0,0 +1,32 @@ +#ifndef __NVKM_DISP_H__ +#define __NVKM_DISP_H__ +#include +#include + +struct nvkm_disp { + struct nvkm_engine base; + + struct list_head outp; + + struct nvkm_event hpd; + struct nvkm_event vblank; +}; + +static inline struct nvkm_disp * +nvkm_disp(void *obj) +{ + return (void *)nvkm_engine(obj, NVDEV_ENGINE_DISP); +} + +extern struct nvkm_oclass *nv04_disp_oclass; +extern struct nvkm_oclass *nv50_disp_oclass; +extern struct nvkm_oclass *g84_disp_oclass; +extern struct nvkm_oclass *gt200_disp_oclass; +extern struct nvkm_oclass *g94_disp_oclass; +extern struct nvkm_oclass *gt215_disp_oclass; +extern struct nvkm_oclass *gf110_disp_oclass; +extern struct nvkm_oclass *gk104_disp_oclass; +extern struct nvkm_oclass *gk110_disp_oclass; +extern struct nvkm_oclass *gm107_disp_oclass; +extern struct nvkm_oclass *gm204_disp_oclass; +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/dmaobj.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/dmaobj.h new file mode 100644 index 000000000..c4fce8afc --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/dmaobj.h @@ -0,0 +1,26 @@ +#ifndef __NVKM_DMAOBJ_H__ +#define __NVKM_DMAOBJ_H__ +#include +struct nvkm_gpuobj; + +struct nvkm_dmaobj { + struct nvkm_object base; + u32 target; + u32 access; + u64 start; + u64 limit; +}; + +struct nvkm_dmaeng { + struct nvkm_engine base; + + /* creates a "physical" dma object from a struct nvkm_dmaobj */ + int (*bind)(struct nvkm_dmaobj *dmaobj, struct nvkm_object *parent, + struct nvkm_gpuobj **); +}; + +extern struct nvkm_oclass *nv04_dmaeng_oclass; +extern struct nvkm_oclass *nv50_dmaeng_oclass; +extern struct nvkm_oclass *gf100_dmaeng_oclass; +extern struct nvkm_oclass *gf110_dmaeng_oclass; +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/falcon.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/falcon.h new file mode 100644 index 000000000..bd38cf913 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/falcon.h @@ -0,0 +1,81 @@ +#ifndef __NVKM_FALCON_H__ +#define __NVKM_FALCON_H__ +#include + +struct nvkm_falcon_chan { + struct nvkm_engctx base; +}; + +#define nvkm_falcon_context_create(p,e,c,g,s,a,f,d) \ + nvkm_engctx_create((p), (e), (c), (g), (s), (a), (f), (d)) +#define nvkm_falcon_context_destroy(d) \ + nvkm_engctx_destroy(&(d)->base) +#define nvkm_falcon_context_init(d) \ + nvkm_engctx_init(&(d)->base) +#define nvkm_falcon_context_fini(d,s) \ + nvkm_engctx_fini(&(d)->base, (s)) + +#define _nvkm_falcon_context_ctor _nvkm_engctx_ctor +#define _nvkm_falcon_context_dtor _nvkm_engctx_dtor +#define _nvkm_falcon_context_init _nvkm_engctx_init +#define _nvkm_falcon_context_fini _nvkm_engctx_fini +#define _nvkm_falcon_context_rd32 _nvkm_engctx_rd32 +#define _nvkm_falcon_context_wr32 _nvkm_engctx_wr32 + +struct nvkm_falcon_data { + bool external; +}; + +#include + +struct nvkm_falcon { + struct nvkm_engine base; + + u32 addr; + u8 version; + u8 secret; + + struct nvkm_gpuobj *core; + bool external; + + struct { + u32 limit; + u32 *data; + u32 size; + } code; + + struct { + u32 limit; + u32 *data; + u32 size; + } data; +}; + +#define nv_falcon(priv) (&(priv)->base) + +#define nvkm_falcon_create(p,e,c,b,d,i,f,r) \ + nvkm_falcon_create_((p), (e), (c), (b), (d), (i), (f), \ + sizeof(**r),(void **)r) +#define nvkm_falcon_destroy(p) \ + nvkm_engine_destroy(&(p)->base) +#define nvkm_falcon_init(p) ({ \ + struct nvkm_falcon *falcon = (p); \ + _nvkm_falcon_init(nv_object(falcon)); \ +}) +#define nvkm_falcon_fini(p,s) ({ \ + struct nvkm_falcon *falcon = (p); \ + _nvkm_falcon_fini(nv_object(falcon), (s)); \ +}) + +int nvkm_falcon_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, u32, bool, const char *, + const char *, int, void **); + +void nvkm_falcon_intr(struct nvkm_subdev *subdev); + +#define _nvkm_falcon_dtor _nvkm_engine_dtor +int _nvkm_falcon_init(struct nvkm_object *); +int _nvkm_falcon_fini(struct nvkm_object *, bool); +u32 _nvkm_falcon_rd32(struct nvkm_object *, u64); +void _nvkm_falcon_wr32(struct nvkm_object *, u64, u32); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/fifo.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/fifo.h new file mode 100644 index 000000000..97cdeab8e --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/fifo.h @@ -0,0 +1,127 @@ +#ifndef __NVKM_FIFO_H__ +#define __NVKM_FIFO_H__ +#include + +struct nvkm_fifo_chan { + struct nvkm_namedb namedb; + struct nvkm_dmaobj *pushdma; + struct nvkm_gpuobj *pushgpu; + void __iomem *user; + u64 addr; + u32 size; + u16 chid; + atomic_t refcnt; /* NV04_NVSW_SET_REF */ +}; + +static inline struct nvkm_fifo_chan * +nvkm_fifo_chan(void *obj) +{ + return (void *)nv_namedb(obj); +} + +#define nvkm_fifo_channel_create(p,e,c,b,a,s,n,m,d) \ + nvkm_fifo_channel_create_((p), (e), (c), (b), (a), (s), (n), \ + (m), sizeof(**d), (void **)d) +#define nvkm_fifo_channel_init(p) \ + nvkm_namedb_init(&(p)->namedb) +#define nvkm_fifo_channel_fini(p,s) \ + nvkm_namedb_fini(&(p)->namedb, (s)) + +int nvkm_fifo_channel_create_(struct nvkm_object *, + struct nvkm_object *, + struct nvkm_oclass *, + int bar, u32 addr, u32 size, u32 push, + u64 engmask, int len, void **); +void nvkm_fifo_channel_destroy(struct nvkm_fifo_chan *); + +#define _nvkm_fifo_channel_init _nvkm_namedb_init +#define _nvkm_fifo_channel_fini _nvkm_namedb_fini + +void _nvkm_fifo_channel_dtor(struct nvkm_object *); +int _nvkm_fifo_channel_map(struct nvkm_object *, u64 *, u32 *); +u32 _nvkm_fifo_channel_rd32(struct nvkm_object *, u64); +void _nvkm_fifo_channel_wr32(struct nvkm_object *, u64, u32); +int _nvkm_fifo_channel_ntfy(struct nvkm_object *, u32, struct nvkm_event **); + +#include + +struct nvkm_fifo_base { + struct nvkm_gpuobj gpuobj; +}; + +#define nvkm_fifo_context_create(p,e,c,g,s,a,f,d) \ + nvkm_gpuobj_create((p), (e), (c), 0, (g), (s), (a), (f), (d)) +#define nvkm_fifo_context_destroy(p) \ + nvkm_gpuobj_destroy(&(p)->gpuobj) +#define nvkm_fifo_context_init(p) \ + nvkm_gpuobj_init(&(p)->gpuobj) +#define nvkm_fifo_context_fini(p,s) \ + nvkm_gpuobj_fini(&(p)->gpuobj, (s)) + +#define _nvkm_fifo_context_dtor _nvkm_gpuobj_dtor +#define _nvkm_fifo_context_init _nvkm_gpuobj_init +#define _nvkm_fifo_context_fini _nvkm_gpuobj_fini +#define _nvkm_fifo_context_rd32 _nvkm_gpuobj_rd32 +#define _nvkm_fifo_context_wr32 _nvkm_gpuobj_wr32 + +#include +#include + +struct nvkm_fifo { + struct nvkm_engine base; + + struct nvkm_event cevent; /* channel creation event */ + struct nvkm_event uevent; /* async user trigger */ + + struct nvkm_object **channel; + spinlock_t lock; + u16 min; + u16 max; + + int (*chid)(struct nvkm_fifo *, struct nvkm_object *); + void (*pause)(struct nvkm_fifo *, unsigned long *); + void (*start)(struct nvkm_fifo *, unsigned long *); +}; + +static inline struct nvkm_fifo * +nvkm_fifo(void *obj) +{ + return (void *)nvkm_engine(obj, NVDEV_ENGINE_FIFO); +} + +#define nvkm_fifo_create(o,e,c,fc,lc,d) \ + nvkm_fifo_create_((o), (e), (c), (fc), (lc), sizeof(**d), (void **)d) +#define nvkm_fifo_init(p) \ + nvkm_engine_init(&(p)->base) +#define nvkm_fifo_fini(p,s) \ + nvkm_engine_fini(&(p)->base, (s)) + +int nvkm_fifo_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, int min, int max, + int size, void **); +void nvkm_fifo_destroy(struct nvkm_fifo *); +const char * +nvkm_client_name_for_fifo_chid(struct nvkm_fifo *fifo, u32 chid); + +#define _nvkm_fifo_init _nvkm_engine_init +#define _nvkm_fifo_fini _nvkm_engine_fini + +extern struct nvkm_oclass *nv04_fifo_oclass; +extern struct nvkm_oclass *nv10_fifo_oclass; +extern struct nvkm_oclass *nv17_fifo_oclass; +extern struct nvkm_oclass *nv40_fifo_oclass; +extern struct nvkm_oclass *nv50_fifo_oclass; +extern struct nvkm_oclass *g84_fifo_oclass; +extern struct nvkm_oclass *gf100_fifo_oclass; +extern struct nvkm_oclass *gk104_fifo_oclass; +extern struct nvkm_oclass *gk20a_fifo_oclass; +extern struct nvkm_oclass *gk208_fifo_oclass; +extern struct nvkm_oclass *gm204_fifo_oclass; + +int nvkm_fifo_uevent_ctor(struct nvkm_object *, void *, u32, + struct nvkm_notify *); +void nvkm_fifo_uevent(struct nvkm_fifo *); + +void nv04_fifo_intr(struct nvkm_subdev *); +int nv04_fifo_context_attach(struct nvkm_object *, struct nvkm_object *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/gr.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/gr.h new file mode 100644 index 000000000..7cbe20280 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/gr.h @@ -0,0 +1,88 @@ +#ifndef __NVKM_GR_H__ +#define __NVKM_GR_H__ +#include + +struct nvkm_gr_chan { + struct nvkm_engctx base; +}; + +#define nvkm_gr_context_create(p,e,c,g,s,a,f,d) \ + nvkm_engctx_create((p), (e), (c), (g), (s), (a), (f), (d)) +#define nvkm_gr_context_destroy(d) \ + nvkm_engctx_destroy(&(d)->base) +#define nvkm_gr_context_init(d) \ + nvkm_engctx_init(&(d)->base) +#define nvkm_gr_context_fini(d,s) \ + nvkm_engctx_fini(&(d)->base, (s)) + +#define _nvkm_gr_context_dtor _nvkm_engctx_dtor +#define _nvkm_gr_context_init _nvkm_engctx_init +#define _nvkm_gr_context_fini _nvkm_engctx_fini +#define _nvkm_gr_context_rd32 _nvkm_engctx_rd32 +#define _nvkm_gr_context_wr32 _nvkm_engctx_wr32 + +#include + +struct nvkm_gr { + struct nvkm_engine base; + + /* Returns chipset-specific counts of units packed into an u64. + */ + u64 (*units)(struct nvkm_gr *); +}; + +static inline struct nvkm_gr * +nvkm_gr(void *obj) +{ + return (void *)nvkm_engine(obj, NVDEV_ENGINE_GR); +} + +#define nvkm_gr_create(p,e,c,y,d) \ + nvkm_engine_create((p), (e), (c), (y), "PGRAPH", "graphics", (d)) +#define nvkm_gr_destroy(d) \ + nvkm_engine_destroy(&(d)->base) +#define nvkm_gr_init(d) \ + nvkm_engine_init(&(d)->base) +#define nvkm_gr_fini(d,s) \ + nvkm_engine_fini(&(d)->base, (s)) + +#define _nvkm_gr_dtor _nvkm_engine_dtor +#define _nvkm_gr_init _nvkm_engine_init +#define _nvkm_gr_fini _nvkm_engine_fini + +extern struct nvkm_oclass nv04_gr_oclass; +extern struct nvkm_oclass nv10_gr_oclass; +extern struct nvkm_oclass nv20_gr_oclass; +extern struct nvkm_oclass nv25_gr_oclass; +extern struct nvkm_oclass nv2a_gr_oclass; +extern struct nvkm_oclass nv30_gr_oclass; +extern struct nvkm_oclass nv34_gr_oclass; +extern struct nvkm_oclass nv35_gr_oclass; +extern struct nvkm_oclass nv40_gr_oclass; +extern struct nvkm_oclass nv50_gr_oclass; +extern struct nvkm_oclass *gf100_gr_oclass; +extern struct nvkm_oclass *gf108_gr_oclass; +extern struct nvkm_oclass *gf104_gr_oclass; +extern struct nvkm_oclass *gf110_gr_oclass; +extern struct nvkm_oclass *gf117_gr_oclass; +extern struct nvkm_oclass *gf119_gr_oclass; +extern struct nvkm_oclass *gk104_gr_oclass; +extern struct nvkm_oclass *gk20a_gr_oclass; +extern struct nvkm_oclass *gk110_gr_oclass; +extern struct nvkm_oclass *gk110b_gr_oclass; +extern struct nvkm_oclass *gk208_gr_oclass; +extern struct nvkm_oclass *gm107_gr_oclass; +extern struct nvkm_oclass *gm204_gr_oclass; +extern struct nvkm_oclass *gm206_gr_oclass; + +#include + +extern const struct nvkm_bitfield nv04_gr_nsource[]; +extern struct nvkm_ofuncs nv04_gr_ofuncs; +bool nv04_gr_idle(void *obj); + +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/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/mpeg.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/mpeg.h new file mode 100644 index 000000000..4e500b398 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/mpeg.h @@ -0,0 +1,62 @@ +#ifndef __NVKM_MPEG_H__ +#define __NVKM_MPEG_H__ +#include + +struct nvkm_mpeg_chan { + struct nvkm_engctx base; +}; + +#define nvkm_mpeg_context_create(p,e,c,g,s,a,f,d) \ + nvkm_engctx_create((p), (e), (c), (g), (s), (a), (f), (d)) +#define nvkm_mpeg_context_destroy(d) \ + nvkm_engctx_destroy(&(d)->base) +#define nvkm_mpeg_context_init(d) \ + nvkm_engctx_init(&(d)->base) +#define nvkm_mpeg_context_fini(d,s) \ + nvkm_engctx_fini(&(d)->base, (s)) + +#define _nvkm_mpeg_context_dtor _nvkm_engctx_dtor +#define _nvkm_mpeg_context_init _nvkm_engctx_init +#define _nvkm_mpeg_context_fini _nvkm_engctx_fini +#define _nvkm_mpeg_context_rd32 _nvkm_engctx_rd32 +#define _nvkm_mpeg_context_wr32 _nvkm_engctx_wr32 + +#include + +struct nvkm_mpeg { + struct nvkm_engine base; +}; + +#define nvkm_mpeg_create(p,e,c,d) \ + nvkm_engine_create((p), (e), (c), true, "PMPEG", "mpeg", (d)) +#define nvkm_mpeg_destroy(d) \ + nvkm_engine_destroy(&(d)->base) +#define nvkm_mpeg_init(d) \ + nvkm_engine_init(&(d)->base) +#define nvkm_mpeg_fini(d,s) \ + nvkm_engine_fini(&(d)->base, (s)) + +#define _nvkm_mpeg_dtor _nvkm_engine_dtor +#define _nvkm_mpeg_init _nvkm_engine_init +#define _nvkm_mpeg_fini _nvkm_engine_fini + +extern struct nvkm_oclass nv31_mpeg_oclass; +extern struct nvkm_oclass nv40_mpeg_oclass; +extern struct nvkm_oclass nv44_mpeg_oclass; +extern struct nvkm_oclass nv50_mpeg_oclass; +extern struct nvkm_oclass g84_mpeg_oclass; +extern struct nvkm_ofuncs nv31_mpeg_ofuncs; +extern struct nvkm_oclass nv31_mpeg_cclass; +extern struct nvkm_oclass nv31_mpeg_sclass[]; +extern struct nvkm_oclass nv40_mpeg_sclass[]; +void nv31_mpeg_intr(struct nvkm_subdev *); +void nv31_mpeg_tile_prog(struct nvkm_engine *, int); +int nv31_mpeg_init(struct nvkm_object *); + +extern struct nvkm_ofuncs nv50_mpeg_ofuncs; +int nv50_mpeg_context_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); +void nv50_mpeg_intr(struct nvkm_subdev *); +int nv50_mpeg_init(struct nvkm_object *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/mspdec.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/mspdec.h new file mode 100644 index 000000000..54b7672ee --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/mspdec.h @@ -0,0 +1,7 @@ +#ifndef __NVKM_MSPDEC_H__ +#define __NVKM_MSPDEC_H__ +#include +extern struct nvkm_oclass g98_mspdec_oclass; +extern struct nvkm_oclass gf100_mspdec_oclass; +extern struct nvkm_oclass gk104_mspdec_oclass; +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/msppp.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/msppp.h new file mode 100644 index 000000000..c6c69d0a8 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/msppp.h @@ -0,0 +1,6 @@ +#ifndef __NVKM_MSPPP_H__ +#define __NVKM_MSPPP_H__ +#include +extern struct nvkm_oclass g98_msppp_oclass; +extern struct nvkm_oclass gf100_msppp_oclass; +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/msvld.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/msvld.h new file mode 100644 index 000000000..1f193b7bd --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/msvld.h @@ -0,0 +1,7 @@ +#ifndef __NVKM_MSVLD_H__ +#define __NVKM_MSVLD_H__ +#include +extern struct nvkm_oclass g98_msvld_oclass; +extern struct nvkm_oclass gf100_msvld_oclass; +extern struct nvkm_oclass gk104_msvld_oclass; +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/pm.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/pm.h new file mode 100644 index 000000000..93181bbf0 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/pm.h @@ -0,0 +1,34 @@ +#ifndef __NVKM_PM_H__ +#define __NVKM_PM_H__ +#include + +struct nvkm_perfdom; +struct nvkm_perfctr; +struct nvkm_pm { + struct nvkm_engine base; + + struct nvkm_perfctx *context; + void *profile_data; + + struct list_head domains; + u32 sequence; + + /*XXX: temp for daemon backend */ + u32 pwr[8]; + u32 last; +}; + +static inline struct nvkm_pm * +nvkm_pm(void *obj) +{ + return (void *)nvkm_engine(obj, NVDEV_ENGINE_PM); +} + +extern struct nvkm_oclass *nv40_pm_oclass; +extern struct nvkm_oclass *nv50_pm_oclass; +extern struct nvkm_oclass *g84_pm_oclass; +extern struct nvkm_oclass *gt215_pm_oclass; +extern struct nvkm_oclass gf100_pm_oclass; +extern struct nvkm_oclass gk104_pm_oclass; +extern struct nvkm_oclass gk110_pm_oclass; +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/sec.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/sec.h new file mode 100644 index 000000000..44590a2a4 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/sec.h @@ -0,0 +1,5 @@ +#ifndef __NVKM_SEC_H__ +#define __NVKM_SEC_H__ +#include +extern struct nvkm_oclass g98_sec_oclass; +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/sw.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/sw.h new file mode 100644 index 000000000..a529013c9 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/sw.h @@ -0,0 +1,50 @@ +#ifndef __NVKM_SW_H__ +#define __NVKM_SW_H__ +#include + +struct nvkm_sw_chan { + struct nvkm_engctx base; + + int (*flip)(void *); + void *flip_data; +}; + +#define nvkm_sw_context_create(p,e,c,d) \ + nvkm_engctx_create((p), (e), (c), (p), 0, 0, 0, (d)) +#define nvkm_sw_context_destroy(d) \ + nvkm_engctx_destroy(&(d)->base) +#define nvkm_sw_context_init(d) \ + nvkm_engctx_init(&(d)->base) +#define nvkm_sw_context_fini(d,s) \ + nvkm_engctx_fini(&(d)->base, (s)) + +#define _nvkm_sw_context_dtor _nvkm_engctx_dtor +#define _nvkm_sw_context_init _nvkm_engctx_init +#define _nvkm_sw_context_fini _nvkm_engctx_fini + +#include + +struct nvkm_sw { + struct nvkm_engine base; +}; + +#define nvkm_sw_create(p,e,c,d) \ + nvkm_engine_create((p), (e), (c), true, "SW", "software", (d)) +#define nvkm_sw_destroy(d) \ + nvkm_engine_destroy(&(d)->base) +#define nvkm_sw_init(d) \ + nvkm_engine_init(&(d)->base) +#define nvkm_sw_fini(d,s) \ + nvkm_engine_fini(&(d)->base, (s)) + +#define _nvkm_sw_dtor _nvkm_engine_dtor +#define _nvkm_sw_init _nvkm_engine_init +#define _nvkm_sw_fini _nvkm_engine_fini + +extern struct nvkm_oclass *nv04_sw_oclass; +extern struct nvkm_oclass *nv10_sw_oclass; +extern struct nvkm_oclass *nv50_sw_oclass; +extern struct nvkm_oclass *gf100_sw_oclass; + +void nv04_sw_intr(struct nvkm_subdev *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/vp.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/vp.h new file mode 100644 index 000000000..7851f18c5 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/vp.h @@ -0,0 +1,5 @@ +#ifndef __NVKM_VP_H__ +#define __NVKM_VP_H__ +#include +extern struct nvkm_oclass g84_vp_oclass; +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/xtensa.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/xtensa.h new file mode 100644 index 000000000..7a216cca2 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/engine/xtensa.h @@ -0,0 +1,35 @@ +#ifndef __NVKM_XTENSA_H__ +#define __NVKM_XTENSA_H__ +#include +struct nvkm_gpuobj; + +struct nvkm_xtensa { + struct nvkm_engine base; + + u32 addr; + struct nvkm_gpuobj *gpu_fw; + u32 fifo_val; + u32 unkd28; +}; + +#define nvkm_xtensa_create(p,e,c,b,d,i,f,r) \ + nvkm_xtensa_create_((p), (e), (c), (b), (d), (i), (f), \ + sizeof(**r),(void **)r) + +int _nvkm_xtensa_engctx_ctor(struct nvkm_object *, + struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); + +void _nvkm_xtensa_intr(struct nvkm_subdev *); +int nvkm_xtensa_create_(struct nvkm_object *, + struct nvkm_object *, + struct nvkm_oclass *, u32, bool, + const char *, const char *, + int, void **); +#define _nvkm_xtensa_dtor _nvkm_engine_dtor +int _nvkm_xtensa_init(struct nvkm_object *); +int _nvkm_xtensa_fini(struct nvkm_object *, bool); +u32 _nvkm_xtensa_rd32(struct nvkm_object *, u64); +void _nvkm_xtensa_wr32(struct nvkm_object *, u64, u32); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bar.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bar.h new file mode 100644 index 000000000..c7a007b8b --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bar.h @@ -0,0 +1,33 @@ +#ifndef __NVKM_BAR_H__ +#define __NVKM_BAR_H__ +#include +struct nvkm_mem; +struct nvkm_vma; + +struct nvkm_bar { + struct nvkm_subdev base; + + int (*alloc)(struct nvkm_bar *, struct nvkm_object *, + struct nvkm_mem *, struct nvkm_object **); + + int (*kmap)(struct nvkm_bar *, struct nvkm_mem *, u32 flags, + struct nvkm_vma *); + int (*umap)(struct nvkm_bar *, struct nvkm_mem *, u32 flags, + struct nvkm_vma *); + void (*unmap)(struct nvkm_bar *, struct nvkm_vma *); + void (*flush)(struct nvkm_bar *); + + /* whether the BAR supports to be ioremapped WC or should be uncached */ + bool iomap_uncached; +}; + +static inline struct nvkm_bar * +nvkm_bar(void *obj) +{ + return (void *)nvkm_subdev(obj, NVDEV_SUBDEV_BAR); +} + +extern struct nvkm_oclass nv50_bar_oclass; +extern struct nvkm_oclass gf100_bar_oclass; +extern struct nvkm_oclass gk20a_bar_oclass; +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios.h new file mode 100644 index 000000000..cef287e0b --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios.h @@ -0,0 +1,32 @@ +#ifndef __NVKM_BIOS_H__ +#define __NVKM_BIOS_H__ +#include + +struct nvkm_bios { + struct nvkm_subdev base; + u32 size; + u8 *data; + + u32 bmp_offset; + u32 bit_offset; + + struct { + u8 major; + u8 chip; + u8 minor; + u8 micro; + u8 patch; + } version; +}; + +static inline struct nvkm_bios * +nvkm_bios(void *obj) +{ + return (void *)nvkm_subdev(obj, NVDEV_SUBDEV_VBIOS); +} + +u8 nvbios_checksum(const u8 *data, int size); +u16 nvbios_findstr(const u8 *data, int size, const char *str, int len); + +extern struct nvkm_oclass nvkm_bios_oclass; +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/M0203.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/M0203.h new file mode 100644 index 000000000..cf202c793 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/M0203.h @@ -0,0 +1,29 @@ +#ifndef __NVBIOS_M0203_H__ +#define __NVBIOS_M0203_H__ +struct nvbios_M0203T { +#define M0203T_TYPE_RAMCFG 0x00 + u8 type; + u16 pointer; +}; + +u32 nvbios_M0203Te(struct nvkm_bios *, u8 *ver, u8 *hdr, u8 *cnt, u8 *len); +u32 nvbios_M0203Tp(struct nvkm_bios *, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_M0203T *); + +struct nvbios_M0203E { +#define M0203E_TYPE_DDR2 0x0 +#define M0203E_TYPE_DDR3 0x1 +#define M0203E_TYPE_GDDR3 0x2 +#define M0203E_TYPE_GDDR5 0x3 +#define M0203E_TYPE_SKIP 0xf + u8 type; + u8 strap; + u8 group; +}; + +u32 nvbios_M0203Ee(struct nvkm_bios *, int idx, u8 *ver, u8 *hdr); +u32 nvbios_M0203Ep(struct nvkm_bios *, int idx, u8 *ver, u8 *hdr, + struct nvbios_M0203E *); +u32 nvbios_M0203Em(struct nvkm_bios *, u8 ramcfg, u8 *ver, u8 *hdr, + struct nvbios_M0203E *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/M0205.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/M0205.h new file mode 100644 index 000000000..d34608ff2 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/M0205.h @@ -0,0 +1,29 @@ +#ifndef __NVBIOS_M0205_H__ +#define __NVBIOS_M0205_H__ +struct nvbios_M0205T { + u16 freq; +}; + +u32 nvbios_M0205Te(struct nvkm_bios *, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz); +u32 nvbios_M0205Tp(struct nvkm_bios *, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz, + struct nvbios_M0205T *); + +struct nvbios_M0205E { + u8 type; +}; + +u32 nvbios_M0205Ee(struct nvkm_bios *, int idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len); +u32 nvbios_M0205Ep(struct nvkm_bios *, int idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_M0205E *); + +struct nvbios_M0205S { + u8 data; +}; + +u32 nvbios_M0205Se(struct nvkm_bios *, int ent, int idx, u8 *ver, u8 *hdr); +u32 nvbios_M0205Sp(struct nvkm_bios *, int ent, int idx, u8 *ver, u8 *hdr, + struct nvbios_M0205S *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/M0209.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/M0209.h new file mode 100644 index 000000000..c7ff8d952 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/M0209.h @@ -0,0 +1,27 @@ +#ifndef __NVBIOS_M0209_H__ +#define __NVBIOS_M0209_H__ +u32 nvbios_M0209Te(struct nvkm_bios *, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz); + +struct nvbios_M0209E { + u8 v00_40; + u8 bits; + u8 modulo; + u8 v02_40; + u8 v02_07; + u8 v03; +}; + +u32 nvbios_M0209Ee(struct nvkm_bios *, int idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len); +u32 nvbios_M0209Ep(struct nvkm_bios *, int idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_M0209E *); + +struct nvbios_M0209S { + u32 data[0x200]; +}; + +u32 nvbios_M0209Se(struct nvkm_bios *, int ent, int idx, u8 *ver, u8 *hdr); +u32 nvbios_M0209Sp(struct nvkm_bios *, int ent, int idx, u8 *ver, u8 *hdr, + struct nvbios_M0209S *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/P0260.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/P0260.h new file mode 100644 index 000000000..1c1c52eac --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/P0260.h @@ -0,0 +1,21 @@ +#ifndef __NVBIOS_P0260_H__ +#define __NVBIOS_P0260_H__ +u32 nvbios_P0260Te(struct nvkm_bios *, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *xnr, u8 *xsz); + +struct nvbios_P0260E { + u32 data; +}; + +u32 nvbios_P0260Ee(struct nvkm_bios *, int idx, u8 *ver, u8 *hdr); +u32 nvbios_P0260Ep(struct nvkm_bios *, int idx, u8 *ver, u8 *hdr, + struct nvbios_P0260E *); + +struct nvbios_P0260X { + u32 data; +}; + +u32 nvbios_P0260Xe(struct nvkm_bios *, int idx, u8 *ver, u8 *hdr); +u32 nvbios_P0260Xp(struct nvkm_bios *, int idx, u8 *ver, u8 *hdr, + struct nvbios_P0260X *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/bit.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/bit.h new file mode 100644 index 000000000..6711732b7 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/bit.h @@ -0,0 +1,11 @@ +#ifndef __NVBIOS_BIT_H__ +#define __NVBIOS_BIT_H__ +struct bit_entry { + u8 id; + u8 version; + u16 length; + u16 offset; +}; + +int bit_entry(struct nvkm_bios *, u8 id, struct bit_entry *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/bmp.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/bmp.h new file mode 100644 index 000000000..4107aa546 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/bmp.h @@ -0,0 +1,37 @@ +#ifndef __NVBIOS_BMP_H__ +#define __NVBIOS_BMP_H__ +static inline u16 +bmp_version(struct nvkm_bios *bios) +{ + if (bios->bmp_offset) { + return nv_ro08(bios, bios->bmp_offset + 5) << 8 | + nv_ro08(bios, bios->bmp_offset + 6); + } + + return 0x0000; +} + +static inline u16 +bmp_mem_init_table(struct nvkm_bios *bios) +{ + if (bmp_version(bios) >= 0x0300) + return nv_ro16(bios, bios->bmp_offset + 24); + return 0x0000; +} + +static inline u16 +bmp_sdr_seq_table(struct nvkm_bios *bios) +{ + if (bmp_version(bios) >= 0x0300) + return nv_ro16(bios, bios->bmp_offset + 26); + return 0x0000; +} + +static inline u16 +bmp_ddr_seq_table(struct nvkm_bios *bios) +{ + if (bmp_version(bios) >= 0x0300) + return nv_ro16(bios, bios->bmp_offset + 28); + return 0x0000; +} +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/boost.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/boost.h new file mode 100644 index 000000000..934b0ae55 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/boost.h @@ -0,0 +1,27 @@ +#ifndef __NVBIOS_BOOST_H__ +#define __NVBIOS_BOOST_H__ +u16 nvbios_boostTe(struct nvkm_bios *, u8 *, u8 *, u8 *, u8 *, u8 *, u8 *); + +struct nvbios_boostE { + u8 pstate; + u32 min; + u32 max; +}; + +u16 nvbios_boostEe(struct nvkm_bios *, int idx, u8 *, u8 *, u8 *, u8 *); +u16 nvbios_boostEp(struct nvkm_bios *, int idx, u8 *, u8 *, u8 *, u8 *, + struct nvbios_boostE *); +u16 nvbios_boostEm(struct nvkm_bios *, u8, u8 *, u8 *, u8 *, u8 *, + struct nvbios_boostE *); + +struct nvbios_boostS { + u8 domain; + u8 percent; + u32 min; + u32 max; +}; + +u16 nvbios_boostSe(struct nvkm_bios *, int, u16, u8 *, u8 *, u8, u8); +u16 nvbios_boostSp(struct nvkm_bios *, int, u16, u8 *, u8 *, u8, u8, + struct nvbios_boostS *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/conn.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/conn.h new file mode 100644 index 000000000..e8e77ee24 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/conn.h @@ -0,0 +1,44 @@ +#ifndef __NVBIOS_CONN_H__ +#define __NVBIOS_CONN_H__ +enum dcb_connector_type { + DCB_CONNECTOR_VGA = 0x00, + DCB_CONNECTOR_TV_0 = 0x10, + DCB_CONNECTOR_TV_1 = 0x11, + DCB_CONNECTOR_TV_3 = 0x13, + DCB_CONNECTOR_DVI_I = 0x30, + DCB_CONNECTOR_DVI_D = 0x31, + DCB_CONNECTOR_DMS59_0 = 0x38, + DCB_CONNECTOR_DMS59_1 = 0x39, + DCB_CONNECTOR_LVDS = 0x40, + DCB_CONNECTOR_LVDS_SPWG = 0x41, + DCB_CONNECTOR_DP = 0x46, + DCB_CONNECTOR_eDP = 0x47, + DCB_CONNECTOR_HDMI_0 = 0x60, + DCB_CONNECTOR_HDMI_1 = 0x61, + DCB_CONNECTOR_HDMI_C = 0x63, + DCB_CONNECTOR_DMS59_DP0 = 0x64, + DCB_CONNECTOR_DMS59_DP1 = 0x65, + DCB_CONNECTOR_NONE = 0xff +}; + +struct nvbios_connT { +}; + +u32 nvbios_connTe(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len); +u32 nvbios_connTp(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_connT *info); + +struct nvbios_connE { + u8 type; + u8 location; + u8 hpd; + u8 dp; + u8 di; + u8 sr; + u8 lcdid; +}; + +u32 nvbios_connEe(struct nvkm_bios *bios, u8 idx, u8 *ver, u8 *hdr); +u32 nvbios_connEp(struct nvkm_bios *bios, u8 idx, u8 *ver, u8 *hdr, + struct nvbios_connE *info); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/cstep.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/cstep.h new file mode 100644 index 000000000..2f0e0c8e8 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/cstep.h @@ -0,0 +1,26 @@ +#ifndef __NVBIOS_CSTEP_H__ +#define __NVBIOS_CSTEP_H__ +u16 nvbios_cstepTe(struct nvkm_bios *, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *xnr, u8 *xsz); + +struct nvbios_cstepE { + u8 pstate; + u8 index; +}; + +u16 nvbios_cstepEe(struct nvkm_bios *, int idx, u8 *ver, u8 *hdr); +u16 nvbios_cstepEp(struct nvkm_bios *, int idx, u8 *ver, u8 *hdr, + struct nvbios_cstepE *); +u16 nvbios_cstepEm(struct nvkm_bios *, u8 pstate, u8 *ver, u8 *hdr, + struct nvbios_cstepE *); + +struct nvbios_cstepX { + u32 freq; + u8 unkn[2]; + u8 voltage; +}; + +u16 nvbios_cstepXe(struct nvkm_bios *, int idx, u8 *ver, u8 *hdr); +u16 nvbios_cstepXp(struct nvkm_bios *, int idx, u8 *ver, u8 *hdr, + struct nvbios_cstepX *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/dcb.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/dcb.h new file mode 100644 index 000000000..4892a65dd --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/dcb.h @@ -0,0 +1,65 @@ +#ifndef __NVBIOS_DCB_H__ +#define __NVBIOS_DCB_H__ +enum dcb_output_type { + DCB_OUTPUT_ANALOG = 0x0, + DCB_OUTPUT_TV = 0x1, + DCB_OUTPUT_TMDS = 0x2, + DCB_OUTPUT_LVDS = 0x3, + DCB_OUTPUT_DP = 0x6, + DCB_OUTPUT_EOL = 0xe, + DCB_OUTPUT_UNUSED = 0xf, + DCB_OUTPUT_ANY = -1, +}; + +struct dcb_output { + int index; /* may not be raw dcb index if merging has happened */ + u16 hasht; + u16 hashm; + enum dcb_output_type type; + uint8_t i2c_index; + uint8_t heads; + uint8_t connector; + uint8_t bus; + uint8_t location; + uint8_t or; + uint8_t link; + bool duallink_possible; + uint8_t extdev; + union { + struct sor_conf { + int link; + } sorconf; + struct { + int maxfreq; + } crtconf; + struct { + struct sor_conf sor; + bool use_straps_for_mode; + bool use_acpi_for_edid; + bool use_power_scripts; + } lvdsconf; + struct { + bool has_component_output; + } tvconf; + struct { + struct sor_conf sor; + int link_nr; + int link_bw; + } dpconf; + struct { + struct sor_conf sor; + int slave_addr; + } tmdsconf; + }; + bool i2c_upper_default; +}; + +u16 dcb_table(struct nvkm_bios *, u8 *ver, u8 *hdr, u8 *ent, u8 *len); +u16 dcb_outp(struct nvkm_bios *, u8 idx, u8 *ver, u8 *len); +u16 dcb_outp_parse(struct nvkm_bios *, u8 idx, u8 *, u8 *, + struct dcb_output *); +u16 dcb_outp_match(struct nvkm_bios *, u16 type, u16 mask, u8 *, u8 *, + struct dcb_output *); +int dcb_outp_foreach(struct nvkm_bios *, void *data, int (*exec) + (struct nvkm_bios *, void *, int index, u16 entry)); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/disp.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/disp.h new file mode 100644 index 000000000..db10c11f0 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/disp.h @@ -0,0 +1,39 @@ +#ifndef __NVBIOS_DISP_H__ +#define __NVBIOS_DISP_H__ +u16 nvbios_disp_table(struct nvkm_bios *, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *sub); + +struct nvbios_disp { + u16 data; +}; + +u16 nvbios_disp_entry(struct nvkm_bios *, u8 idx, u8 *ver, u8 *hdr, u8 *sub); +u16 nvbios_disp_parse(struct nvkm_bios *, u8 idx, u8 *ver, u8 *hdr, u8 *sub, + struct nvbios_disp *); + +struct nvbios_outp { + u16 type; + u16 mask; + u16 script[3]; +}; + +u16 nvbios_outp_entry(struct nvkm_bios *, u8 idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len); +u16 nvbios_outp_parse(struct nvkm_bios *, u8 idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_outp *); +u16 nvbios_outp_match(struct nvkm_bios *, u16 type, u16 mask, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_outp *); + +struct nvbios_ocfg { + u16 match; + u16 clkcmp[2]; +}; + +u16 nvbios_ocfg_entry(struct nvkm_bios *, u16 outp, u8 idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len); +u16 nvbios_ocfg_parse(struct nvkm_bios *, u16 outp, u8 idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_ocfg *); +u16 nvbios_ocfg_match(struct nvkm_bios *, u16 outp, u16 type, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_ocfg *); +u16 nvbios_oclk_match(struct nvkm_bios *, u16 cmp, u32 khz); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/dp.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/dp.h new file mode 100644 index 000000000..b4d39df70 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/dp.h @@ -0,0 +1,31 @@ +#ifndef __NVBIOS_DP_H__ +#define __NVBIOS_DP_H__ +struct nvbios_dpout { + u16 type; + u16 mask; + u8 flags; + u32 script[5]; + u32 lnkcmp; +}; + +u16 nvbios_dpout_parse(struct nvkm_bios *, u8 idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_dpout *); +u16 nvbios_dpout_match(struct nvkm_bios *, u16 type, u16 mask, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_dpout *); + +struct nvbios_dpcfg { + u8 pc; + u8 dc; + u8 pe; + u8 tx_pu; +}; + +u16 +nvbios_dpcfg_parse(struct nvkm_bios *, u16 outp, u8 idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_dpcfg *); +u16 +nvbios_dpcfg_match(struct nvkm_bios *, u16 outp, u8 pc, u8 vs, u8 pe, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_dpcfg *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/extdev.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/extdev.h new file mode 100644 index 000000000..6d3bedc63 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/extdev.h @@ -0,0 +1,25 @@ +#ifndef __NVBIOS_EXTDEV_H__ +#define __NVBIOS_EXTDEV_H__ +enum nvbios_extdev_type { + NVBIOS_EXTDEV_LM89 = 0x02, + NVBIOS_EXTDEV_VT1103M = 0x40, + NVBIOS_EXTDEV_PX3540 = 0x41, + NVBIOS_EXTDEV_VT1105M = 0x42, /* or close enough... */ + NVBIOS_EXTDEV_ADT7473 = 0x70, /* can also be a LM64 */ + NVBIOS_EXTDEV_HDCP_EEPROM = 0x90, + NVBIOS_EXTDEV_NONE = 0xff, +}; + +struct nvbios_extdev_func { + u8 type; + u8 addr; + u8 bus; +}; + +int +nvbios_extdev_parse(struct nvkm_bios *, int, struct nvbios_extdev_func *); + +int +nvbios_extdev_find(struct nvkm_bios *, enum nvbios_extdev_type, + struct nvbios_extdev_func *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/fan.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/fan.h new file mode 100644 index 000000000..693ea7d9e --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/fan.h @@ -0,0 +1,6 @@ +#ifndef __NVBIOS_FAN_H__ +#define __NVBIOS_FAN_H__ +#include + +u16 nvbios_fan_parse(struct nvkm_bios *bios, struct nvbios_therm_fan *fan); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/gpio.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/gpio.h new file mode 100644 index 000000000..33be260dd --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/gpio.h @@ -0,0 +1,46 @@ +#ifndef __NVBIOS_GPIO_H__ +#define __NVBIOS_GPIO_H__ +enum dcb_gpio_func_name { + DCB_GPIO_PANEL_POWER = 0x01, + DCB_GPIO_TVDAC0 = 0x0c, + DCB_GPIO_TVDAC1 = 0x2d, + DCB_GPIO_FAN = 0x09, + DCB_GPIO_FAN_SENSE = 0x3d, + DCB_GPIO_UNUSED = 0xff, + DCB_GPIO_VID0 = 0x04, + DCB_GPIO_VID1 = 0x05, + DCB_GPIO_VID2 = 0x06, + DCB_GPIO_VID3 = 0x1a, + DCB_GPIO_VID4 = 0x73, + DCB_GPIO_VID5 = 0x74, + DCB_GPIO_VID6 = 0x75, + DCB_GPIO_VID7 = 0x76, +}; + +#define DCB_GPIO_LOG_DIR 0x02 +#define DCB_GPIO_LOG_DIR_OUT 0x00 +#define DCB_GPIO_LOG_DIR_IN 0x02 +#define DCB_GPIO_LOG_VAL 0x01 +#define DCB_GPIO_LOG_VAL_LO 0x00 +#define DCB_GPIO_LOG_VAL_HI 0x01 + +struct dcb_gpio_func { + u8 func; + u8 line; + u8 log[2]; + + /* so far, "param" seems to only have an influence on PWM-related + * GPIOs such as FAN_CONTROL and PANEL_BACKLIGHT_LEVEL. + * if param equals 1, hardware PWM is available + * if param equals 0, the host should toggle the GPIO itself + */ + u8 param; +}; + +u16 dcb_gpio_table(struct nvkm_bios *, u8 *ver, u8 *hdr, u8 *cnt, u8 *len); +u16 dcb_gpio_entry(struct nvkm_bios *, int idx, int ent, u8 *ver, u8 *len); +u16 dcb_gpio_parse(struct nvkm_bios *, int idx, int ent, u8 *ver, u8 *len, + struct dcb_gpio_func *); +u16 dcb_gpio_match(struct nvkm_bios *, int idx, u8 func, u8 line, + u8 *ver, u8 *len, struct dcb_gpio_func *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/i2c.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/i2c.h new file mode 100644 index 000000000..85c529ecf --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/i2c.h @@ -0,0 +1,25 @@ +#ifndef __NVBIOS_I2C_H__ +#define __NVBIOS_I2C_H__ +enum dcb_i2c_type { + /* matches bios type field prior to ccb 4.1 */ + DCB_I2C_NV04_BIT = 0x00, + DCB_I2C_NV4E_BIT = 0x04, + DCB_I2C_NVIO_BIT = 0x05, + DCB_I2C_NVIO_AUX = 0x06, + /* made up - mostly */ + DCB_I2C_PMGR = 0x80, + DCB_I2C_UNUSED = 0xff +}; + +struct dcb_i2c_entry { + enum dcb_i2c_type type; + u8 drive; + u8 sense; + u8 share; + u8 auxch; +}; + +u16 dcb_i2c_table(struct nvkm_bios *, u8 *ver, u8 *hdr, u8 *cnt, u8 *len); +u16 dcb_i2c_entry(struct nvkm_bios *, u8 index, u8 *ver, u8 *len); +int dcb_i2c_parse(struct nvkm_bios *, u8 index, struct dcb_i2c_entry *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/image.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/image.h new file mode 100644 index 000000000..e15d63b9a --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/image.h @@ -0,0 +1,11 @@ +#ifndef __NVBIOS_IMAGE_H__ +#define __NVBIOS_IMAGE_H__ +struct nvbios_image { + u32 base; + u32 size; + u8 type; + bool last; +}; + +bool nvbios_image(struct nvkm_bios *, int, struct nvbios_image *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/init.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/init.h new file mode 100644 index 000000000..578a667ee --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/init.h @@ -0,0 +1,20 @@ +#ifndef __NVBIOS_INIT_H__ +#define __NVBIOS_INIT_H__ +struct nvbios_init { + struct nvkm_subdev *subdev; + struct nvkm_bios *bios; + u16 offset; + struct dcb_output *outp; + int crtc; + + /* internal state used during parsing */ + u8 execute; + u32 nested; + u16 repeat; + u16 repend; + u32 ramcfg; +}; + +int nvbios_exec(struct nvbios_init *); +int nvbios_init(struct nvkm_subdev *, bool execute); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/mxm.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/mxm.h new file mode 100644 index 000000000..4e31b64c5 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/mxm.h @@ -0,0 +1,6 @@ +#ifndef __NVBIOS_MXM_H__ +#define __NVBIOS_MXM_H__ +u16 mxm_table(struct nvkm_bios *, u8 *ver, u8 *hdr); +u8 mxm_sor_map(struct nvkm_bios *, u8 conn); +u8 mxm_ddc_map(struct nvkm_bios *, u8 port); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/npde.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/npde.h new file mode 100644 index 000000000..64a59549b --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/npde.h @@ -0,0 +1,10 @@ +#ifndef __NVBIOS_NPDE_H__ +#define __NVBIOS_NPDE_H__ +struct nvbios_npdeT { + u32 image_size; + bool last; +}; + +u32 nvbios_npdeTe(struct nvkm_bios *, u32); +u32 nvbios_npdeTp(struct nvkm_bios *, u32, struct nvbios_npdeT *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/pcir.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/pcir.h new file mode 100644 index 000000000..e85931541 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/pcir.h @@ -0,0 +1,16 @@ +#ifndef __NVBIOS_PCIR_H__ +#define __NVBIOS_PCIR_H__ +struct nvbios_pcirT { + u16 vendor_id; + u16 device_id; + u8 class_code[3]; + u32 image_size; + u16 image_rev; + u8 image_type; + bool last; +}; + +u32 nvbios_pcirTe(struct nvkm_bios *, u32, u8 *ver, u16 *hdr); +u32 nvbios_pcirTp(struct nvkm_bios *, u32, u8 *ver, u16 *hdr, + struct nvbios_pcirT *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/perf.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/perf.h new file mode 100644 index 000000000..7cc2becab --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/perf.h @@ -0,0 +1,41 @@ +#ifndef __NVBIOS_PERF_H__ +#define __NVBIOS_PERF_H__ +u16 nvbios_perf_table(struct nvkm_bios *, u8 *ver, u8 *hdr, + u8 *cnt, u8 *len, u8 *snr, u8 *ssz); + +struct nvbios_perfE { + u8 pstate; + u8 fanspeed; + u8 voltage; + u32 core; + u32 shader; + u32 memory; + u32 vdec; + u32 disp; + u32 script; +}; + +u16 nvbios_perf_entry(struct nvkm_bios *, int idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len); +u16 nvbios_perfEp(struct nvkm_bios *, int idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_perfE *); + +struct nvbios_perfS { + union { + struct { + u32 freq; + } v40; + }; +}; + +u32 nvbios_perfSe(struct nvkm_bios *, u32 data, int idx, + u8 *ver, u8 *hdr, u8 cnt, u8 len); +u32 nvbios_perfSp(struct nvkm_bios *, u32 data, int idx, + u8 *ver, u8 *hdr, u8 cnt, u8 len, struct nvbios_perfS *); + +struct nvbios_perf_fan { + u32 pwm_divisor; +}; + +int nvbios_perf_fan_parse(struct nvkm_bios *, struct nvbios_perf_fan *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/pll.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/pll.h new file mode 100644 index 000000000..5a69978d1 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/pll.h @@ -0,0 +1,75 @@ +#ifndef __NVBIOS_PLL_H__ +#define __NVBIOS_PLL_H__ +/*XXX: kill me */ +struct nvkm_pll_vals { + union { + struct { +#ifdef __BIG_ENDIAN + uint8_t N1, M1, N2, M2; +#else + uint8_t M1, N1, M2, N2; +#endif + }; + struct { + uint16_t NM1, NM2; + } __attribute__((packed)); + }; + int log2P; + + int refclk; +}; + +/* these match types in pll limits table version 0x40, + * nvkm uses them on all chipsets internally where a + * specific pll needs to be referenced, but the exact + * register isn't known. + */ +enum nvbios_pll_type { + PLL_CORE = 0x01, + PLL_SHADER = 0x02, + PLL_UNK03 = 0x03, + PLL_MEMORY = 0x04, + PLL_VDEC = 0x05, + PLL_UNK40 = 0x40, + PLL_UNK41 = 0x41, + PLL_UNK42 = 0x42, + PLL_VPLL0 = 0x80, + PLL_VPLL1 = 0x81, + PLL_VPLL2 = 0x82, + PLL_VPLL3 = 0x83, + PLL_MAX = 0xff +}; + +struct nvbios_pll { + enum nvbios_pll_type type; + u32 reg; + u32 refclk; + + u8 min_p; + u8 max_p; + u8 bias_p; + + /* + * for most pre nv50 cards setting a log2P of 7 (the common max_log2p + * value) is no different to 6 (at least for vplls) so allowing the MNP + * calc to use 7 causes the generated clock to be out by a factor of 2. + * however, max_log2p cannot be fixed-up during parsing as the + * unmodified max_log2p value is still needed for setting mplls, hence + * an additional max_usable_log2p member + */ + u8 max_p_usable; + + struct { + u32 min_freq; + u32 max_freq; + u32 min_inputfreq; + u32 max_inputfreq; + u8 min_m; + u8 max_m; + u8 min_n; + u8 max_n; + } vco1, vco2; +}; + +int nvbios_pll_parse(struct nvkm_bios *, u32 type, struct nvbios_pll *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/pmu.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/pmu.h new file mode 100644 index 000000000..d606875c1 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/pmu.h @@ -0,0 +1,35 @@ +#ifndef __NVBIOS_PMU_H__ +#define __NVBIOS_PMU_H__ +struct nvbios_pmuT { +}; + +u32 nvbios_pmuTe(struct nvkm_bios *, u8 *ver, u8 *hdr, u8 *cnt, u8 *len); +u32 nvbios_pmuTp(struct nvkm_bios *, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_pmuT *); + +struct nvbios_pmuE { + u8 type; + u32 data; +}; + +u32 nvbios_pmuEe(struct nvkm_bios *, int idx, u8 *ver, u8 *hdr); +u32 nvbios_pmuEp(struct nvkm_bios *, int idx, u8 *ver, u8 *hdr, + struct nvbios_pmuE *); + +struct nvbios_pmuR { + u32 boot_addr_pmu; + u32 boot_addr; + u32 boot_size; + u32 code_addr_pmu; + u32 code_addr; + u32 code_size; + u32 init_addr_pmu; + + u32 data_addr_pmu; + u32 data_addr; + u32 data_size; + u32 args_addr_pmu; +}; + +bool nvbios_pmuRm(struct nvkm_bios *, u8 type, struct nvbios_pmuR *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/ramcfg.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/ramcfg.h new file mode 100644 index 000000000..420426793 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/ramcfg.h @@ -0,0 +1,141 @@ +#ifndef __NVBIOS_RAMCFG_H__ +#define __NVBIOS_RAMCFG_H__ +struct nvbios_ramcfg { + unsigned rammap_ver; + unsigned rammap_hdr; + unsigned rammap_min; + unsigned rammap_max; + union { + struct { + unsigned rammap_10_04_02:1; + unsigned rammap_10_04_08:1; + }; + struct { + unsigned rammap_11_08_01:1; + unsigned rammap_11_08_0c:2; + unsigned rammap_11_08_10:1; + unsigned rammap_11_09_01ff:9; + unsigned rammap_11_0a_03fe:9; + unsigned rammap_11_0a_0400:1; + unsigned rammap_11_0a_0800:1; + unsigned rammap_11_0b_01f0:5; + unsigned rammap_11_0b_0200:1; + unsigned rammap_11_0b_0400:1; + unsigned rammap_11_0b_0800:1; + unsigned rammap_11_0d:8; + unsigned rammap_11_0e:8; + unsigned rammap_11_0f:8; + unsigned rammap_11_11_0c:2; + }; + }; + + unsigned ramcfg_ver; + unsigned ramcfg_hdr; + unsigned ramcfg_timing; + union { + struct { + unsigned ramcfg_10_02_01:1; + unsigned ramcfg_10_02_02:1; + unsigned ramcfg_10_02_04:1; + unsigned ramcfg_10_02_08:1; + unsigned ramcfg_10_02_10:1; + unsigned ramcfg_10_02_20:1; + unsigned ramcfg_10_DLLoff:1; + unsigned ramcfg_10_03_0f:4; + unsigned ramcfg_10_04_01:1; + unsigned ramcfg_10_05:8; + unsigned ramcfg_10_06:8; + unsigned ramcfg_10_07:8; + unsigned ramcfg_10_08:8; + unsigned ramcfg_10_09_0f:4; + unsigned ramcfg_10_09_f0:4; + }; + struct { + unsigned ramcfg_11_01_01:1; + unsigned ramcfg_11_01_02:1; + unsigned ramcfg_11_01_04:1; + unsigned ramcfg_11_01_08:1; + unsigned ramcfg_11_01_10:1; + unsigned ramcfg_11_01_20:1; + unsigned ramcfg_11_01_40:1; + unsigned ramcfg_11_01_80:1; + unsigned ramcfg_11_02_03:2; + unsigned ramcfg_11_02_04:1; + unsigned ramcfg_11_02_08:1; + unsigned ramcfg_11_02_10:1; + unsigned ramcfg_11_02_40:1; + unsigned ramcfg_11_02_80:1; + unsigned ramcfg_11_03_0f:4; + unsigned ramcfg_11_03_30:2; + unsigned ramcfg_11_03_c0:2; + unsigned ramcfg_11_03_f0:4; + unsigned ramcfg_11_04:8; + unsigned ramcfg_11_06:8; + unsigned ramcfg_11_07_02:1; + unsigned ramcfg_11_07_04:1; + unsigned ramcfg_11_07_08:1; + unsigned ramcfg_11_07_10:1; + unsigned ramcfg_11_07_40:1; + unsigned ramcfg_11_07_80:1; + unsigned ramcfg_11_08_01:1; + unsigned ramcfg_11_08_02:1; + unsigned ramcfg_11_08_04:1; + unsigned ramcfg_11_08_08:1; + unsigned ramcfg_11_08_10:1; + unsigned ramcfg_11_08_20:1; + unsigned ramcfg_11_09:8; + }; + }; + + unsigned timing_ver; + unsigned timing_hdr; + unsigned timing[11]; + union { + struct { + unsigned timing_10_WR:8; + unsigned timing_10_WTR:8; + unsigned timing_10_CL:8; + unsigned timing_10_RC:8; + /*empty: 4 */ + unsigned timing_10_RFC:8; /* Byte 5 */ + /*empty: 6 */ + unsigned timing_10_RAS:8; /* Byte 7 */ + /*empty: 8 */ + unsigned timing_10_RP:8; /* Byte 9 */ + unsigned timing_10_RCDRD:8; + unsigned timing_10_RCDWR:8; + unsigned timing_10_RRD:8; + unsigned timing_10_13:8; + unsigned timing_10_ODT:3; + /* empty: 15 */ + unsigned timing_10_16:8; + /* empty: 17 */ + unsigned timing_10_18:8; + unsigned timing_10_CWL:8; + unsigned timing_10_20:8; + unsigned timing_10_21:8; + /* empty: 22, 23 */ + unsigned timing_10_24:8; + }; + struct { + unsigned timing_20_2e_03:2; + unsigned timing_20_2e_30:2; + unsigned timing_20_2e_c0:2; + unsigned timing_20_2f_03:2; + unsigned timing_20_2c_003f:6; + unsigned timing_20_2c_1fc0:7; + unsigned timing_20_30_f8:5; + unsigned timing_20_30_07:3; + unsigned timing_20_31_0007:3; + unsigned timing_20_31_0078:4; + unsigned timing_20_31_0780:4; + unsigned timing_20_31_0800:1; + unsigned timing_20_31_7000:3; + unsigned timing_20_31_8000:1; + }; + }; +}; + +u8 nvbios_ramcfg_count(struct nvkm_bios *); +u8 nvbios_ramcfg_index(struct nvkm_subdev *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/rammap.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/rammap.h new file mode 100644 index 000000000..609a905ec --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/rammap.h @@ -0,0 +1,21 @@ +#ifndef __NVBIOS_RAMMAP_H__ +#define __NVBIOS_RAMMAP_H__ +#include + +u32 nvbios_rammapTe(struct nvkm_bios *, u8 *ver, u8 *hdr, + u8 *cnt, u8 *len, u8 *snr, u8 *ssz); + +u32 nvbios_rammapEe(struct nvkm_bios *, int idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len); +u32 nvbios_rammapEp(struct nvkm_bios *, int idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_ramcfg *); +u32 nvbios_rammapEm(struct nvkm_bios *, u16 mhz, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_ramcfg *); + +u32 nvbios_rammapSe(struct nvkm_bios *, u32 data, + u8 ever, u8 ehdr, u8 ecnt, u8 elen, int idx, + u8 *ver, u8 *hdr); +u32 nvbios_rammapSp(struct nvkm_bios *, u32 data, + u8 ever, u8 ehdr, u8 ecnt, u8 elen, int idx, + u8 *ver, u8 *hdr, struct nvbios_ramcfg *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/therm.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/therm.h new file mode 100644 index 000000000..dd3ba960e --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/therm.h @@ -0,0 +1,72 @@ +#ifndef __NVBIOS_THERM_H__ +#define __NVBIOS_THERM_H__ +struct nvbios_therm_threshold { + u8 temp; + u8 hysteresis; +}; + +struct nvbios_therm_sensor { + /* diode */ + s16 slope_mult; + s16 slope_div; + s16 offset_num; + s16 offset_den; + s8 offset_constant; + + /* thresholds */ + struct nvbios_therm_threshold thrs_fan_boost; + struct nvbios_therm_threshold thrs_down_clock; + struct nvbios_therm_threshold thrs_critical; + struct nvbios_therm_threshold thrs_shutdown; +}; + +enum nvbios_therm_fan_type { + NVBIOS_THERM_FAN_UNK = 0, + NVBIOS_THERM_FAN_TOGGLE = 1, + NVBIOS_THERM_FAN_PWM = 2, +}; + +/* no vbios have more than 6 */ +#define NVKM_TEMP_FAN_TRIP_MAX 10 +struct nvbios_therm_trip_point { + int fan_duty; + int temp; + int hysteresis; +}; + +enum nvbios_therm_fan_mode { + NVBIOS_THERM_FAN_TRIP = 0, + NVBIOS_THERM_FAN_LINEAR = 1, + NVBIOS_THERM_FAN_OTHER = 2, +}; + +struct nvbios_therm_fan { + enum nvbios_therm_fan_type type; + + u32 pwm_freq; + + u8 min_duty; + u8 max_duty; + + u16 bump_period; + u16 slow_down_period; + + enum nvbios_therm_fan_mode fan_mode; + struct nvbios_therm_trip_point trip[NVKM_TEMP_FAN_TRIP_MAX]; + u8 nr_fan_trip; + u8 linear_min_temp; + u8 linear_max_temp; +}; + +enum nvbios_therm_domain { + NVBIOS_THERM_DOMAIN_CORE, + NVBIOS_THERM_DOMAIN_AMBIENT, +}; + +int +nvbios_therm_sensor_parse(struct nvkm_bios *, enum nvbios_therm_domain, + struct nvbios_therm_sensor *); + +int +nvbios_therm_fan_parse(struct nvkm_bios *, struct nvbios_therm_fan *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/timing.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/timing.h new file mode 100644 index 000000000..339a826aa --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/timing.h @@ -0,0 +1,11 @@ +#ifndef __NVBIOS_TIMING_H__ +#define __NVBIOS_TIMING_H__ +#include + +u16 nvbios_timingTe(struct nvkm_bios *, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz); +u16 nvbios_timingEe(struct nvkm_bios *, int idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len); +u16 nvbios_timingEp(struct nvkm_bios *, int idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_ramcfg *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/vmap.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/vmap.h new file mode 100644 index 000000000..6633c6db9 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/vmap.h @@ -0,0 +1,21 @@ +#ifndef __NVBIOS_VMAP_H__ +#define __NVBIOS_VMAP_H__ +struct nvbios_vmap { +}; + +u16 nvbios_vmap_table(struct nvkm_bios *, u8 *ver, u8 *hdr, u8 *cnt, u8 *len); +u16 nvbios_vmap_parse(struct nvkm_bios *, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_vmap *); + +struct nvbios_vmap_entry { + u8 unk0; + u8 link; + u32 min; + u32 max; + s32 arg[6]; +}; + +u16 nvbios_vmap_entry(struct nvkm_bios *, int idx, u8 *ver, u8 *len); +u16 nvbios_vmap_entry_parse(struct nvkm_bios *, int idx, u8 *ver, u8 *len, + struct nvbios_vmap_entry *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/volt.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/volt.h new file mode 100644 index 000000000..eb2de4b85 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/volt.h @@ -0,0 +1,23 @@ +#ifndef __NVBIOS_VOLT_H__ +#define __NVBIOS_VOLT_H__ +struct nvbios_volt { + u8 vidmask; + u32 min; + u32 max; + u32 base; + s16 step; +}; + +u16 nvbios_volt_table(struct nvkm_bios *, u8 *ver, u8 *hdr, u8 *cnt, u8 *len); +u16 nvbios_volt_parse(struct nvkm_bios *, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_volt *); + +struct nvbios_volt_entry { + u32 voltage; + u8 vid; +}; + +u16 nvbios_volt_entry(struct nvkm_bios *, int idx, u8 *ver, u8 *len); +u16 nvbios_volt_entry_parse(struct nvkm_bios *, int idx, u8 *ver, u8 *len, + struct nvbios_volt_entry *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/xpio.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/xpio.h new file mode 100644 index 000000000..0c0fe234f --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/xpio.h @@ -0,0 +1,18 @@ +#ifndef __NVBIOS_XPIO_H__ +#define __NVBIOS_XPIO_H__ + +#define NVBIOS_XPIO_FLAG_AUX 0x10 +#define NVBIOS_XPIO_FLAG_AUX0 0x00 +#define NVBIOS_XPIO_FLAG_AUX1 0x10 + +struct nvbios_xpio { + u8 type; + u8 addr; + u8 flags; +}; + +u16 dcb_xpio_table(struct nvkm_bios *, u8 idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len); +u16 dcb_xpio_parse(struct nvkm_bios *, u8 idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_xpio *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bus.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bus.h new file mode 100644 index 000000000..fba83c048 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/bus.h @@ -0,0 +1,50 @@ +#ifndef __NVKM_BUS_H__ +#define __NVKM_BUS_H__ +#include + +struct nvkm_bus_intr { + u32 stat; + u32 unit; +}; + +struct nvkm_bus { + struct nvkm_subdev base; + int (*hwsq_exec)(struct nvkm_bus *, u32 *, u32); + u32 hwsq_size; +}; + +static inline struct nvkm_bus * +nvkm_bus(void *obj) +{ + return (void *)nvkm_subdev(obj, NVDEV_SUBDEV_BUS); +} + +#define nvkm_bus_create(p, e, o, d) \ + nvkm_subdev_create_((p), (e), (o), 0, "PBUS", "master", \ + sizeof(**d), (void **)d) +#define nvkm_bus_destroy(p) \ + nvkm_subdev_destroy(&(p)->base) +#define nvkm_bus_init(p) \ + nvkm_subdev_init(&(p)->base) +#define nvkm_bus_fini(p, s) \ + nvkm_subdev_fini(&(p)->base, (s)) + +#define _nvkm_bus_dtor _nvkm_subdev_dtor +#define _nvkm_bus_init _nvkm_subdev_init +#define _nvkm_bus_fini _nvkm_subdev_fini + +extern struct nvkm_oclass *nv04_bus_oclass; +extern struct nvkm_oclass *nv31_bus_oclass; +extern struct nvkm_oclass *nv50_bus_oclass; +extern struct nvkm_oclass *g94_bus_oclass; +extern struct nvkm_oclass *gf100_bus_oclass; + +/* interface to sequencer */ +struct nvkm_hwsq; +int nvkm_hwsq_init(struct nvkm_bus *, struct nvkm_hwsq **); +int nvkm_hwsq_fini(struct nvkm_hwsq **, bool exec); +void nvkm_hwsq_wr32(struct nvkm_hwsq *, u32 addr, u32 data); +void nvkm_hwsq_setf(struct nvkm_hwsq *, u8 flag, int data); +void nvkm_hwsq_wait(struct nvkm_hwsq *, u8 flag, u8 data); +void nvkm_hwsq_nsec(struct nvkm_hwsq *, u32 nsec); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/clk.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/clk.h new file mode 100644 index 000000000..f5d303850 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/clk.h @@ -0,0 +1,161 @@ +#ifndef __NVKM_CLK_H__ +#define __NVKM_CLK_H__ +#include +#include +struct nvbios_pll; +struct nvkm_pll_vals; + +enum nv_clk_src { + nv_clk_src_crystal, + nv_clk_src_href, + + nv_clk_src_hclk, + nv_clk_src_hclkm3, + nv_clk_src_hclkm3d2, + nv_clk_src_hclkm2d3, /* NVAA */ + nv_clk_src_hclkm4, /* NVAA */ + nv_clk_src_cclk, /* NVAA */ + + nv_clk_src_host, + + nv_clk_src_sppll0, + nv_clk_src_sppll1, + + nv_clk_src_mpllsrcref, + nv_clk_src_mpllsrc, + nv_clk_src_mpll, + nv_clk_src_mdiv, + + nv_clk_src_core, + nv_clk_src_core_intm, + nv_clk_src_shader, + + nv_clk_src_mem, + + nv_clk_src_gpc, + nv_clk_src_rop, + nv_clk_src_hubk01, + nv_clk_src_hubk06, + nv_clk_src_hubk07, + nv_clk_src_copy, + nv_clk_src_daemon, + nv_clk_src_disp, + nv_clk_src_vdec, + + nv_clk_src_dom6, + + nv_clk_src_max, +}; + +struct nvkm_cstate { + struct list_head head; + u8 voltage; + u32 domain[nv_clk_src_max]; +}; + +struct nvkm_pstate { + struct list_head head; + struct list_head list; /* c-states */ + struct nvkm_cstate base; + u8 pstate; + u8 fanspeed; +}; + +struct nvkm_domain { + enum nv_clk_src name; + u8 bios; /* 0xff for none */ +#define NVKM_CLK_DOM_FLAG_CORE 0x01 + u8 flags; + const char *mname; + int mdiv; +}; + +struct nvkm_clk { + struct nvkm_subdev base; + + struct nvkm_domain *domains; + struct nvkm_pstate bstate; + + struct list_head states; + int state_nr; + + struct work_struct work; + wait_queue_head_t wait; + atomic_t waiting; + + struct nvkm_notify pwrsrc_ntfy; + int pwrsrc; + int pstate; /* current */ + int ustate_ac; /* user-requested (-1 disabled, -2 perfmon) */ + int ustate_dc; /* user-requested (-1 disabled, -2 perfmon) */ + int astate; /* perfmon adjustment (base) */ + int tstate; /* thermal adjustment (max-) */ + int dstate; /* display adjustment (min+) */ + + bool allow_reclock; + + 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 *); + + /*XXX: die, these are here *only* to support the completely + * bat-shit insane what-was-nvkm_hw.c code + */ + int (*pll_calc)(struct nvkm_clk *, struct nvbios_pll *, int clk, + struct nvkm_pll_vals *pv); + int (*pll_prog)(struct nvkm_clk *, u32 reg1, struct nvkm_pll_vals *pv); +}; + +static inline struct nvkm_clk * +nvkm_clk(void *obj) +{ + return (void *)nvkm_subdev(obj, NVDEV_SUBDEV_CLK); +} + +#define nvkm_clk_create(p,e,o,i,r,s,n,d) \ + nvkm_clk_create_((p), (e), (o), (i), (r), (s), (n), sizeof(**d), \ + (void **)d) +#define nvkm_clk_destroy(p) ({ \ + struct nvkm_clk *clk = (p); \ + _nvkm_clk_dtor(nv_object(clk)); \ +}) +#define nvkm_clk_init(p) ({ \ + struct nvkm_clk *clk = (p); \ + _nvkm_clk_init(nv_object(clk)); \ +}) +#define nvkm_clk_fini(p,s) ({ \ + struct nvkm_clk *clk = (p); \ + _nvkm_clk_fini(nv_object(clk), (s)); \ +}) + +int nvkm_clk_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, + struct nvkm_domain *, struct nvkm_pstate *, + int, bool, int, void **); +void _nvkm_clk_dtor(struct nvkm_object *); +int _nvkm_clk_init(struct nvkm_object *); +int _nvkm_clk_fini(struct nvkm_object *, bool); + +extern struct nvkm_oclass nv04_clk_oclass; +extern struct nvkm_oclass nv40_clk_oclass; +extern struct nvkm_oclass *nv50_clk_oclass; +extern struct nvkm_oclass *g84_clk_oclass; +extern struct nvkm_oclass *mcp77_clk_oclass; +extern struct nvkm_oclass gt215_clk_oclass; +extern struct nvkm_oclass gf100_clk_oclass; +extern struct nvkm_oclass gk104_clk_oclass; +extern struct nvkm_oclass gk20a_clk_oclass; + +int nv04_clk_pll_set(struct nvkm_clk *, u32 type, u32 freq); +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 *); +int gt215_clk_pll_calc(struct nvkm_clk *, struct nvbios_pll *, + int clk, struct nvkm_pll_vals *); + +int nvkm_clk_ustate(struct nvkm_clk *, int req, int pwr); +int nvkm_clk_astate(struct nvkm_clk *, int req, int rel, bool wait); +int nvkm_clk_dstate(struct nvkm_clk *, int req, int rel); +int nvkm_clk_tstate(struct nvkm_clk *, int req, int rel); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/devinit.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/devinit.h new file mode 100644 index 000000000..d1bbe0d62 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/devinit.h @@ -0,0 +1,32 @@ +#ifndef __NVKM_DEVINIT_H__ +#define __NVKM_DEVINIT_H__ +#include + +struct nvkm_devinit { + struct nvkm_subdev base; + bool post; + void (*meminit)(struct nvkm_devinit *); + int (*pll_set)(struct nvkm_devinit *, u32 type, u32 freq); + u32 (*mmio)(struct nvkm_devinit *, u32 addr); +}; + +static inline struct nvkm_devinit * +nvkm_devinit(void *obj) +{ + return (void *)nvkm_subdev(obj, NVDEV_SUBDEV_DEVINIT); +} + +extern struct nvkm_oclass *nv04_devinit_oclass; +extern struct nvkm_oclass *nv05_devinit_oclass; +extern struct nvkm_oclass *nv10_devinit_oclass; +extern struct nvkm_oclass *nv1a_devinit_oclass; +extern struct nvkm_oclass *nv20_devinit_oclass; +extern struct nvkm_oclass *nv50_devinit_oclass; +extern struct nvkm_oclass *g84_devinit_oclass; +extern struct nvkm_oclass *g98_devinit_oclass; +extern struct nvkm_oclass *gt215_devinit_oclass; +extern struct nvkm_oclass *mcp89_devinit_oclass; +extern struct nvkm_oclass *gf100_devinit_oclass; +extern struct nvkm_oclass *gm107_devinit_oclass; +extern struct nvkm_oclass *gm204_devinit_oclass; +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/fb.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/fb.h new file mode 100644 index 000000000..16da56cf4 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/fb.h @@ -0,0 +1,154 @@ +#ifndef __NVKM_FB_H__ +#define __NVKM_FB_H__ +#include + +#include + +/* memory type/access flags, do not match hardware values */ +#define NV_MEM_ACCESS_RO 1 +#define NV_MEM_ACCESS_WO 2 +#define NV_MEM_ACCESS_RW (NV_MEM_ACCESS_RO | NV_MEM_ACCESS_WO) +#define NV_MEM_ACCESS_SYS 4 +#define NV_MEM_ACCESS_VM 8 +#define NV_MEM_ACCESS_NOSNOOP 16 + +#define NV_MEM_TARGET_VRAM 0 +#define NV_MEM_TARGET_PCI 1 +#define NV_MEM_TARGET_PCI_NOSNOOP 2 +#define NV_MEM_TARGET_VM 3 +#define NV_MEM_TARGET_GART 4 + +#define NV_MEM_TYPE_VM 0x7f +#define NV_MEM_COMP_VM 0x03 + +struct nvkm_mem { + struct drm_device *dev; + + struct nvkm_vma bar_vma; + struct nvkm_vma vma[2]; + u8 page_shift; + + struct nvkm_mm_node *tag; + struct list_head regions; + dma_addr_t *pages; + u32 memtype; + u64 offset; + u64 size; + struct sg_table *sg; +}; + +struct nvkm_fb_tile { + struct nvkm_mm_node *tag; + u32 addr; + u32 limit; + u32 pitch; + u32 zcomp; +}; + +struct nvkm_fb { + struct nvkm_subdev base; + + bool (*memtype_valid)(struct nvkm_fb *, u32 memtype); + + struct nvkm_ram *ram; + + struct nvkm_mm vram; + struct nvkm_mm tags; + + struct { + struct nvkm_fb_tile region[16]; + 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; +}; + +static inline struct nvkm_fb * +nvkm_fb(void *obj) +{ + /* fbram uses this before device subdev pointer is valid */ + if (nv_iclass(obj, NV_SUBDEV_CLASS) && + nv_subidx(obj) == NVDEV_SUBDEV_FB) + return obj; + + return (void *)nvkm_subdev(obj, NVDEV_SUBDEV_FB); +} + +extern struct nvkm_oclass *nv04_fb_oclass; +extern struct nvkm_oclass *nv10_fb_oclass; +extern struct nvkm_oclass *nv1a_fb_oclass; +extern struct nvkm_oclass *nv20_fb_oclass; +extern struct nvkm_oclass *nv25_fb_oclass; +extern struct nvkm_oclass *nv30_fb_oclass; +extern struct nvkm_oclass *nv35_fb_oclass; +extern struct nvkm_oclass *nv36_fb_oclass; +extern struct nvkm_oclass *nv40_fb_oclass; +extern struct nvkm_oclass *nv41_fb_oclass; +extern struct nvkm_oclass *nv44_fb_oclass; +extern struct nvkm_oclass *nv46_fb_oclass; +extern struct nvkm_oclass *nv47_fb_oclass; +extern struct nvkm_oclass *nv49_fb_oclass; +extern struct nvkm_oclass *nv4e_fb_oclass; +extern struct nvkm_oclass *nv50_fb_oclass; +extern struct nvkm_oclass *g84_fb_oclass; +extern struct nvkm_oclass *gt215_fb_oclass; +extern struct nvkm_oclass *mcp77_fb_oclass; +extern struct nvkm_oclass *mcp89_fb_oclass; +extern struct nvkm_oclass *gf100_fb_oclass; +extern struct nvkm_oclass *gk104_fb_oclass; +extern struct nvkm_oclass *gk20a_fb_oclass; +extern struct nvkm_oclass *gm107_fb_oclass; + +#include +#include + +struct nvkm_ram_data { + struct list_head head; + struct nvbios_ramcfg bios; + u32 freq; +}; + +struct nvkm_ram { + struct nvkm_object base; + enum { + NV_MEM_TYPE_UNKNOWN = 0, + NV_MEM_TYPE_STOLEN, + NV_MEM_TYPE_SGRAM, + NV_MEM_TYPE_SDRAM, + NV_MEM_TYPE_DDR1, + NV_MEM_TYPE_DDR2, + NV_MEM_TYPE_DDR3, + NV_MEM_TYPE_GDDR2, + NV_MEM_TYPE_GDDR3, + NV_MEM_TYPE_GDDR4, + NV_MEM_TYPE_GDDR5 + } type; + u64 stolen; + u64 size; + u32 tags; + + int ranks; + int parts; + int part_mask; + + int (*get)(struct nvkm_fb *, u64 size, u32 align, u32 size_nc, + u32 type, struct nvkm_mem **); + void (*put)(struct nvkm_fb *, struct nvkm_mem **); + + int (*calc)(struct nvkm_fb *, u32 freq); + int (*prog)(struct nvkm_fb *); + void (*tidy)(struct nvkm_fb *); + u32 freq; + u32 mr[16]; + u32 mr1_nuts; + + struct nvkm_ram_data *next; + struct nvkm_ram_data former; + struct nvkm_ram_data xition; + struct nvkm_ram_data target; +}; +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/fuse.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/fuse.h new file mode 100644 index 000000000..a1384786a --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/fuse.h @@ -0,0 +1,28 @@ +#ifndef __NVKM_FUSE_H__ +#define __NVKM_FUSE_H__ +#include +#include + +struct nvkm_fuse { + struct nvkm_subdev base; +}; + +static inline struct nvkm_fuse * +nvkm_fuse(void *obj) +{ + return (void *)nvkm_subdev(obj, NVDEV_SUBDEV_FUSE); +} + +#define nvkm_fuse_create(p, e, o, d) \ + nvkm_fuse_create_((p), (e), (o), sizeof(**d), (void **)d) + +int nvkm_fuse_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, int, void **); +void _nvkm_fuse_dtor(struct nvkm_object *); +int _nvkm_fuse_init(struct nvkm_object *); +#define _nvkm_fuse_fini _nvkm_subdev_fini + +extern struct nvkm_oclass nv50_fuse_oclass; +extern struct nvkm_oclass gf100_fuse_oclass; +extern struct nvkm_oclass gm107_fuse_oclass; +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/gpio.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/gpio.h new file mode 100644 index 000000000..ca5099a81 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/gpio.h @@ -0,0 +1,44 @@ +#ifndef __NVKM_GPIO_H__ +#define __NVKM_GPIO_H__ +#include +#include + +#include +#include + +struct nvkm_gpio_ntfy_req { +#define NVKM_GPIO_HI 0x01 +#define NVKM_GPIO_LO 0x02 +#define NVKM_GPIO_TOGGLED 0x03 + u8 mask; + u8 line; +}; + +struct nvkm_gpio_ntfy_rep { + u8 mask; +}; + +struct nvkm_gpio { + struct nvkm_subdev base; + + struct nvkm_event event; + + void (*reset)(struct nvkm_gpio *, u8 func); + int (*find)(struct nvkm_gpio *, int idx, u8 tag, u8 line, + struct dcb_gpio_func *); + int (*set)(struct nvkm_gpio *, int idx, u8 tag, u8 line, int state); + int (*get)(struct nvkm_gpio *, int idx, u8 tag, u8 line); +}; + +static inline struct nvkm_gpio * +nvkm_gpio(void *obj) +{ + return (void *)nvkm_subdev(obj, NVDEV_SUBDEV_GPIO); +} + +extern struct nvkm_oclass *nv10_gpio_oclass; +extern struct nvkm_oclass *nv50_gpio_oclass; +extern struct nvkm_oclass *g94_gpio_oclass; +extern struct nvkm_oclass *gf110_gpio_oclass; +extern struct nvkm_oclass *gk104_gpio_oclass; +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/i2c.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/i2c.h new file mode 100644 index 000000000..a2e33730f --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/i2c.h @@ -0,0 +1,135 @@ +#ifndef __NVKM_I2C_H__ +#define __NVKM_I2C_H__ +#include +#include + +#include +#include + +#define NV_I2C_PORT(n) (0x00 + (n)) +#define NV_I2C_AUX(n) (0x10 + (n)) +#define NV_I2C_EXT(n) (0x20 + (n)) +#define NV_I2C_DEFAULT(n) (0x80 + (n)) + +#define NV_I2C_TYPE_DCBI2C(n) (0x0000 | (n)) +#define NV_I2C_TYPE_EXTDDC(e) (0x0005 | (e) << 8) +#define NV_I2C_TYPE_EXTAUX(e) (0x0006 | (e) << 8) + +struct nvkm_i2c_ntfy_req { +#define NVKM_I2C_PLUG 0x01 +#define NVKM_I2C_UNPLUG 0x02 +#define NVKM_I2C_IRQ 0x04 +#define NVKM_I2C_DONE 0x08 +#define NVKM_I2C_ANY 0x0f + u8 mask; + u8 port; +}; + +struct nvkm_i2c_ntfy_rep { + u8 mask; +}; + +struct nvkm_i2c_port { + struct nvkm_object base; + struct i2c_adapter adapter; + struct mutex mutex; + + struct list_head head; + u8 index; + int aux; + + const struct nvkm_i2c_func *func; +}; + +struct nvkm_i2c_func { + void (*drive_scl)(struct nvkm_i2c_port *, int); + void (*drive_sda)(struct nvkm_i2c_port *, int); + int (*sense_scl)(struct nvkm_i2c_port *); + int (*sense_sda)(struct nvkm_i2c_port *); + + int (*aux)(struct nvkm_i2c_port *, bool, u8, u32, u8 *, u8); + int (*pattern)(struct nvkm_i2c_port *, int pattern); + int (*lnk_ctl)(struct nvkm_i2c_port *, int nr, int bw, bool enh); + int (*drv_ctl)(struct nvkm_i2c_port *, int lane, int sw, int pe); +}; + +struct nvkm_i2c_board_info { + struct i2c_board_info dev; + u8 udelay; /* set to 0 to use the standard delay */ +}; + +struct nvkm_i2c { + struct nvkm_subdev base; + struct nvkm_event event; + + struct nvkm_i2c_port *(*find)(struct nvkm_i2c *, u8 index); + struct nvkm_i2c_port *(*find_type)(struct nvkm_i2c *, u16 type); + int (*acquire_pad)(struct nvkm_i2c_port *, unsigned long timeout); + void (*release_pad)(struct nvkm_i2c_port *); + int (*acquire)(struct nvkm_i2c_port *, unsigned long timeout); + void (*release)(struct nvkm_i2c_port *); + int (*identify)(struct nvkm_i2c *, int index, + const char *what, struct nvkm_i2c_board_info *, + bool (*match)(struct nvkm_i2c_port *, + struct i2c_board_info *, void *), + void *); + + wait_queue_head_t wait; + struct list_head ports; +}; + +static inline struct nvkm_i2c * +nvkm_i2c(void *obj) +{ + return (void *)nvkm_subdev(obj, NVDEV_SUBDEV_I2C); +} + +extern struct nvkm_oclass *nv04_i2c_oclass; +extern struct nvkm_oclass *nv4e_i2c_oclass; +extern struct nvkm_oclass *nv50_i2c_oclass; +extern struct nvkm_oclass *g94_i2c_oclass; +extern struct nvkm_oclass *gf110_i2c_oclass; +extern struct nvkm_oclass *gf117_i2c_oclass; +extern struct nvkm_oclass *gk104_i2c_oclass; +extern struct nvkm_oclass *gm204_i2c_oclass; + +static inline int +nv_rdi2cr(struct nvkm_i2c_port *port, u8 addr, u8 reg) +{ + u8 val; + struct i2c_msg msgs[] = { + { .addr = addr, .flags = 0, .len = 1, .buf = ® }, + { .addr = addr, .flags = I2C_M_RD, .len = 1, .buf = &val }, + }; + + int ret = i2c_transfer(&port->adapter, msgs, 2); + if (ret != 2) + return -EIO; + + return val; +} + +static inline int +nv_wri2cr(struct nvkm_i2c_port *port, u8 addr, u8 reg, u8 val) +{ + u8 buf[2] = { reg, val }; + struct i2c_msg msgs[] = { + { .addr = addr, .flags = 0, .len = 2, .buf = buf }, + }; + + int ret = i2c_transfer(&port->adapter, msgs, 1); + if (ret != 1) + return -EIO; + + return 0; +} + +static inline bool +nv_probe_i2c(struct nvkm_i2c_port *port, u8 addr) +{ + return nv_rdi2cr(port, addr, 0) >= 0; +} + +int nv_rdaux(struct nvkm_i2c_port *, u32 addr, u8 *data, u8 size); +int nv_wraux(struct nvkm_i2c_port *, u32 addr, u8 *data, u8 size); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/ibus.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/ibus.h new file mode 100644 index 000000000..2150d8af0 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/ibus.h @@ -0,0 +1,32 @@ +#ifndef __NVKM_IBUS_H__ +#define __NVKM_IBUS_H__ +#include + +struct nvkm_ibus { + struct nvkm_subdev base; +}; + +static inline struct nvkm_ibus * +nvkm_ibus(void *obj) +{ + return (void *)nvkm_subdev(obj, NVDEV_SUBDEV_IBUS); +} + +#define nvkm_ibus_create(p,e,o,d) \ + nvkm_subdev_create_((p), (e), (o), 0, "PIBUS", "ibus", \ + sizeof(**d), (void **)d) +#define nvkm_ibus_destroy(p) \ + nvkm_subdev_destroy(&(p)->base) +#define nvkm_ibus_init(p) \ + nvkm_subdev_init(&(p)->base) +#define nvkm_ibus_fini(p,s) \ + nvkm_subdev_fini(&(p)->base, (s)) + +#define _nvkm_ibus_dtor _nvkm_subdev_dtor +#define _nvkm_ibus_init _nvkm_subdev_init +#define _nvkm_ibus_fini _nvkm_subdev_fini + +extern struct nvkm_oclass gf100_ibus_oclass; +extern struct nvkm_oclass gk104_ibus_oclass; +extern struct nvkm_oclass gk20a_ibus_oclass; +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/instmem.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/instmem.h new file mode 100644 index 000000000..1bcb763cf --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/instmem.h @@ -0,0 +1,49 @@ +#ifndef __NVKM_INSTMEM_H__ +#define __NVKM_INSTMEM_H__ +#include + +struct nvkm_instobj { + struct nvkm_object base; + struct list_head head; + u32 *suspend; + u64 addr; + u32 size; +}; + +static inline struct nvkm_instobj * +nv_memobj(void *obj) +{ +#if CONFIG_NOUVEAU_DEBUG >= NV_DBG_PARANOIA + if (unlikely(!nv_iclass(obj, NV_MEMOBJ_CLASS))) + nv_assert("BAD CAST -> NvMemObj, %08x", nv_hclass(obj)); +#endif + return obj; +} + +struct nvkm_instmem { + struct nvkm_subdev base; + struct list_head list; + + u32 reserved; + int (*alloc)(struct nvkm_instmem *, struct nvkm_object *, + u32 size, u32 align, struct nvkm_object **); +}; + +static inline struct nvkm_instmem * +nvkm_instmem(void *obj) +{ + /* nv04/nv40 impls need to create objects in their constructor, + * which is before the subdev pointer is valid + */ + if (nv_iclass(obj, NV_SUBDEV_CLASS) && + nv_subidx(obj) == NVDEV_SUBDEV_INSTMEM) + return obj; + + return (void *)nvkm_subdev(obj, NVDEV_SUBDEV_INSTMEM); +} + +extern struct nvkm_oclass *nv04_instmem_oclass; +extern struct nvkm_oclass *nv40_instmem_oclass; +extern struct nvkm_oclass *nv50_instmem_oclass; +extern struct nvkm_oclass *gk20a_instmem_oclass; +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/ltc.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/ltc.h new file mode 100644 index 000000000..cd5d29fc0 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/ltc.h @@ -0,0 +1,31 @@ +#ifndef __NVKM_LTC_H__ +#define __NVKM_LTC_H__ +#include +struct nvkm_mm_node; + +#define NVKM_LTC_MAX_ZBC_CNT 16 + +struct nvkm_ltc { + struct nvkm_subdev base; + + int (*tags_alloc)(struct nvkm_ltc *, u32 count, + struct nvkm_mm_node **); + void (*tags_free)(struct nvkm_ltc *, struct nvkm_mm_node **); + void (*tags_clear)(struct nvkm_ltc *, u32 first, u32 count); + + int zbc_min; + int zbc_max; + int (*zbc_color_get)(struct nvkm_ltc *, int index, const u32[4]); + int (*zbc_depth_get)(struct nvkm_ltc *, int index, const u32); +}; + +static inline struct nvkm_ltc * +nvkm_ltc(void *obj) +{ + return (void *)nvkm_subdev(obj, NVDEV_SUBDEV_LTC); +} + +extern struct nvkm_oclass *gf100_ltc_oclass; +extern struct nvkm_oclass *gk104_ltc_oclass; +extern struct nvkm_oclass *gm107_ltc_oclass; +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/mc.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/mc.h new file mode 100644 index 000000000..055bea770 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/mc.h @@ -0,0 +1,28 @@ +#ifndef __NVKM_MC_H__ +#define __NVKM_MC_H__ +#include + +struct nvkm_mc { + struct nvkm_subdev base; + bool use_msi; + unsigned int irq; + void (*unk260)(struct nvkm_mc *, u32); +}; + +static inline struct nvkm_mc * +nvkm_mc(void *obj) +{ + return (void *)nvkm_subdev(obj, NVDEV_SUBDEV_MC); +} + +extern struct nvkm_oclass *nv04_mc_oclass; +extern struct nvkm_oclass *nv40_mc_oclass; +extern struct nvkm_oclass *nv44_mc_oclass; +extern struct nvkm_oclass *nv4c_mc_oclass; +extern struct nvkm_oclass *nv50_mc_oclass; +extern struct nvkm_oclass *g94_mc_oclass; +extern struct nvkm_oclass *g98_mc_oclass; +extern struct nvkm_oclass *gf100_mc_oclass; +extern struct nvkm_oclass *gf106_mc_oclass; +extern struct nvkm_oclass *gk20a_mc_oclass; +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/mmu.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/mmu.h new file mode 100644 index 000000000..3a5368776 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/mmu.h @@ -0,0 +1,104 @@ +#ifndef __NVKM_MMU_H__ +#define __NVKM_MMU_H__ +#include +#include +struct nvkm_device; +struct nvkm_mem; + +struct nvkm_vm_pgt { + struct nvkm_gpuobj *obj[2]; + u32 refcount[2]; +}; + +struct nvkm_vm_pgd { + struct list_head head; + struct nvkm_gpuobj *obj; +}; + +struct nvkm_vma { + struct list_head head; + int refcount; + struct nvkm_vm *vm; + struct nvkm_mm_node *node; + u64 offset; + u32 access; +}; + +struct nvkm_vm { + struct nvkm_mmu *mmu; + struct nvkm_mm mm; + struct kref refcount; + + struct list_head pgd_list; + atomic_t engref[NVDEV_SUBDEV_NR]; + + struct nvkm_vm_pgt *pgt; + u32 fpde; + u32 lpde; +}; + +struct nvkm_mmu { + struct nvkm_subdev base; + + u64 limit; + u8 dma_bits; + u32 pgt_bits; + u8 spg_shift; + u8 lpg_shift; + + int (*create)(struct nvkm_mmu *, u64 offset, u64 length, + u64 mm_offset, struct nvkm_vm **); + + void (*map_pgt)(struct nvkm_gpuobj *pgd, u32 pde, + struct nvkm_gpuobj *pgt[2]); + void (*map)(struct nvkm_vma *, struct nvkm_gpuobj *, + struct nvkm_mem *, u32 pte, u32 cnt, + u64 phys, u64 delta); + void (*map_sg)(struct nvkm_vma *, struct nvkm_gpuobj *, + struct nvkm_mem *, u32 pte, u32 cnt, dma_addr_t *); + void (*unmap)(struct nvkm_gpuobj *pgt, u32 pte, u32 cnt); + void (*flush)(struct nvkm_vm *); +}; + +static inline struct nvkm_mmu * +nvkm_mmu(void *obj) +{ + return (void *)nvkm_subdev(obj, NVDEV_SUBDEV_MMU); +} + +#define nvkm_mmu_create(p,e,o,i,f,d) \ + nvkm_subdev_create((p), (e), (o), 0, (i), (f), (d)) +#define nvkm_mmu_destroy(p) \ + nvkm_subdev_destroy(&(p)->base) +#define nvkm_mmu_init(p) \ + nvkm_subdev_init(&(p)->base) +#define nvkm_mmu_fini(p,s) \ + nvkm_subdev_fini(&(p)->base, (s)) + +#define _nvkm_mmu_dtor _nvkm_subdev_dtor +#define _nvkm_mmu_init _nvkm_subdev_init +#define _nvkm_mmu_fini _nvkm_subdev_fini + +extern struct nvkm_oclass nv04_mmu_oclass; +extern struct nvkm_oclass nv41_mmu_oclass; +extern struct nvkm_oclass nv44_mmu_oclass; +extern struct nvkm_oclass nv50_mmu_oclass; +extern struct nvkm_oclass gf100_mmu_oclass; + +int nv04_vm_create(struct nvkm_mmu *, u64, u64, u64, + struct nvkm_vm **); +void nv04_mmu_dtor(struct nvkm_object *); + +int nvkm_vm_create(struct nvkm_mmu *, u64 offset, u64 length, u64 mm_offset, + u32 block, struct nvkm_vm **); +int nvkm_vm_new(struct nvkm_device *, u64 offset, u64 length, u64 mm_offset, + struct nvkm_vm **); +int nvkm_vm_ref(struct nvkm_vm *, struct nvkm_vm **, struct nvkm_gpuobj *pgd); +int nvkm_vm_get(struct nvkm_vm *, u64 size, u32 page_shift, u32 access, + struct nvkm_vma *); +void nvkm_vm_put(struct nvkm_vma *); +void nvkm_vm_map(struct nvkm_vma *, struct nvkm_mem *); +void nvkm_vm_map_at(struct nvkm_vma *, u64 offset, struct nvkm_mem *); +void nvkm_vm_unmap(struct nvkm_vma *); +void nvkm_vm_unmap_at(struct nvkm_vma *, u64 offset, u64 length); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/mxm.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/mxm.h new file mode 100644 index 000000000..fba613477 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/mxm.h @@ -0,0 +1,34 @@ +#ifndef __NVKM_MXM_H__ +#define __NVKM_MXM_H__ +#include + +#define MXM_SANITISE_DCB 0x00000001 + +struct nvkm_mxm { + struct nvkm_subdev base; + u32 action; + u8 *mxms; +}; + +static inline struct nvkm_mxm * +nvkm_mxm(void *obj) +{ + return (void *)nvkm_subdev(obj, NVDEV_SUBDEV_MXM); +} + +#define nvkm_mxm_create(p,e,o,d) \ + nvkm_mxm_create_((p), (e), (o), sizeof(**d), (void **)d) +#define nvkm_mxm_init(p) \ + nvkm_subdev_init(&(p)->base) +#define nvkm_mxm_fini(p,s) \ + nvkm_subdev_fini(&(p)->base, (s)) +int nvkm_mxm_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, int, void **); +void nvkm_mxm_destroy(struct nvkm_mxm *); + +#define _nvkm_mxm_dtor _nvkm_subdev_dtor +#define _nvkm_mxm_init _nvkm_subdev_init +#define _nvkm_mxm_fini _nvkm_subdev_fini + +extern struct nvkm_oclass nv50_mxm_oclass; +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/pmu.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/pmu.h new file mode 100644 index 000000000..755942352 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/pmu.h @@ -0,0 +1,54 @@ +#ifndef __NVKM_PMU_H__ +#define __NVKM_PMU_H__ +#include + +struct nvkm_pmu { + struct nvkm_subdev base; + + struct { + u32 base; + u32 size; + } send; + + struct { + u32 base; + u32 size; + + struct work_struct work; + wait_queue_head_t wait; + u32 process; + u32 message; + u32 data[2]; + } recv; + + int (*message)(struct nvkm_pmu *, u32[2], u32, u32, u32, u32); + void (*pgob)(struct nvkm_pmu *, bool); +}; + +static inline struct nvkm_pmu * +nvkm_pmu(void *obj) +{ + return (void *)nvkm_subdev(obj, NVDEV_SUBDEV_PMU); +} + +extern struct nvkm_oclass *gt215_pmu_oclass; +extern struct nvkm_oclass *gf100_pmu_oclass; +extern struct nvkm_oclass *gf110_pmu_oclass; +extern struct nvkm_oclass *gk104_pmu_oclass; +extern struct nvkm_oclass *gk110_pmu_oclass; +extern struct nvkm_oclass *gk208_pmu_oclass; +extern struct nvkm_oclass *gk20a_pmu_oclass; + +/* interface to MEMX process running on PMU */ +struct nvkm_memx; +int nvkm_memx_init(struct nvkm_pmu *, struct nvkm_memx **); +int nvkm_memx_fini(struct nvkm_memx **, bool exec); +void nvkm_memx_wr32(struct nvkm_memx *, u32 addr, u32 data); +void nvkm_memx_wait(struct nvkm_memx *, u32 addr, u32 mask, u32 data, u32 nsec); +void nvkm_memx_nsec(struct nvkm_memx *, u32 nsec); +void nvkm_memx_wait_vblank(struct nvkm_memx *); +void nvkm_memx_train(struct nvkm_memx *); +int nvkm_memx_train_result(struct nvkm_pmu *, u32 *, int); +void nvkm_memx_block(struct nvkm_memx *); +void nvkm_memx_unblock(struct nvkm_memx *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/therm.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/therm.h new file mode 100644 index 000000000..6662829b6 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/therm.h @@ -0,0 +1,79 @@ +#ifndef __NVKM_THERM_H__ +#define __NVKM_THERM_H__ +#include + +enum nvkm_therm_fan_mode { + NVKM_THERM_CTRL_NONE = 0, + NVKM_THERM_CTRL_MANUAL = 1, + NVKM_THERM_CTRL_AUTO = 2, +}; + +enum nvkm_therm_attr_type { + NVKM_THERM_ATTR_FAN_MIN_DUTY = 0, + NVKM_THERM_ATTR_FAN_MAX_DUTY = 1, + NVKM_THERM_ATTR_FAN_MODE = 2, + + NVKM_THERM_ATTR_THRS_FAN_BOOST = 10, + NVKM_THERM_ATTR_THRS_FAN_BOOST_HYST = 11, + NVKM_THERM_ATTR_THRS_DOWN_CLK = 12, + NVKM_THERM_ATTR_THRS_DOWN_CLK_HYST = 13, + NVKM_THERM_ATTR_THRS_CRITICAL = 14, + NVKM_THERM_ATTR_THRS_CRITICAL_HYST = 15, + NVKM_THERM_ATTR_THRS_SHUTDOWN = 16, + NVKM_THERM_ATTR_THRS_SHUTDOWN_HYST = 17, +}; + +struct nvkm_therm { + struct nvkm_subdev base; + + 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 (*fan_get)(struct nvkm_therm *); + int (*fan_set)(struct nvkm_therm *, int); + int (*fan_sense)(struct nvkm_therm *); + + int (*temp_get)(struct nvkm_therm *); + + int (*attr_get)(struct nvkm_therm *, enum nvkm_therm_attr_type); + int (*attr_set)(struct nvkm_therm *, enum nvkm_therm_attr_type, int); +}; + +static inline struct nvkm_therm * +nvkm_therm(void *obj) +{ + return (void *)nvkm_subdev(obj, NVDEV_SUBDEV_THERM); +} + +#define nvkm_therm_create(p,e,o,d) \ + nvkm_therm_create_((p), (e), (o), sizeof(**d), (void **)d) +#define nvkm_therm_destroy(p) ({ \ + struct nvkm_therm *therm = (p); \ + _nvkm_therm_dtor(nv_object(therm)); \ +}) +#define nvkm_therm_init(p) ({ \ + struct nvkm_therm *therm = (p); \ + _nvkm_therm_init(nv_object(therm)); \ +}) +#define nvkm_therm_fini(p,s) ({ \ + struct nvkm_therm *therm = (p); \ + _nvkm_therm_init(nv_object(therm), (s)); \ +}) + +int nvkm_therm_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, int, void **); +void _nvkm_therm_dtor(struct nvkm_object *); +int _nvkm_therm_init(struct nvkm_object *); +int _nvkm_therm_fini(struct nvkm_object *, bool); + +int nvkm_therm_cstate(struct nvkm_therm *, int, int); + +extern struct nvkm_oclass nv40_therm_oclass; +extern struct nvkm_oclass nv50_therm_oclass; +extern struct nvkm_oclass g84_therm_oclass; +extern struct nvkm_oclass gt215_therm_oclass; +extern struct nvkm_oclass gf110_therm_oclass; +extern struct nvkm_oclass gm107_therm_oclass; +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/timer.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/timer.h new file mode 100644 index 000000000..4ad55082e --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/timer.h @@ -0,0 +1,61 @@ +#ifndef __NVKM_TIMER_H__ +#define __NVKM_TIMER_H__ +#include + +struct nvkm_alarm { + struct list_head head; + u64 timestamp; + void (*func)(struct nvkm_alarm *); +}; + +static inline void +nvkm_alarm_init(struct nvkm_alarm *alarm, + void (*func)(struct nvkm_alarm *)) +{ + INIT_LIST_HEAD(&alarm->head); + alarm->func = func; +} + +bool nvkm_timer_wait_eq(void *, u64 nsec, u32 addr, u32 mask, u32 data); +bool nvkm_timer_wait_ne(void *, u64 nsec, u32 addr, u32 mask, u32 data); +bool nvkm_timer_wait_cb(void *, u64 nsec, bool (*func)(void *), void *data); +void nvkm_timer_alarm(void *, u32 nsec, struct nvkm_alarm *); +void nvkm_timer_alarm_cancel(void *, struct nvkm_alarm *); + +#define NV_WAIT_DEFAULT 2000000000ULL +#define nv_wait(o,a,m,v) \ + nvkm_timer_wait_eq((o), NV_WAIT_DEFAULT, (a), (m), (v)) +#define nv_wait_ne(o,a,m,v) \ + nvkm_timer_wait_ne((o), NV_WAIT_DEFAULT, (a), (m), (v)) +#define nv_wait_cb(o,c,d) \ + nvkm_timer_wait_cb((o), NV_WAIT_DEFAULT, (c), (d)) + +struct nvkm_timer { + struct nvkm_subdev base; + u64 (*read)(struct nvkm_timer *); + void (*alarm)(struct nvkm_timer *, u64 time, struct nvkm_alarm *); + void (*alarm_cancel)(struct nvkm_timer *, struct nvkm_alarm *); +}; + +static inline struct nvkm_timer * +nvkm_timer(void *obj) +{ + return (void *)nvkm_subdev(obj, NVDEV_SUBDEV_TIMER); +} + +#define nvkm_timer_create(p,e,o,d) \ + nvkm_subdev_create_((p), (e), (o), 0, "PTIMER", "timer", \ + sizeof(**d), (void **)d) +#define nvkm_timer_destroy(p) \ + nvkm_subdev_destroy(&(p)->base) +#define nvkm_timer_init(p) \ + nvkm_subdev_init(&(p)->base) +#define nvkm_timer_fini(p,s) \ + nvkm_subdev_fini(&(p)->base, (s)) + +int nvkm_timer_create_(struct nvkm_object *, struct nvkm_engine *, + struct nvkm_oclass *, int size, void **); + +extern struct nvkm_oclass nv04_timer_oclass; +extern struct nvkm_oclass gk20a_timer_oclass; +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/vga.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/vga.h new file mode 100644 index 000000000..fee09ad81 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/vga.h @@ -0,0 +1,30 @@ +#ifndef __NOUVEAU_VGA_H__ +#define __NOUVEAU_VGA_H__ + +#include + +/* access to various legacy io ports */ +u8 nv_rdport(void *obj, int head, u16 port); +void nv_wrport(void *obj, int head, u16 port, u8 value); + +/* VGA Sequencer */ +u8 nv_rdvgas(void *obj, int head, u8 index); +void nv_wrvgas(void *obj, int head, u8 index, u8 value); + +/* VGA Graphics */ +u8 nv_rdvgag(void *obj, int head, u8 index); +void nv_wrvgag(void *obj, int head, u8 index, u8 value); + +/* VGA CRTC */ +u8 nv_rdvgac(void *obj, int head, u8 index); +void nv_wrvgac(void *obj, int head, u8 index, u8 value); + +/* VGA indexed port access dispatcher */ +u8 nv_rdvgai(void *obj, int head, u16 port, u8 index); +void nv_wrvgai(void *obj, int head, u16 port, u8 index, u8 value); + +bool nv_lockvgac(void *obj, bool lock); +u8 nv_rdvgaowner(void *obj); +void nv_wrvgaowner(void *obj, u8); + +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/volt.h b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/volt.h new file mode 100644 index 000000000..e3d7243fb --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/include/nvkm/subdev/volt.h @@ -0,0 +1,58 @@ +#ifndef __NVKM_VOLT_H__ +#define __NVKM_VOLT_H__ +#include + +struct nvkm_voltage { + u32 uv; + u8 id; +}; + +struct nvkm_volt { + struct nvkm_subdev base; + + int (*vid_get)(struct nvkm_volt *); + int (*get)(struct nvkm_volt *); + int (*vid_set)(struct nvkm_volt *, u8 vid); + int (*set)(struct nvkm_volt *, u32 uv); + int (*set_id)(struct nvkm_volt *, u8 id, int condition); + + u8 vid_mask; + u8 vid_nr; + struct { + u32 uv; + u8 vid; + } vid[256]; +}; + +static inline struct nvkm_volt * +nvkm_volt(void *obj) +{ + return (void *)nvkm_subdev(obj, NVDEV_SUBDEV_VOLT); +} + +#define nvkm_volt_create(p, e, o, d) \ + nvkm_volt_create_((p), (e), (o), sizeof(**d), (void **)d) +#define nvkm_volt_destroy(p) ({ \ + struct nvkm_volt *v = (p); \ + _nvkm_volt_dtor(nv_object(v)); \ +}) +#define nvkm_volt_init(p) ({ \ + struct nvkm_volt *v = (p); \ + _nvkm_volt_init(nv_object(v)); \ +}) +#define nvkm_volt_fini(p,s) \ + nvkm_subdev_fini((p), (s)) + +int nvkm_volt_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, int, void **); +void _nvkm_volt_dtor(struct nvkm_object *); +int _nvkm_volt_init(struct nvkm_object *); +#define _nvkm_volt_fini _nvkm_subdev_fini + +extern struct nvkm_oclass nv40_volt_oclass; +extern struct nvkm_oclass gk20a_volt_oclass; + +int nvkm_voltgpio_init(struct nvkm_volt *); +int nvkm_voltgpio_get(struct nvkm_volt *); +int nvkm_voltgpio_set(struct nvkm_volt *, u8); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_abi16.c b/kernel/drivers/gpu/drm/nouveau/nouveau_abi16.c new file mode 100644 index 000000000..d8b0891a1 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_abi16.c @@ -0,0 +1,537 @@ +/* + * 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 "nouveau_drm.h" +#include "nouveau_dma.h" +#include "nouveau_gem.h" +#include "nouveau_chan.h" +#include "nouveau_abi16.h" + +struct nouveau_abi16 * +nouveau_abi16_get(struct drm_file *file_priv, struct drm_device *dev) +{ + struct nouveau_cli *cli = nouveau_cli(file_priv); + mutex_lock(&cli->mutex); + if (!cli->abi16) { + struct nouveau_abi16 *abi16; + cli->abi16 = abi16 = kzalloc(sizeof(*abi16), GFP_KERNEL); + if (cli->abi16) { + struct nv_device_v0 args = { + .device = ~0ULL, + }; + + INIT_LIST_HEAD(&abi16->channels); + + /* allocate device object targeting client's default + * device (ie. the one that belongs to the fd it + * opened) + */ + if (nvif_device_init(&cli->base.base, NULL, + NOUVEAU_ABI16_DEVICE, NV_DEVICE, + &args, sizeof(args), + &abi16->device) == 0) + return cli->abi16; + + kfree(cli->abi16); + cli->abi16 = NULL; + } + + mutex_unlock(&cli->mutex); + } + return cli->abi16; +} + +int +nouveau_abi16_put(struct nouveau_abi16 *abi16, int ret) +{ + struct nouveau_cli *cli = (void *)nvif_client(&abi16->device.base); + mutex_unlock(&cli->mutex); + return ret; +} + +u16 +nouveau_abi16_swclass(struct nouveau_drm *drm) +{ + switch (drm->device.info.family) { + case NV_DEVICE_INFO_V0_TNT: + return 0x006e; + case NV_DEVICE_INFO_V0_CELSIUS: + case NV_DEVICE_INFO_V0_KELVIN: + case NV_DEVICE_INFO_V0_RANKINE: + case NV_DEVICE_INFO_V0_CURIE: + return 0x016e; + case NV_DEVICE_INFO_V0_TESLA: + return 0x506e; + case NV_DEVICE_INFO_V0_FERMI: + case NV_DEVICE_INFO_V0_KEPLER: + case NV_DEVICE_INFO_V0_MAXWELL: + return 0x906e; + } + + return 0x0000; +} + +static void +nouveau_abi16_ntfy_fini(struct nouveau_abi16_chan *chan, + struct nouveau_abi16_ntfy *ntfy) +{ + nvkm_mm_free(&chan->heap, &ntfy->node); + list_del(&ntfy->head); + kfree(ntfy); +} + +static void +nouveau_abi16_chan_fini(struct nouveau_abi16 *abi16, + struct nouveau_abi16_chan *chan) +{ + struct nouveau_abi16_ntfy *ntfy, *temp; + + /* wait for all activity to stop before releasing notify object, which + * may be still in use */ + if (chan->chan && chan->ntfy) + nouveau_channel_idle(chan->chan); + + /* cleanup notifier state */ + list_for_each_entry_safe(ntfy, temp, &chan->notifiers, head) { + nouveau_abi16_ntfy_fini(chan, ntfy); + } + + if (chan->ntfy) { + nouveau_bo_vma_del(chan->ntfy, &chan->ntfy_vma); + nouveau_bo_unpin(chan->ntfy); + drm_gem_object_unreference_unlocked(&chan->ntfy->gem); + } + + if (chan->heap.block_size) + nvkm_mm_fini(&chan->heap); + + /* destroy channel object, all children will be killed too */ + if (chan->chan) { + abi16->handles &= ~(1ULL << (chan->chan->object->handle & 0xffff)); + nouveau_channel_del(&chan->chan); + } + + list_del(&chan->head); + kfree(chan); +} + +void +nouveau_abi16_fini(struct nouveau_abi16 *abi16) +{ + struct nouveau_cli *cli = (void *)nvif_client(&abi16->device.base); + struct nouveau_abi16_chan *chan, *temp; + + /* cleanup channels */ + list_for_each_entry_safe(chan, temp, &abi16->channels, head) { + nouveau_abi16_chan_fini(abi16, chan); + } + + /* destroy the device object */ + nvif_device_fini(&abi16->device); + + kfree(cli->abi16); + cli->abi16 = NULL; +} + +int +nouveau_abi16_ioctl_getparam(ABI16_IOCTL_ARGS) +{ + struct nouveau_cli *cli = nouveau_cli(file_priv); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvif_device *device = &drm->device; + struct nvkm_timer *ptimer = nvxx_timer(device); + struct nvkm_gr *gr = nvxx_gr(device); + struct drm_nouveau_getparam *getparam = data; + + switch (getparam->param) { + case NOUVEAU_GETPARAM_CHIPSET_ID: + getparam->value = device->info.chipset; + break; + case NOUVEAU_GETPARAM_PCI_VENDOR: + if (nv_device_is_pci(nvxx_device(device))) + getparam->value = dev->pdev->vendor; + else + getparam->value = 0; + break; + case NOUVEAU_GETPARAM_PCI_DEVICE: + if (nv_device_is_pci(nvxx_device(device))) + getparam->value = dev->pdev->device; + else + getparam->value = 0; + break; + case NOUVEAU_GETPARAM_BUS_TYPE: + if (!nv_device_is_pci(nvxx_device(device))) + getparam->value = 3; + else + if (drm_pci_device_is_agp(dev)) + getparam->value = 0; + else + if (!pci_is_pcie(dev->pdev)) + getparam->value = 1; + else + getparam->value = 2; + break; + case NOUVEAU_GETPARAM_FB_SIZE: + getparam->value = drm->gem.vram_available; + break; + case NOUVEAU_GETPARAM_AGP_SIZE: + getparam->value = drm->gem.gart_available; + break; + case NOUVEAU_GETPARAM_VM_VRAM_BASE: + getparam->value = 0; /* deprecated */ + break; + case NOUVEAU_GETPARAM_PTIMER_TIME: + getparam->value = ptimer->read(ptimer); + break; + case NOUVEAU_GETPARAM_HAS_BO_USAGE: + getparam->value = 1; + break; + case NOUVEAU_GETPARAM_HAS_PAGEFLIP: + getparam->value = 1; + break; + case NOUVEAU_GETPARAM_GRAPH_UNITS: + getparam->value = gr->units ? gr->units(gr) : 0; + break; + default: + NV_PRINTK(debug, cli, "unknown parameter %lld\n", getparam->param); + return -EINVAL; + } + + return 0; +} + +int +nouveau_abi16_ioctl_setparam(ABI16_IOCTL_ARGS) +{ + return -EINVAL; +} + +int +nouveau_abi16_ioctl_channel_alloc(ABI16_IOCTL_ARGS) +{ + struct drm_nouveau_channel_alloc *init = data; + struct nouveau_cli *cli = nouveau_cli(file_priv); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv, dev); + struct nouveau_abi16_chan *chan; + struct nvif_device *device; + int ret; + + if (unlikely(!abi16)) + return -ENOMEM; + + if (!drm->channel) + return nouveau_abi16_put(abi16, -ENODEV); + + device = &abi16->device; + + /* hack to allow channel engine type specification on kepler */ + if (device->info.family >= NV_DEVICE_INFO_V0_KEPLER) { + if (init->fb_ctxdma_handle != ~0) + init->fb_ctxdma_handle = KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_GR; + else + init->fb_ctxdma_handle = init->tt_ctxdma_handle; + + /* allow flips to be executed if this is a graphics channel */ + init->tt_ctxdma_handle = 0; + if (init->fb_ctxdma_handle == KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_GR) + init->tt_ctxdma_handle = 1; + } + + if (init->fb_ctxdma_handle == ~0 || init->tt_ctxdma_handle == ~0) + return nouveau_abi16_put(abi16, -EINVAL); + + /* allocate "abi16 channel" data and make up a handle for it */ + init->channel = __ffs64(~abi16->handles); + if (~abi16->handles == 0) + return nouveau_abi16_put(abi16, -ENOSPC); + + chan = kzalloc(sizeof(*chan), GFP_KERNEL); + if (!chan) + return nouveau_abi16_put(abi16, -ENOMEM); + + INIT_LIST_HEAD(&chan->notifiers); + list_add(&chan->head, &abi16->channels); + abi16->handles |= (1ULL << init->channel); + + /* create channel object and initialise dma and fence management */ + ret = nouveau_channel_new(drm, device, + NOUVEAU_ABI16_CHAN(init->channel), + init->fb_ctxdma_handle, + init->tt_ctxdma_handle, &chan->chan); + if (ret) + goto done; + + if (device->info.family >= NV_DEVICE_INFO_V0_TESLA) + init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM | + NOUVEAU_GEM_DOMAIN_GART; + else + if (chan->chan->push.buffer->bo.mem.mem_type == TTM_PL_VRAM) + init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM; + else + init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_GART; + + if (device->info.family < NV_DEVICE_INFO_V0_CELSIUS) { + init->subchan[0].handle = 0x00000000; + init->subchan[0].grclass = 0x0000; + init->subchan[1].handle = chan->chan->nvsw.handle; + init->subchan[1].grclass = 0x506e; + init->nr_subchan = 2; + } + + /* Named memory object area */ + ret = nouveau_gem_new(dev, PAGE_SIZE, 0, NOUVEAU_GEM_DOMAIN_GART, + 0, 0, &chan->ntfy); + if (ret == 0) + ret = nouveau_bo_pin(chan->ntfy, TTM_PL_FLAG_TT, false); + if (ret) + goto done; + + if (device->info.family >= NV_DEVICE_INFO_V0_TESLA) { + ret = nouveau_bo_vma_add(chan->ntfy, cli->vm, + &chan->ntfy_vma); + if (ret) + goto done; + } + + ret = drm_gem_handle_create(file_priv, &chan->ntfy->gem, + &init->notifier_handle); + if (ret) + goto done; + + ret = nvkm_mm_init(&chan->heap, 0, PAGE_SIZE, 1); +done: + if (ret) + nouveau_abi16_chan_fini(abi16, chan); + return nouveau_abi16_put(abi16, ret); +} + +static struct nouveau_abi16_chan * +nouveau_abi16_chan(struct nouveau_abi16 *abi16, int channel) +{ + struct nouveau_abi16_chan *chan; + + list_for_each_entry(chan, &abi16->channels, head) { + if (chan->chan->object->handle == NOUVEAU_ABI16_CHAN(channel)) + return chan; + } + + return NULL; +} + +int +nouveau_abi16_ioctl_channel_free(ABI16_IOCTL_ARGS) +{ + struct drm_nouveau_channel_free *req = data; + struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv, dev); + struct nouveau_abi16_chan *chan; + + if (unlikely(!abi16)) + return -ENOMEM; + + chan = nouveau_abi16_chan(abi16, req->channel); + if (!chan) + return nouveau_abi16_put(abi16, -ENOENT); + nouveau_abi16_chan_fini(abi16, chan); + return nouveau_abi16_put(abi16, 0); +} + +int +nouveau_abi16_ioctl_grobj_alloc(ABI16_IOCTL_ARGS) +{ + struct drm_nouveau_grobj_alloc *init = data; + struct { + struct nvif_ioctl_v0 ioctl; + struct nvif_ioctl_new_v0 new; + } args = { + .ioctl.owner = NVIF_IOCTL_V0_OWNER_ANY, + .ioctl.type = NVIF_IOCTL_V0_NEW, + .ioctl.path_nr = 3, + .ioctl.path[2] = NOUVEAU_ABI16_CLIENT, + .ioctl.path[1] = NOUVEAU_ABI16_DEVICE, + .ioctl.path[0] = NOUVEAU_ABI16_CHAN(init->channel), + .new.route = NVDRM_OBJECT_ABI16, + .new.handle = init->handle, + .new.oclass = init->class, + }; + struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv, dev); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvif_client *client; + int ret; + + if (unlikely(!abi16)) + return -ENOMEM; + + if (init->handle == ~0) + return nouveau_abi16_put(abi16, -EINVAL); + client = nvif_client(nvif_object(&abi16->device)); + + /* compatibility with userspace that assumes 506e for all chipsets */ + if (init->class == 0x506e) { + init->class = nouveau_abi16_swclass(drm); + if (init->class == 0x906e) + return nouveau_abi16_put(abi16, 0); + } + + ret = nvif_client_ioctl(client, &args, sizeof(args)); + return nouveau_abi16_put(abi16, ret); +} + +int +nouveau_abi16_ioctl_notifierobj_alloc(ABI16_IOCTL_ARGS) +{ + struct drm_nouveau_notifierobj_alloc *info = data; + struct { + struct nvif_ioctl_v0 ioctl; + struct nvif_ioctl_new_v0 new; + struct nv_dma_v0 ctxdma; + } args = { + .ioctl.owner = NVIF_IOCTL_V0_OWNER_ANY, + .ioctl.type = NVIF_IOCTL_V0_NEW, + .ioctl.path_nr = 3, + .ioctl.path[2] = NOUVEAU_ABI16_CLIENT, + .ioctl.path[1] = NOUVEAU_ABI16_DEVICE, + .ioctl.path[0] = NOUVEAU_ABI16_CHAN(info->channel), + .new.route = NVDRM_OBJECT_ABI16, + .new.handle = info->handle, + .new.oclass = NV_DMA_IN_MEMORY, + }; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv, dev); + struct nouveau_abi16_chan *chan; + struct nouveau_abi16_ntfy *ntfy; + struct nvif_device *device = &abi16->device; + struct nvif_client *client; + int ret; + + if (unlikely(!abi16)) + return -ENOMEM; + + /* completely unnecessary for these chipsets... */ + if (unlikely(device->info.family >= NV_DEVICE_INFO_V0_FERMI)) + return nouveau_abi16_put(abi16, -EINVAL); + client = nvif_client(nvif_object(&abi16->device)); + + chan = nouveau_abi16_chan(abi16, info->channel); + if (!chan) + return nouveau_abi16_put(abi16, -ENOENT); + + ntfy = kzalloc(sizeof(*ntfy), GFP_KERNEL); + if (!ntfy) + return nouveau_abi16_put(abi16, -ENOMEM); + + list_add(&ntfy->head, &chan->notifiers); + ntfy->handle = info->handle; + + ret = nvkm_mm_head(&chan->heap, 0, 1, info->size, info->size, 1, + &ntfy->node); + if (ret) + goto done; + + args.ctxdma.start = ntfy->node->offset; + args.ctxdma.limit = ntfy->node->offset + ntfy->node->length - 1; + if (device->info.family >= NV_DEVICE_INFO_V0_TESLA) { + args.ctxdma.target = NV_DMA_V0_TARGET_VM; + args.ctxdma.access = NV_DMA_V0_ACCESS_VM; + args.ctxdma.start += chan->ntfy_vma.offset; + args.ctxdma.limit += chan->ntfy_vma.offset; + } else + if (drm->agp.stat == ENABLED) { + args.ctxdma.target = NV_DMA_V0_TARGET_AGP; + args.ctxdma.access = NV_DMA_V0_ACCESS_RDWR; + args.ctxdma.start += drm->agp.base + chan->ntfy->bo.offset; + args.ctxdma.limit += drm->agp.base + chan->ntfy->bo.offset; + client->super = true; + } else { + args.ctxdma.target = NV_DMA_V0_TARGET_VM; + args.ctxdma.access = NV_DMA_V0_ACCESS_RDWR; + args.ctxdma.start += chan->ntfy->bo.offset; + args.ctxdma.limit += chan->ntfy->bo.offset; + } + + ret = nvif_client_ioctl(client, &args, sizeof(args)); + client->super = false; + if (ret) + goto done; + + info->offset = ntfy->node->offset; + +done: + if (ret) + nouveau_abi16_ntfy_fini(chan, ntfy); + return nouveau_abi16_put(abi16, ret); +} + +int +nouveau_abi16_ioctl_gpuobj_free(ABI16_IOCTL_ARGS) +{ + struct drm_nouveau_gpuobj_free *fini = data; + struct { + struct nvif_ioctl_v0 ioctl; + struct nvif_ioctl_del del; + } args = { + .ioctl.owner = NVDRM_OBJECT_ABI16, + .ioctl.type = NVIF_IOCTL_V0_DEL, + .ioctl.path_nr = 4, + .ioctl.path[3] = NOUVEAU_ABI16_CLIENT, + .ioctl.path[2] = NOUVEAU_ABI16_DEVICE, + .ioctl.path[1] = NOUVEAU_ABI16_CHAN(fini->channel), + .ioctl.path[0] = fini->handle, + }; + struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv, dev); + struct nouveau_abi16_chan *chan; + struct nouveau_abi16_ntfy *ntfy; + struct nvif_client *client; + int ret; + + if (unlikely(!abi16)) + return -ENOMEM; + + chan = nouveau_abi16_chan(abi16, fini->channel); + if (!chan) + return nouveau_abi16_put(abi16, -ENOENT); + client = nvif_client(nvif_object(&abi16->device)); + + /* synchronize with the user channel and destroy the gpu object */ + nouveau_channel_idle(chan->chan); + + ret = nvif_client_ioctl(client, &args, sizeof(args)); + if (ret) + return nouveau_abi16_put(abi16, ret); + + /* cleanup extra state if this object was a notifier */ + list_for_each_entry(ntfy, &chan->notifiers, head) { + if (ntfy->handle == fini->handle) { + nvkm_mm_free(&chan->heap, &ntfy->node); + list_del(&ntfy->head); + break; + } + } + + return nouveau_abi16_put(abi16, 0); +} diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_abi16.h b/kernel/drivers/gpu/drm/nouveau/nouveau_abi16.h new file mode 100644 index 000000000..86eb1caf4 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_abi16.h @@ -0,0 +1,114 @@ +#ifndef __NOUVEAU_ABI16_H__ +#define __NOUVEAU_ABI16_H__ + +#define ABI16_IOCTL_ARGS \ + struct drm_device *dev, void *data, struct drm_file *file_priv + +int nouveau_abi16_ioctl_getparam(ABI16_IOCTL_ARGS); +int nouveau_abi16_ioctl_setparam(ABI16_IOCTL_ARGS); +int nouveau_abi16_ioctl_channel_alloc(ABI16_IOCTL_ARGS); +int nouveau_abi16_ioctl_channel_free(ABI16_IOCTL_ARGS); +int nouveau_abi16_ioctl_grobj_alloc(ABI16_IOCTL_ARGS); +int nouveau_abi16_ioctl_notifierobj_alloc(ABI16_IOCTL_ARGS); +int nouveau_abi16_ioctl_gpuobj_free(ABI16_IOCTL_ARGS); + +struct nouveau_abi16_ntfy { + struct list_head head; + struct nvkm_mm_node *node; + u32 handle; +}; + +struct nouveau_abi16_chan { + struct list_head head; + struct nouveau_channel *chan; + struct list_head notifiers; + struct nouveau_bo *ntfy; + struct nvkm_vma ntfy_vma; + struct nvkm_mm heap; +}; + +struct nouveau_abi16 { + struct nvif_device device; + struct list_head channels; + u64 handles; +}; + +struct nouveau_drm; +struct nouveau_abi16 *nouveau_abi16_get(struct drm_file *, struct drm_device *); +int nouveau_abi16_put(struct nouveau_abi16 *, int); +void nouveau_abi16_fini(struct nouveau_abi16 *); +u16 nouveau_abi16_swclass(struct nouveau_drm *); + +#define NOUVEAU_GEM_DOMAIN_VRAM (1 << 1) +#define NOUVEAU_GEM_DOMAIN_GART (1 << 2) + +struct drm_nouveau_channel_alloc { + uint32_t fb_ctxdma_handle; + uint32_t tt_ctxdma_handle; + + int channel; + uint32_t pushbuf_domains; + + /* Notifier memory */ + uint32_t notifier_handle; + + /* DRM-enforced subchannel assignments */ + struct { + uint32_t handle; + uint32_t grclass; + } subchan[8]; + uint32_t nr_subchan; +}; + +struct drm_nouveau_channel_free { + int channel; +}; + +struct drm_nouveau_grobj_alloc { + int channel; + uint32_t handle; + int class; +}; + +struct drm_nouveau_notifierobj_alloc { + uint32_t channel; + uint32_t handle; + uint32_t size; + uint32_t offset; +}; + +struct drm_nouveau_gpuobj_free { + int channel; + uint32_t handle; +}; + +#define NOUVEAU_GETPARAM_PCI_VENDOR 3 +#define NOUVEAU_GETPARAM_PCI_DEVICE 4 +#define NOUVEAU_GETPARAM_BUS_TYPE 5 +#define NOUVEAU_GETPARAM_FB_SIZE 8 +#define NOUVEAU_GETPARAM_AGP_SIZE 9 +#define NOUVEAU_GETPARAM_CHIPSET_ID 11 +#define NOUVEAU_GETPARAM_VM_VRAM_BASE 12 +#define NOUVEAU_GETPARAM_GRAPH_UNITS 13 +#define NOUVEAU_GETPARAM_PTIMER_TIME 14 +#define NOUVEAU_GETPARAM_HAS_BO_USAGE 15 +#define NOUVEAU_GETPARAM_HAS_PAGEFLIP 16 +struct drm_nouveau_getparam { + uint64_t param; + uint64_t value; +}; + +struct drm_nouveau_setparam { + uint64_t param; + uint64_t value; +}; + +#define DRM_IOCTL_NOUVEAU_GETPARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_NOUVEAU_GETPARAM, struct drm_nouveau_getparam) +#define DRM_IOCTL_NOUVEAU_SETPARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_NOUVEAU_SETPARAM, struct drm_nouveau_setparam) +#define DRM_IOCTL_NOUVEAU_CHANNEL_ALLOC DRM_IOWR(DRM_COMMAND_BASE + DRM_NOUVEAU_CHANNEL_ALLOC, struct drm_nouveau_channel_alloc) +#define DRM_IOCTL_NOUVEAU_CHANNEL_FREE DRM_IOW (DRM_COMMAND_BASE + DRM_NOUVEAU_CHANNEL_FREE, struct drm_nouveau_channel_free) +#define DRM_IOCTL_NOUVEAU_GROBJ_ALLOC DRM_IOW (DRM_COMMAND_BASE + DRM_NOUVEAU_GROBJ_ALLOC, struct drm_nouveau_grobj_alloc) +#define DRM_IOCTL_NOUVEAU_NOTIFIEROBJ_ALLOC DRM_IOWR(DRM_COMMAND_BASE + DRM_NOUVEAU_NOTIFIEROBJ_ALLOC, struct drm_nouveau_notifierobj_alloc) +#define DRM_IOCTL_NOUVEAU_GPUOBJ_FREE DRM_IOW (DRM_COMMAND_BASE + DRM_NOUVEAU_GPUOBJ_FREE, struct drm_nouveau_gpuobj_free) + +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_acpi.c b/kernel/drivers/gpu/drm/nouveau/nouveau_acpi.c new file mode 100644 index 000000000..622424692 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_acpi.c @@ -0,0 +1,427 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "nouveau_drm.h" +#include "nouveau_acpi.h" + +#define NOUVEAU_DSM_LED 0x02 +#define NOUVEAU_DSM_LED_STATE 0x00 +#define NOUVEAU_DSM_LED_OFF 0x10 +#define NOUVEAU_DSM_LED_STAMINA 0x11 +#define NOUVEAU_DSM_LED_SPEED 0x12 + +#define NOUVEAU_DSM_POWER 0x03 +#define NOUVEAU_DSM_POWER_STATE 0x00 +#define NOUVEAU_DSM_POWER_SPEED 0x01 +#define NOUVEAU_DSM_POWER_STAMINA 0x02 + +#define NOUVEAU_DSM_OPTIMUS_CAPS 0x1A +#define NOUVEAU_DSM_OPTIMUS_FLAGS 0x1B + +#define NOUVEAU_DSM_OPTIMUS_POWERDOWN_PS3 (3 << 24) +#define NOUVEAU_DSM_OPTIMUS_NO_POWERDOWN_PS3 (2 << 24) +#define NOUVEAU_DSM_OPTIMUS_FLAGS_CHANGED (1) + +#define NOUVEAU_DSM_OPTIMUS_SET_POWERDOWN (NOUVEAU_DSM_OPTIMUS_POWERDOWN_PS3 | NOUVEAU_DSM_OPTIMUS_FLAGS_CHANGED) + +/* result of the optimus caps function */ +#define OPTIMUS_ENABLED (1 << 0) +#define OPTIMUS_STATUS_MASK (3 << 3) +#define OPTIMUS_STATUS_OFF (0 << 3) +#define OPTIMUS_STATUS_ON_ENABLED (1 << 3) +#define OPTIMUS_STATUS_PWR_STABLE (3 << 3) +#define OPTIMUS_DISPLAY_HOTPLUG (1 << 6) +#define OPTIMUS_CAPS_MASK (7 << 24) +#define OPTIMUS_DYNAMIC_PWR_CAP (1 << 24) + +#define OPTIMUS_AUDIO_CAPS_MASK (3 << 27) +#define OPTIMUS_HDA_CODEC_MASK (2 << 27) /* hda bios control */ + +static struct nouveau_dsm_priv { + bool dsm_detected; + bool optimus_detected; + acpi_handle dhandle; + acpi_handle rom_handle; +} nouveau_dsm_priv; + +bool nouveau_is_optimus(void) { + return nouveau_dsm_priv.optimus_detected; +} + +bool nouveau_is_v1_dsm(void) { + return nouveau_dsm_priv.dsm_detected; +} + +#define NOUVEAU_DSM_HAS_MUX 0x1 +#define NOUVEAU_DSM_HAS_OPT 0x2 + +#ifdef CONFIG_VGA_SWITCHEROO +static const char nouveau_dsm_muid[] = { + 0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D, + 0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4, +}; + +static const char nouveau_op_dsm_muid[] = { + 0xF8, 0xD8, 0x86, 0xA4, 0xDA, 0x0B, 0x1B, 0x47, + 0xA7, 0x2B, 0x60, 0x42, 0xA6, 0xB5, 0xBE, 0xE0, +}; + +static int nouveau_optimus_dsm(acpi_handle handle, int func, int arg, uint32_t *result) +{ + int i; + union acpi_object *obj; + char args_buff[4]; + union acpi_object argv4 = { + .buffer.type = ACPI_TYPE_BUFFER, + .buffer.length = 4, + .buffer.pointer = args_buff + }; + + /* ACPI is little endian, AABBCCDD becomes {DD,CC,BB,AA} */ + for (i = 0; i < 4; i++) + args_buff[i] = (arg >> i * 8) & 0xFF; + + *result = 0; + obj = acpi_evaluate_dsm_typed(handle, nouveau_op_dsm_muid, 0x00000100, + func, &argv4, ACPI_TYPE_BUFFER); + if (!obj) { + acpi_handle_info(handle, "failed to evaluate _DSM\n"); + return AE_ERROR; + } else { + if (obj->buffer.length == 4) { + *result |= obj->buffer.pointer[0]; + *result |= (obj->buffer.pointer[1] << 8); + *result |= (obj->buffer.pointer[2] << 16); + *result |= (obj->buffer.pointer[3] << 24); + } + ACPI_FREE(obj); + } + + return 0; +} + +/* + * On some platforms, _DSM(nouveau_op_dsm_muid, func0) has special + * requirements on the fourth parameter, so a private implementation + * instead of using acpi_check_dsm(). + */ +static int nouveau_check_optimus_dsm(acpi_handle handle) +{ + int result; + + /* + * Function 0 returns a Buffer containing available functions. + * The args parameter is ignored for function 0, so just put 0 in it + */ + if (nouveau_optimus_dsm(handle, 0, 0, &result)) + return 0; + + /* + * ACPI Spec v4 9.14.1: if bit 0 is zero, no function is supported. + * If the n-th bit is enabled, function n is supported + */ + return result & 1 && result & (1 << NOUVEAU_DSM_OPTIMUS_CAPS); +} + +static int nouveau_dsm(acpi_handle handle, int func, int arg) +{ + int ret = 0; + union acpi_object *obj; + union acpi_object argv4 = { + .integer.type = ACPI_TYPE_INTEGER, + .integer.value = arg, + }; + + obj = acpi_evaluate_dsm_typed(handle, nouveau_dsm_muid, 0x00000102, + func, &argv4, ACPI_TYPE_INTEGER); + if (!obj) { + acpi_handle_info(handle, "failed to evaluate _DSM\n"); + return AE_ERROR; + } else { + if (obj->integer.value == 0x80000002) + ret = -ENODEV; + ACPI_FREE(obj); + } + + return ret; +} + +static int nouveau_dsm_switch_mux(acpi_handle handle, int mux_id) +{ + mxm_wmi_call_mxmx(mux_id == NOUVEAU_DSM_LED_STAMINA ? MXM_MXDS_ADAPTER_IGD : MXM_MXDS_ADAPTER_0); + mxm_wmi_call_mxds(mux_id == NOUVEAU_DSM_LED_STAMINA ? MXM_MXDS_ADAPTER_IGD : MXM_MXDS_ADAPTER_0); + return nouveau_dsm(handle, NOUVEAU_DSM_LED, mux_id); +} + +static int nouveau_dsm_set_discrete_state(acpi_handle handle, enum vga_switcheroo_state state) +{ + int arg; + if (state == VGA_SWITCHEROO_ON) + arg = NOUVEAU_DSM_POWER_SPEED; + else + arg = NOUVEAU_DSM_POWER_STAMINA; + nouveau_dsm(handle, NOUVEAU_DSM_POWER, arg); + return 0; +} + +static int nouveau_dsm_switchto(enum vga_switcheroo_client_id id) +{ + if (!nouveau_dsm_priv.dsm_detected) + return 0; + if (id == VGA_SWITCHEROO_IGD) + return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_STAMINA); + else + return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_SPEED); +} + +static int nouveau_dsm_power_state(enum vga_switcheroo_client_id id, + enum vga_switcheroo_state state) +{ + if (id == VGA_SWITCHEROO_IGD) + return 0; + + /* Optimus laptops have the card already disabled in + * nouveau_switcheroo_set_state */ + if (!nouveau_dsm_priv.dsm_detected) + return 0; + + return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dhandle, state); +} + +static int nouveau_dsm_get_client_id(struct pci_dev *pdev) +{ + /* easy option one - intel vendor ID means Integrated */ + if (pdev->vendor == PCI_VENDOR_ID_INTEL) + return VGA_SWITCHEROO_IGD; + + /* is this device on Bus 0? - this may need improving */ + if (pdev->bus->number == 0) + return VGA_SWITCHEROO_IGD; + + return VGA_SWITCHEROO_DIS; +} + +static struct vga_switcheroo_handler nouveau_dsm_handler = { + .switchto = nouveau_dsm_switchto, + .power_state = nouveau_dsm_power_state, + .get_client_id = nouveau_dsm_get_client_id, +}; + +static int nouveau_dsm_pci_probe(struct pci_dev *pdev) +{ + acpi_handle dhandle; + int retval = 0; + + dhandle = ACPI_HANDLE(&pdev->dev); + if (!dhandle) + return false; + + if (!acpi_has_method(dhandle, "_DSM")) + return false; + + if (acpi_check_dsm(dhandle, nouveau_dsm_muid, 0x00000102, + 1 << NOUVEAU_DSM_POWER)) + retval |= NOUVEAU_DSM_HAS_MUX; + + if (nouveau_check_optimus_dsm(dhandle)) + retval |= NOUVEAU_DSM_HAS_OPT; + + if (retval & NOUVEAU_DSM_HAS_OPT) { + uint32_t result; + nouveau_optimus_dsm(dhandle, NOUVEAU_DSM_OPTIMUS_CAPS, 0, + &result); + dev_info(&pdev->dev, "optimus capabilities: %s, status %s%s\n", + (result & OPTIMUS_ENABLED) ? "enabled" : "disabled", + (result & OPTIMUS_DYNAMIC_PWR_CAP) ? "dynamic power, " : "", + (result & OPTIMUS_HDA_CODEC_MASK) ? "hda bios codec supported" : ""); + } + if (retval) + nouveau_dsm_priv.dhandle = dhandle; + + return retval; +} + +static bool nouveau_dsm_detect(void) +{ + char acpi_method_name[255] = { 0 }; + struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name}; + struct pci_dev *pdev = NULL; + int has_dsm = 0; + int has_optimus = 0; + int vga_count = 0; + bool guid_valid; + int retval; + bool ret = false; + + /* lookup the MXM GUID */ + guid_valid = mxm_wmi_supported(); + + if (guid_valid) + printk("MXM: GUID detected in BIOS\n"); + + /* now do DSM detection */ + while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) { + vga_count++; + + retval = nouveau_dsm_pci_probe(pdev); + if (retval & NOUVEAU_DSM_HAS_MUX) + has_dsm |= 1; + if (retval & NOUVEAU_DSM_HAS_OPT) + has_optimus = 1; + } + + while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_3D << 8, pdev)) != NULL) { + vga_count++; + + retval = nouveau_dsm_pci_probe(pdev); + if (retval & NOUVEAU_DSM_HAS_MUX) + has_dsm |= 1; + if (retval & NOUVEAU_DSM_HAS_OPT) + has_optimus = 1; + } + + /* find the optimus DSM or the old v1 DSM */ + if (has_optimus == 1) { + acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME, + &buffer); + printk(KERN_INFO "VGA switcheroo: detected Optimus DSM method %s handle\n", + acpi_method_name); + nouveau_dsm_priv.optimus_detected = true; + ret = true; + } else if (vga_count == 2 && has_dsm && guid_valid) { + acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME, + &buffer); + printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n", + acpi_method_name); + nouveau_dsm_priv.dsm_detected = true; + ret = true; + } + + + return ret; +} + +void nouveau_register_dsm_handler(void) +{ + bool r; + + r = nouveau_dsm_detect(); + if (!r) + return; + + vga_switcheroo_register_handler(&nouveau_dsm_handler); +} + +/* Must be called for Optimus models before the card can be turned off */ +void nouveau_switcheroo_optimus_dsm(void) +{ + u32 result = 0; + if (!nouveau_dsm_priv.optimus_detected) + return; + + nouveau_optimus_dsm(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_OPTIMUS_FLAGS, + 0x3, &result); + + nouveau_optimus_dsm(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_OPTIMUS_CAPS, + NOUVEAU_DSM_OPTIMUS_SET_POWERDOWN, &result); + +} + +void nouveau_unregister_dsm_handler(void) +{ + if (nouveau_dsm_priv.optimus_detected || nouveau_dsm_priv.dsm_detected) + vga_switcheroo_unregister_handler(); +} +#else +void nouveau_register_dsm_handler(void) {} +void nouveau_unregister_dsm_handler(void) {} +void nouveau_switcheroo_optimus_dsm(void) {} +#endif + +/* retrieve the ROM in 4k blocks */ +static int nouveau_rom_call(acpi_handle rom_handle, uint8_t *bios, + int offset, int len) +{ + 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 = len; + + status = acpi_evaluate_object(rom_handle, NULL, &rom_arg, &buffer); + if (ACPI_FAILURE(status)) { + printk(KERN_INFO "failed to evaluate ROM got %s\n", acpi_format_exception(status)); + return -ENODEV; + } + obj = (union acpi_object *)buffer.pointer; + memcpy(bios+offset, obj->buffer.pointer, len); + kfree(buffer.pointer); + return len; +} + +bool nouveau_acpi_rom_supported(struct pci_dev *pdev) +{ + acpi_status status; + acpi_handle dhandle, rom_handle; + + dhandle = ACPI_HANDLE(&pdev->dev); + if (!dhandle) + return false; + + status = acpi_get_handle(dhandle, "_ROM", &rom_handle); + if (ACPI_FAILURE(status)) + return false; + + nouveau_dsm_priv.rom_handle = rom_handle; + return true; +} + +int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len) +{ + return nouveau_rom_call(nouveau_dsm_priv.rom_handle, bios, offset, len); +} + +void * +nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector) +{ + struct acpi_device *acpidev; + acpi_handle handle; + int type, ret; + void *edid; + + switch (connector->connector_type) { + case DRM_MODE_CONNECTOR_LVDS: + case DRM_MODE_CONNECTOR_eDP: + type = ACPI_VIDEO_DISPLAY_LCD; + break; + default: + return NULL; + } + + handle = ACPI_HANDLE(&dev->pdev->dev); + if (!handle) + return NULL; + + ret = acpi_bus_get_device(handle, &acpidev); + if (ret) + return NULL; + + ret = acpi_video_get_edid(acpidev, type, -1, &edid); + if (ret < 0) + return NULL; + + return kmemdup(edid, EDID_LENGTH, GFP_KERNEL); +} diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_acpi.h b/kernel/drivers/gpu/drm/nouveau/nouveau_acpi.h new file mode 100644 index 000000000..74acf0f87 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_acpi.h @@ -0,0 +1,26 @@ +#ifndef __NOUVEAU_ACPI_H__ +#define __NOUVEAU_ACPI_H__ + +#define ROM_BIOS_PAGE 4096 + +#if defined(CONFIG_ACPI) && defined(CONFIG_X86) +bool nouveau_is_optimus(void); +bool nouveau_is_v1_dsm(void); +void nouveau_register_dsm_handler(void); +void nouveau_unregister_dsm_handler(void); +void nouveau_switcheroo_optimus_dsm(void); +int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len); +bool nouveau_acpi_rom_supported(struct pci_dev *pdev); +void *nouveau_acpi_edid(struct drm_device *, struct drm_connector *); +#else +static inline bool nouveau_is_optimus(void) { return false; }; +static inline bool nouveau_is_v1_dsm(void) { return false; }; +static inline void nouveau_register_dsm_handler(void) {} +static inline void nouveau_unregister_dsm_handler(void) {} +static inline void nouveau_switcheroo_optimus_dsm(void) {} +static inline bool nouveau_acpi_rom_supported(struct pci_dev *pdev) { return false; } +static inline int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len) { return -EINVAL; } +static inline void *nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector) { return NULL; } +#endif + +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_agp.c b/kernel/drivers/gpu/drm/nouveau/nouveau_agp.c new file mode 100644 index 000000000..0b5970955 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_agp.c @@ -0,0 +1,195 @@ +#include + +#include "nouveau_drm.h" +#include "nouveau_agp.h" +#include "nouveau_reg.h" + +#if __OS_HAS_AGP +MODULE_PARM_DESC(agpmode, "AGP mode (0 to disable AGP)"); +static int nouveau_agpmode = -1; +module_param_named(agpmode, nouveau_agpmode, int, 0400); + +struct nouveau_agpmode_quirk { + u16 hostbridge_vendor; + u16 hostbridge_device; + u16 chip_vendor; + u16 chip_device; + int mode; +}; + +static struct nouveau_agpmode_quirk nouveau_agpmode_quirk_list[] = { + /* VIA Apollo PRO133x / GeForce FX 5600 Ultra, max agpmode 2, fdo #20341 */ + { PCI_VENDOR_ID_VIA, 0x0691, PCI_VENDOR_ID_NVIDIA, 0x0311, 2 }, + + {}, +}; + +static unsigned long +get_agp_mode(struct nouveau_drm *drm, const struct drm_agp_info *info) +{ + struct nvif_device *device = &drm->device; + struct nouveau_agpmode_quirk *quirk = nouveau_agpmode_quirk_list; + int agpmode = nouveau_agpmode; + unsigned long mode = info->mode; + + /* + * FW seems to be broken on nv18, it makes the card lock up + * randomly. + */ + if (device->info.chipset == 0x18) + mode &= ~PCI_AGP_COMMAND_FW; + + /* + * Go through the quirks list and adjust the agpmode accordingly. + */ + while (agpmode == -1 && quirk->hostbridge_vendor) { + if (info->id_vendor == quirk->hostbridge_vendor && + info->id_device == quirk->hostbridge_device && + nvxx_device(device)->pdev->vendor == quirk->chip_vendor && + nvxx_device(device)->pdev->device == quirk->chip_device) { + agpmode = quirk->mode; + NV_INFO(drm, "Forcing agp mode to %dX. Use agpmode to override.\n", + agpmode); + break; + } + ++quirk; + } + + /* + * AGP mode set in the command line. + */ + if (agpmode > 0) { + bool agpv3 = mode & 0x8; + int rate = agpv3 ? agpmode / 4 : agpmode; + + mode = (mode & ~0x7) | (rate & 0x7); + } + + return mode; +} + +static bool +nouveau_agp_enabled(struct nouveau_drm *drm) +{ + struct drm_device *dev = drm->dev; + + if (!dev->pdev || !drm_pci_device_is_agp(dev) || !dev->agp) + return false; + + if (drm->agp.stat == UNKNOWN) { + if (!nouveau_agpmode) + return false; +#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 + * the GATT somehow, making them quite painful to deal + * with... + */ + if (nouveau_agpmode == -1) + return false; +#endif + return true; + } + + return (drm->agp.stat == ENABLED); +} +#endif + +void +nouveau_agp_reset(struct nouveau_drm *drm) +{ +#if __OS_HAS_AGP + struct nvif_device *device = &drm->device; + struct drm_device *dev = drm->dev; + u32 save[2]; + int ret; + + if (!nouveau_agp_enabled(drm)) + return; + + /* 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 ((nvif_rd32(device, NV04_PBUS_PCI_NV_19) | + dev->agp->mode) & PCI_AGP_COMMAND_FW) { + struct drm_agp_info info; + struct drm_agp_mode mode; + + ret = drm_agp_info(dev, &info); + if (ret) + return; + + mode.mode = get_agp_mode(drm, &info); + mode.mode &= ~PCI_AGP_COMMAND_FW; + + ret = drm_agp_enable(dev, mode); + if (ret) + return; + } + + + /* clear busmaster bit, and disable AGP */ + save[0] = nvif_mask(device, NV04_PBUS_PCI_NV_1, 0x00000004, 0x00000000); + nvif_wr32(device, NV04_PBUS_PCI_NV_19, 0); + + /* reset PGRAPH, PFIFO and PTIMER */ + save[1] = nvif_mask(device, 0x000200, 0x00011100, 0x00000000); + nvif_mask(device, 0x000200, 0x00011100, save[1]); + + /* and restore bustmaster bit (gives effect of resetting AGP) */ + nvif_wr32(device, NV04_PBUS_PCI_NV_1, save[0]); +#endif +} + +void +nouveau_agp_init(struct nouveau_drm *drm) +{ +#if __OS_HAS_AGP + struct drm_device *dev = drm->dev; + struct drm_agp_info info; + struct drm_agp_mode mode; + int ret; + + if (!nouveau_agp_enabled(drm)) + return; + drm->agp.stat = DISABLE; + + ret = drm_agp_acquire(dev); + if (ret) { + NV_ERROR(drm, "unable to acquire AGP: %d\n", ret); + return; + } + + ret = drm_agp_info(dev, &info); + if (ret) { + NV_ERROR(drm, "unable to get AGP info: %d\n", ret); + return; + } + + /* see agp.h for the AGPSTAT_* modes available */ + mode.mode = get_agp_mode(drm, &info); + + ret = drm_agp_enable(dev, mode); + if (ret) { + NV_ERROR(drm, "unable to enable AGP: %d\n", ret); + return; + } + + drm->agp.stat = ENABLED; + drm->agp.base = info.aperture_base; + drm->agp.size = info.aperture_size; +#endif +} + +void +nouveau_agp_fini(struct nouveau_drm *drm) +{ +#if __OS_HAS_AGP + struct drm_device *dev = drm->dev; + if (dev->agp && dev->agp->acquired) + drm_agp_release(dev); +#endif +} diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_agp.h b/kernel/drivers/gpu/drm/nouveau/nouveau_agp.h new file mode 100644 index 000000000..b55c08652 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_agp.h @@ -0,0 +1,10 @@ +#ifndef __NOUVEAU_AGP_H__ +#define __NOUVEAU_AGP_H__ + +struct nouveau_drm; + +void nouveau_agp_reset(struct nouveau_drm *); +void nouveau_agp_init(struct nouveau_drm *); +void nouveau_agp_fini(struct nouveau_drm *); + +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_backlight.c b/kernel/drivers/gpu/drm/nouveau/nouveau_backlight.c new file mode 100644 index 000000000..e566c5b53 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_backlight.c @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2009 Red Hat + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * 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. + * + */ + +/* + * Authors: + * Matthew Garrett + * + * Register locations derived from NVClock by Roderick Colenbrander + */ + +#include + +#include "nouveau_drm.h" +#include "nouveau_reg.h" +#include "nouveau_encoder.h" + +static int +nv40_get_intensity(struct backlight_device *bd) +{ + struct nouveau_drm *drm = bl_get_data(bd); + struct nvif_device *device = &drm->device; + int val = (nvif_rd32(device, NV40_PMC_BACKLIGHT) & + NV40_PMC_BACKLIGHT_MASK) >> 16; + + return val; +} + +static int +nv40_set_intensity(struct backlight_device *bd) +{ + struct nouveau_drm *drm = bl_get_data(bd); + struct nvif_device *device = &drm->device; + int val = bd->props.brightness; + int reg = nvif_rd32(device, NV40_PMC_BACKLIGHT); + + nvif_wr32(device, NV40_PMC_BACKLIGHT, + (val << 16) | (reg & ~NV40_PMC_BACKLIGHT_MASK)); + + return 0; +} + +static const struct backlight_ops nv40_bl_ops = { + .options = BL_CORE_SUSPENDRESUME, + .get_brightness = nv40_get_intensity, + .update_status = nv40_set_intensity, +}; + +static int +nv40_backlight_init(struct drm_connector *connector) +{ + struct nouveau_drm *drm = nouveau_drm(connector->dev); + struct nvif_device *device = &drm->device; + struct backlight_properties props; + struct backlight_device *bd; + + if (!(nvif_rd32(device, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK)) + return 0; + + memset(&props, 0, sizeof(struct backlight_properties)); + props.type = BACKLIGHT_RAW; + props.max_brightness = 31; + bd = backlight_device_register("nv_backlight", connector->kdev, drm, + &nv40_bl_ops, &props); + if (IS_ERR(bd)) + return PTR_ERR(bd); + drm->backlight = bd; + bd->props.brightness = nv40_get_intensity(bd); + backlight_update_status(bd); + + return 0; +} + +static int +nv50_get_intensity(struct backlight_device *bd) +{ + struct nouveau_encoder *nv_encoder = bl_get_data(bd); + struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); + struct nvif_device *device = &drm->device; + int or = nv_encoder->or; + u32 div = 1025; + u32 val; + + val = nvif_rd32(device, NV50_PDISP_SOR_PWM_CTL(or)); + val &= NV50_PDISP_SOR_PWM_CTL_VAL; + return ((val * 100) + (div / 2)) / div; +} + +static int +nv50_set_intensity(struct backlight_device *bd) +{ + struct nouveau_encoder *nv_encoder = bl_get_data(bd); + struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); + struct nvif_device *device = &drm->device; + int or = nv_encoder->or; + u32 div = 1025; + u32 val = (bd->props.brightness * div) / 100; + + nvif_wr32(device, NV50_PDISP_SOR_PWM_CTL(or), + NV50_PDISP_SOR_PWM_CTL_NEW | val); + return 0; +} + +static const struct backlight_ops nv50_bl_ops = { + .options = BL_CORE_SUSPENDRESUME, + .get_brightness = nv50_get_intensity, + .update_status = nv50_set_intensity, +}; + +static int +nva3_get_intensity(struct backlight_device *bd) +{ + struct nouveau_encoder *nv_encoder = bl_get_data(bd); + struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); + struct nvif_device *device = &drm->device; + int or = nv_encoder->or; + u32 div, val; + + div = nvif_rd32(device, NV50_PDISP_SOR_PWM_DIV(or)); + val = nvif_rd32(device, NV50_PDISP_SOR_PWM_CTL(or)); + val &= NVA3_PDISP_SOR_PWM_CTL_VAL; + if (div && div >= val) + return ((val * 100) + (div / 2)) / div; + + return 100; +} + +static int +nva3_set_intensity(struct backlight_device *bd) +{ + struct nouveau_encoder *nv_encoder = bl_get_data(bd); + struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); + struct nvif_device *device = &drm->device; + int or = nv_encoder->or; + u32 div, val; + + div = nvif_rd32(device, NV50_PDISP_SOR_PWM_DIV(or)); + val = (bd->props.brightness * div) / 100; + if (div) { + nvif_wr32(device, NV50_PDISP_SOR_PWM_CTL(or), val | + NV50_PDISP_SOR_PWM_CTL_NEW | + NVA3_PDISP_SOR_PWM_CTL_UNK); + return 0; + } + + return -EINVAL; +} + +static const struct backlight_ops nva3_bl_ops = { + .options = BL_CORE_SUSPENDRESUME, + .get_brightness = nva3_get_intensity, + .update_status = nva3_set_intensity, +}; + +static int +nv50_backlight_init(struct drm_connector *connector) +{ + struct nouveau_drm *drm = nouveau_drm(connector->dev); + struct nvif_device *device = &drm->device; + struct nouveau_encoder *nv_encoder; + struct backlight_properties props; + struct backlight_device *bd; + const struct backlight_ops *ops; + + nv_encoder = find_encoder(connector, DCB_OUTPUT_LVDS); + if (!nv_encoder) { + nv_encoder = find_encoder(connector, DCB_OUTPUT_DP); + if (!nv_encoder) + return -ENODEV; + } + + if (!nvif_rd32(device, NV50_PDISP_SOR_PWM_CTL(nv_encoder->or))) + return 0; + + if (device->info.chipset <= 0xa0 || + device->info.chipset == 0xaa || + device->info.chipset == 0xac) + ops = &nv50_bl_ops; + else + ops = &nva3_bl_ops; + + memset(&props, 0, sizeof(struct backlight_properties)); + props.type = BACKLIGHT_RAW; + props.max_brightness = 100; + bd = backlight_device_register("nv_backlight", connector->kdev, + nv_encoder, ops, &props); + if (IS_ERR(bd)) + return PTR_ERR(bd); + + drm->backlight = bd; + bd->props.brightness = bd->ops->get_brightness(bd); + backlight_update_status(bd); + return 0; +} + +int +nouveau_backlight_init(struct drm_device *dev) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvif_device *device = &drm->device; + struct drm_connector *connector; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS && + connector->connector_type != DRM_MODE_CONNECTOR_eDP) + continue; + + switch (device->info.family) { + case NV_DEVICE_INFO_V0_CURIE: + return nv40_backlight_init(connector); + case NV_DEVICE_INFO_V0_TESLA: + case NV_DEVICE_INFO_V0_FERMI: + case NV_DEVICE_INFO_V0_KEPLER: + return nv50_backlight_init(connector); + default: + break; + } + } + + + return 0; +} + +void +nouveau_backlight_exit(struct drm_device *dev) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + + if (drm->backlight) { + backlight_device_unregister(drm->backlight); + drm->backlight = NULL; + } +} diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_bios.c b/kernel/drivers/gpu/drm/nouveau/nouveau_bios.c new file mode 100644 index 000000000..0190b69bb --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -0,0 +1,2127 @@ +/* + * 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 "nouveau_drm.h" +#include "nouveau_reg.h" +#include "dispnv04/hw.h" +#include "nouveau_encoder.h" + +#include +#include + +/* these defines are made up */ +#define NV_CIO_CRE_44_HEADA 0x0 +#define NV_CIO_CRE_44_HEADB 0x3 +#define FEATURE_MOBILE 0x10 /* also FEATURE_QUADRO for BMP */ + +#define EDID1_LEN 128 + +#define BIOSLOG(sip, fmt, arg...) NV_DEBUG(sip->dev, fmt, ##arg) +#define LOG_OLD_VALUE(x) + +struct init_exec { + bool execute; + bool repeat; +}; + +static bool nv_cksum(const uint8_t *data, unsigned int length) +{ + /* + * There's a few checksums in the BIOS, so here's a generic checking + * function. + */ + int i; + uint8_t sum = 0; + + for (i = 0; i < length; i++) + sum += data[i]; + + if (sum) + return true; + + return false; +} + +static uint16_t clkcmptable(struct nvbios *bios, uint16_t clktable, int pxclk) +{ + int compare_record_len, i = 0; + uint16_t compareclk, scriptptr = 0; + + if (bios->major_version < 5) /* pre BIT */ + compare_record_len = 3; + else + compare_record_len = 4; + + do { + compareclk = ROM16(bios->data[clktable + compare_record_len * i]); + if (pxclk >= compareclk * 10) { + if (bios->major_version < 5) { + uint8_t tmdssub = bios->data[clktable + 2 + compare_record_len * i]; + scriptptr = ROM16(bios->data[bios->init_script_tbls_ptr + tmdssub * 2]); + } else + scriptptr = ROM16(bios->data[clktable + 2 + compare_record_len * i]); + break; + } + i++; + } while (compareclk); + + return scriptptr; +} + +static void +run_digital_op_script(struct drm_device *dev, uint16_t scriptptr, + struct dcb_output *dcbent, int head, bool dl) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + + NV_INFO(drm, "0x%04X: Parsing digital output script table\n", + scriptptr); + NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_44, head ? NV_CIO_CRE_44_HEADB : + NV_CIO_CRE_44_HEADA); + nouveau_bios_run_init_table(dev, scriptptr, dcbent, head); + + nv04_dfp_bind_head(dev, dcbent, head, dl); +} + +static int call_lvds_manufacturer_script(struct drm_device *dev, struct dcb_output *dcbent, int head, enum LVDS_script script) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvbios *bios = &drm->vbios; + uint8_t sub = bios->data[bios->fp.xlated_entry + script] + (bios->fp.link_c_increment && dcbent->or & DCB_OUTPUT_C ? 1 : 0); + uint16_t scriptofs = ROM16(bios->data[bios->init_script_tbls_ptr + sub * 2]); + + if (!bios->fp.xlated_entry || !sub || !scriptofs) + return -EINVAL; + + run_digital_op_script(dev, scriptofs, dcbent, head, bios->fp.dual_link); + + if (script == LVDS_PANEL_OFF) { + /* off-on delay in ms */ + mdelay(ROM16(bios->data[bios->fp.xlated_entry + 7])); + } +#ifdef __powerpc__ + /* Powerbook specific quirks */ + if (script == LVDS_RESET && + (dev->pdev->device == 0x0179 || dev->pdev->device == 0x0189 || + dev->pdev->device == 0x0329)) + nv_write_tmds(dev, dcbent->or, 0, 0x02, 0x72); +#endif + + return 0; +} + +static int run_lvds_table(struct drm_device *dev, struct dcb_output *dcbent, int head, enum LVDS_script script, int pxclk) +{ + /* + * The BIT LVDS table's header has the information to setup the + * necessary registers. Following the standard 4 byte header are: + * A bitmask byte and a dual-link transition pxclk value for use in + * selecting the init script when not using straps; 4 script pointers + * for panel power, selected by output and on/off; and 8 table pointers + * for panel init, the needed one determined by output, and bits in the + * conf byte. These tables are similar to the TMDS tables, consisting + * of a list of pxclks and script pointers. + */ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvbios *bios = &drm->vbios; + unsigned int outputset = (dcbent->or == 4) ? 1 : 0; + uint16_t scriptptr = 0, clktable; + + /* + * For now we assume version 3.0 table - g80 support will need some + * changes + */ + + switch (script) { + case LVDS_INIT: + return -ENOSYS; + case LVDS_BACKLIGHT_ON: + case LVDS_PANEL_ON: + scriptptr = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 7 + outputset * 2]); + break; + case LVDS_BACKLIGHT_OFF: + case LVDS_PANEL_OFF: + scriptptr = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 11 + outputset * 2]); + break; + case LVDS_RESET: + clktable = bios->fp.lvdsmanufacturerpointer + 15; + if (dcbent->or == 4) + clktable += 8; + + if (dcbent->lvdsconf.use_straps_for_mode) { + if (bios->fp.dual_link) + clktable += 4; + if (bios->fp.if_is_24bit) + clktable += 2; + } else { + /* using EDID */ + int cmpval_24bit = (dcbent->or == 4) ? 4 : 1; + + if (bios->fp.dual_link) { + clktable += 4; + cmpval_24bit <<= 1; + } + + if (bios->fp.strapless_is_24bit & cmpval_24bit) + clktable += 2; + } + + clktable = ROM16(bios->data[clktable]); + if (!clktable) { + NV_ERROR(drm, "Pixel clock comparison table not found\n"); + return -ENOENT; + } + scriptptr = clkcmptable(bios, clktable, pxclk); + } + + if (!scriptptr) { + NV_ERROR(drm, "LVDS output init script not found\n"); + return -ENOENT; + } + run_digital_op_script(dev, scriptptr, dcbent, head, bios->fp.dual_link); + + return 0; +} + +int call_lvds_script(struct drm_device *dev, struct dcb_output *dcbent, int head, enum LVDS_script script, int pxclk) +{ + /* + * LVDS operations are multiplexed in an effort to present a single API + * which works with two vastly differing underlying structures. + * This acts as the demux + */ + + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvif_device *device = &drm->device; + struct nvbios *bios = &drm->vbios; + uint8_t lvds_ver = bios->data[bios->fp.lvdsmanufacturerpointer]; + uint32_t sel_clk_binding, sel_clk; + int ret; + + if (bios->fp.last_script_invoc == (script << 1 | head) || !lvds_ver || + (lvds_ver >= 0x30 && script == LVDS_INIT)) + return 0; + + if (!bios->fp.lvds_init_run) { + bios->fp.lvds_init_run = true; + call_lvds_script(dev, dcbent, head, LVDS_INIT, pxclk); + } + + if (script == LVDS_PANEL_ON && bios->fp.reset_after_pclk_change) + call_lvds_script(dev, dcbent, head, LVDS_RESET, pxclk); + if (script == LVDS_RESET && bios->fp.power_off_for_reset) + call_lvds_script(dev, dcbent, head, LVDS_PANEL_OFF, pxclk); + + NV_INFO(drm, "Calling LVDS script %d:\n", script); + + /* don't let script change pll->head binding */ + sel_clk_binding = nvif_rd32(device, NV_PRAMDAC_SEL_CLK) & 0x50000; + + if (lvds_ver < 0x30) + ret = call_lvds_manufacturer_script(dev, dcbent, head, script); + else + ret = run_lvds_table(dev, dcbent, head, script, pxclk); + + bios->fp.last_script_invoc = (script << 1 | head); + + sel_clk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK) & ~0x50000; + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, sel_clk | sel_clk_binding); + /* some scripts set a value in NV_PBUS_POWERCTRL_2 and break video overlay */ + nvif_wr32(device, NV_PBUS_POWERCTRL_2, 0); + + return ret; +} + +struct lvdstableheader { + uint8_t lvds_ver, headerlen, recordlen; +}; + +static int parse_lvds_manufacturer_table_header(struct drm_device *dev, struct nvbios *bios, struct lvdstableheader *lth) +{ + /* + * BMP version (0xa) LVDS table has a simple header of version and + * record length. The BIT LVDS table has the typical BIT table header: + * version byte, header length byte, record length byte, and a byte for + * the maximum number of records that can be held in the table. + */ + + struct nouveau_drm *drm = nouveau_drm(dev); + uint8_t lvds_ver, headerlen, recordlen; + + memset(lth, 0, sizeof(struct lvdstableheader)); + + if (bios->fp.lvdsmanufacturerpointer == 0x0) { + NV_ERROR(drm, "Pointer to LVDS manufacturer table invalid\n"); + return -EINVAL; + } + + lvds_ver = bios->data[bios->fp.lvdsmanufacturerpointer]; + + switch (lvds_ver) { + case 0x0a: /* pre NV40 */ + headerlen = 2; + recordlen = bios->data[bios->fp.lvdsmanufacturerpointer + 1]; + break; + case 0x30: /* NV4x */ + headerlen = bios->data[bios->fp.lvdsmanufacturerpointer + 1]; + if (headerlen < 0x1f) { + NV_ERROR(drm, "LVDS table header not understood\n"); + return -EINVAL; + } + recordlen = bios->data[bios->fp.lvdsmanufacturerpointer + 2]; + break; + case 0x40: /* G80/G90 */ + headerlen = bios->data[bios->fp.lvdsmanufacturerpointer + 1]; + if (headerlen < 0x7) { + NV_ERROR(drm, "LVDS table header not understood\n"); + return -EINVAL; + } + recordlen = bios->data[bios->fp.lvdsmanufacturerpointer + 2]; + break; + default: + NV_ERROR(drm, + "LVDS table revision %d.%d not currently supported\n", + lvds_ver >> 4, lvds_ver & 0xf); + return -ENOSYS; + } + + lth->lvds_ver = lvds_ver; + lth->headerlen = headerlen; + lth->recordlen = recordlen; + + return 0; +} + +static int +get_fp_strap(struct drm_device *dev, struct nvbios *bios) +{ + struct nvif_device *device = &nouveau_drm(dev)->device; + + /* + * The fp strap is normally dictated by the "User Strap" in + * PEXTDEV_BOOT_0[20:16], but on BMP cards when bit 2 of the + * Internal_Flags struct at 0x48 is set, the user strap gets overriden + * by the PCI subsystem ID during POST, but not before the previous user + * strap has been committed to CR58 for CR57=0xf on head A, which may be + * read and used instead + */ + + if (bios->major_version < 5 && bios->data[0x48] & 0x4) + return NVReadVgaCrtc5758(dev, 0, 0xf) & 0xf; + + if (device->info.family >= NV_DEVICE_INFO_V0_TESLA) + return (nvif_rd32(device, NV_PEXTDEV_BOOT_0) >> 24) & 0xf; + else + return (nvif_rd32(device, NV_PEXTDEV_BOOT_0) >> 16) & 0xf; +} + +static int parse_fp_mode_table(struct drm_device *dev, struct nvbios *bios) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + uint8_t *fptable; + uint8_t fptable_ver, headerlen = 0, recordlen, fpentries = 0xf, fpindex; + int ret, ofs, fpstrapping; + struct lvdstableheader lth; + + if (bios->fp.fptablepointer == 0x0) { + /* Apple cards don't have the fp table; the laptops use DDC */ + /* The table is also missing on some x86 IGPs */ +#ifndef __powerpc__ + NV_ERROR(drm, "Pointer to flat panel table invalid\n"); +#endif + bios->digital_min_front_porch = 0x4b; + return 0; + } + + fptable = &bios->data[bios->fp.fptablepointer]; + fptable_ver = fptable[0]; + + switch (fptable_ver) { + /* + * BMP version 0x5.0x11 BIOSen have version 1 like tables, but no + * version field, and miss one of the spread spectrum/PWM bytes. + * This could affect early GF2Go parts (not seen any appropriate ROMs + * though). Here we assume that a version of 0x05 matches this case + * (combining with a BMP version check would be better), as the + * common case for the panel type field is 0x0005, and that is in + * fact what we are reading the first byte of. + */ + case 0x05: /* some NV10, 11, 15, 16 */ + recordlen = 42; + ofs = -1; + break; + case 0x10: /* some NV15/16, and NV11+ */ + recordlen = 44; + ofs = 0; + break; + case 0x20: /* NV40+ */ + headerlen = fptable[1]; + recordlen = fptable[2]; + fpentries = fptable[3]; + /* + * fptable[4] is the minimum + * RAMDAC_FP_HCRTC -> RAMDAC_FP_HSYNC_START gap + */ + bios->digital_min_front_porch = fptable[4]; + ofs = -7; + break; + default: + NV_ERROR(drm, + "FP table revision %d.%d not currently supported\n", + fptable_ver >> 4, fptable_ver & 0xf); + return -ENOSYS; + } + + if (!bios->is_mobile) /* !mobile only needs digital_min_front_porch */ + return 0; + + ret = parse_lvds_manufacturer_table_header(dev, bios, <h); + if (ret) + return ret; + + if (lth.lvds_ver == 0x30 || lth.lvds_ver == 0x40) { + bios->fp.fpxlatetableptr = bios->fp.lvdsmanufacturerpointer + + lth.headerlen + 1; + bios->fp.xlatwidth = lth.recordlen; + } + if (bios->fp.fpxlatetableptr == 0x0) { + NV_ERROR(drm, "Pointer to flat panel xlat table invalid\n"); + return -EINVAL; + } + + fpstrapping = get_fp_strap(dev, bios); + + fpindex = bios->data[bios->fp.fpxlatetableptr + + fpstrapping * bios->fp.xlatwidth]; + + if (fpindex > fpentries) { + NV_ERROR(drm, "Bad flat panel table index\n"); + return -ENOENT; + } + + /* nv4x cards need both a strap value and fpindex of 0xf to use DDC */ + if (lth.lvds_ver > 0x10) + bios->fp_no_ddc = fpstrapping != 0xf || fpindex != 0xf; + + /* + * If either the strap or xlated fpindex value are 0xf there is no + * panel using a strap-derived bios mode present. this condition + * includes, but is different from, the DDC panel indicator above + */ + if (fpstrapping == 0xf || fpindex == 0xf) + return 0; + + bios->fp.mode_ptr = bios->fp.fptablepointer + headerlen + + recordlen * fpindex + ofs; + + NV_INFO(drm, "BIOS FP mode: %dx%d (%dkHz pixel clock)\n", + ROM16(bios->data[bios->fp.mode_ptr + 11]) + 1, + ROM16(bios->data[bios->fp.mode_ptr + 25]) + 1, + ROM16(bios->data[bios->fp.mode_ptr + 7]) * 10); + + return 0; +} + +bool nouveau_bios_fp_mode(struct drm_device *dev, struct drm_display_mode *mode) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvbios *bios = &drm->vbios; + uint8_t *mode_entry = &bios->data[bios->fp.mode_ptr]; + + if (!mode) /* just checking whether we can produce a mode */ + return bios->fp.mode_ptr; + + memset(mode, 0, sizeof(struct drm_display_mode)); + /* + * For version 1.0 (version in byte 0): + * bytes 1-2 are "panel type", including bits on whether Colour/mono, + * single/dual link, and type (TFT etc.) + * bytes 3-6 are bits per colour in RGBX + */ + mode->clock = ROM16(mode_entry[7]) * 10; + /* bytes 9-10 is HActive */ + mode->hdisplay = ROM16(mode_entry[11]) + 1; + /* + * bytes 13-14 is HValid Start + * bytes 15-16 is HValid End + */ + mode->hsync_start = ROM16(mode_entry[17]) + 1; + mode->hsync_end = ROM16(mode_entry[19]) + 1; + mode->htotal = ROM16(mode_entry[21]) + 1; + /* bytes 23-24, 27-30 similarly, but vertical */ + mode->vdisplay = ROM16(mode_entry[25]) + 1; + mode->vsync_start = ROM16(mode_entry[31]) + 1; + mode->vsync_end = ROM16(mode_entry[33]) + 1; + mode->vtotal = ROM16(mode_entry[35]) + 1; + mode->flags |= (mode_entry[37] & 0x10) ? + DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC; + mode->flags |= (mode_entry[37] & 0x1) ? + DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC; + /* + * bytes 38-39 relate to spread spectrum settings + * bytes 40-43 are something to do with PWM + */ + + mode->status = MODE_OK; + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; + drm_mode_set_name(mode); + return bios->fp.mode_ptr; +} + +int nouveau_bios_parse_lvds_table(struct drm_device *dev, int pxclk, bool *dl, bool *if_is_24bit) +{ + /* + * The LVDS table header is (mostly) described in + * parse_lvds_manufacturer_table_header(): the BIT header additionally + * contains the dual-link transition pxclk (in 10s kHz), at byte 5 - if + * straps are not being used for the panel, this specifies the frequency + * at which modes should be set up in the dual link style. + * + * Following the header, the BMP (ver 0xa) table has several records, + * indexed by a separate xlat table, indexed in turn by the fp strap in + * EXTDEV_BOOT. Each record had a config byte, followed by 6 script + * numbers for use by INIT_SUB which controlled panel init and power, + * and finally a dword of ms to sleep between power off and on + * operations. + * + * In the BIT versions, the table following the header serves as an + * integrated config and xlat table: the records in the table are + * indexed by the FP strap nibble in EXTDEV_BOOT, and each record has + * two bytes - the first as a config byte, the second for indexing the + * fp mode table pointed to by the BIT 'D' table + * + * DDC is not used until after card init, so selecting the correct table + * entry and setting the dual link flag for EDID equipped panels, + * requiring tests against the native-mode pixel clock, cannot be done + * until later, when this function should be called with non-zero pxclk + */ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvbios *bios = &drm->vbios; + int fpstrapping = get_fp_strap(dev, bios), lvdsmanufacturerindex = 0; + struct lvdstableheader lth; + uint16_t lvdsofs; + int ret, chip_version = bios->chip_version; + + ret = parse_lvds_manufacturer_table_header(dev, bios, <h); + if (ret) + return ret; + + switch (lth.lvds_ver) { + case 0x0a: /* pre NV40 */ + lvdsmanufacturerindex = bios->data[ + bios->fp.fpxlatemanufacturertableptr + + fpstrapping]; + + /* we're done if this isn't the EDID panel case */ + if (!pxclk) + break; + + if (chip_version < 0x25) { + /* nv17 behaviour + * + * It seems the old style lvds script pointer is reused + * to select 18/24 bit colour depth for EDID panels. + */ + lvdsmanufacturerindex = + (bios->legacy.lvds_single_a_script_ptr & 1) ? + 2 : 0; + if (pxclk >= bios->fp.duallink_transition_clk) + lvdsmanufacturerindex++; + } else if (chip_version < 0x30) { + /* nv28 behaviour (off-chip encoder) + * + * nv28 does a complex dance of first using byte 121 of + * the EDID to choose the lvdsmanufacturerindex, then + * later attempting to match the EDID manufacturer and + * product IDs in a table (signature 'pidt' (panel id + * table?)), setting an lvdsmanufacturerindex of 0 and + * an fp strap of the match index (or 0xf if none) + */ + lvdsmanufacturerindex = 0; + } else { + /* nv31, nv34 behaviour */ + lvdsmanufacturerindex = 0; + if (pxclk >= bios->fp.duallink_transition_clk) + lvdsmanufacturerindex = 2; + if (pxclk >= 140000) + lvdsmanufacturerindex = 3; + } + + /* + * nvidia set the high nibble of (cr57=f, cr58) to + * lvdsmanufacturerindex in this case; we don't + */ + break; + case 0x30: /* NV4x */ + case 0x40: /* G80/G90 */ + lvdsmanufacturerindex = fpstrapping; + break; + default: + NV_ERROR(drm, "LVDS table revision not currently supported\n"); + return -ENOSYS; + } + + lvdsofs = bios->fp.xlated_entry = bios->fp.lvdsmanufacturerpointer + lth.headerlen + lth.recordlen * lvdsmanufacturerindex; + switch (lth.lvds_ver) { + case 0x0a: + bios->fp.power_off_for_reset = bios->data[lvdsofs] & 1; + bios->fp.reset_after_pclk_change = bios->data[lvdsofs] & 2; + bios->fp.dual_link = bios->data[lvdsofs] & 4; + bios->fp.link_c_increment = bios->data[lvdsofs] & 8; + *if_is_24bit = bios->data[lvdsofs] & 16; + break; + case 0x30: + case 0x40: + /* + * No sign of the "power off for reset" or "reset for panel + * on" bits, but it's safer to assume we should + */ + bios->fp.power_off_for_reset = true; + bios->fp.reset_after_pclk_change = true; + + /* + * It's ok lvdsofs is wrong for nv4x edid case; dual_link is + * over-written, and if_is_24bit isn't used + */ + bios->fp.dual_link = bios->data[lvdsofs] & 1; + bios->fp.if_is_24bit = bios->data[lvdsofs] & 2; + bios->fp.strapless_is_24bit = bios->data[bios->fp.lvdsmanufacturerpointer + 4]; + bios->fp.duallink_transition_clk = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 5]) * 10; + break; + } + + /* set dual_link flag for EDID case */ + if (pxclk && (chip_version < 0x25 || chip_version > 0x28)) + bios->fp.dual_link = (pxclk >= bios->fp.duallink_transition_clk); + + *dl = bios->fp.dual_link; + + return 0; +} + +int run_tmds_table(struct drm_device *dev, struct dcb_output *dcbent, int head, int pxclk) +{ + /* + * the pxclk parameter is in kHz + * + * This runs the TMDS regs setting code found on BIT bios cards + * + * For ffs(or) == 1 use the first table, for ffs(or) == 2 and + * ffs(or) == 3, use the second. + */ + + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvif_device *device = &drm->device; + struct nvbios *bios = &drm->vbios; + int cv = bios->chip_version; + uint16_t clktable = 0, scriptptr; + uint32_t sel_clk_binding, sel_clk; + + /* pre-nv17 off-chip tmds uses scripts, post nv17 doesn't */ + if (cv >= 0x17 && cv != 0x1a && cv != 0x20 && + dcbent->location != DCB_LOC_ON_CHIP) + return 0; + + switch (ffs(dcbent->or)) { + case 1: + clktable = bios->tmds.output0_script_ptr; + break; + case 2: + case 3: + clktable = bios->tmds.output1_script_ptr; + break; + } + + if (!clktable) { + NV_ERROR(drm, "Pixel clock comparison table not found\n"); + return -EINVAL; + } + + scriptptr = clkcmptable(bios, clktable, pxclk); + + if (!scriptptr) { + NV_ERROR(drm, "TMDS output init script not found\n"); + return -ENOENT; + } + + /* don't let script change pll->head binding */ + sel_clk_binding = nvif_rd32(device, NV_PRAMDAC_SEL_CLK) & 0x50000; + run_digital_op_script(dev, scriptptr, dcbent, head, pxclk >= 165000); + sel_clk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK) & ~0x50000; + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, sel_clk | sel_clk_binding); + + return 0; +} + +static void parse_script_table_pointers(struct nvbios *bios, uint16_t offset) +{ + /* + * Parses the init table segment for pointers used in script execution. + * + * offset + 0 (16 bits): init script tables pointer + * offset + 2 (16 bits): macro index table pointer + * offset + 4 (16 bits): macro table pointer + * offset + 6 (16 bits): condition table pointer + * offset + 8 (16 bits): io condition table pointer + * offset + 10 (16 bits): io flag condition table pointer + * offset + 12 (16 bits): init function table pointer + */ + + bios->init_script_tbls_ptr = ROM16(bios->data[offset]); +} + +static int parse_bit_A_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry) +{ + /* + * Parses the load detect values for g80 cards. + * + * offset + 0 (16 bits): loadval table pointer + */ + + struct nouveau_drm *drm = nouveau_drm(dev); + uint16_t load_table_ptr; + uint8_t version, headerlen, entrylen, num_entries; + + if (bitentry->length != 3) { + NV_ERROR(drm, "Do not understand BIT A table\n"); + return -EINVAL; + } + + load_table_ptr = ROM16(bios->data[bitentry->offset]); + + if (load_table_ptr == 0x0) { + NV_DEBUG(drm, "Pointer to BIT loadval table invalid\n"); + return -EINVAL; + } + + version = bios->data[load_table_ptr]; + + if (version != 0x10) { + NV_ERROR(drm, "BIT loadval table version %d.%d not supported\n", + version >> 4, version & 0xF); + return -ENOSYS; + } + + headerlen = bios->data[load_table_ptr + 1]; + entrylen = bios->data[load_table_ptr + 2]; + num_entries = bios->data[load_table_ptr + 3]; + + if (headerlen != 4 || entrylen != 4 || num_entries != 2) { + NV_ERROR(drm, "Do not understand BIT loadval table\n"); + return -EINVAL; + } + + /* First entry is normal dac, 2nd tv-out perhaps? */ + bios->dactestval = ROM32(bios->data[load_table_ptr + headerlen]) & 0x3ff; + + return 0; +} + +static int parse_bit_display_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry) +{ + /* + * Parses the flat panel table segment that the bit entry points to. + * Starting at bitentry->offset: + * + * offset + 0 (16 bits): ??? table pointer - seems to have 18 byte + * records beginning with a freq. + * offset + 2 (16 bits): mode table pointer + */ + struct nouveau_drm *drm = nouveau_drm(dev); + + if (bitentry->length != 4) { + NV_ERROR(drm, "Do not understand BIT display table\n"); + return -EINVAL; + } + + bios->fp.fptablepointer = ROM16(bios->data[bitentry->offset + 2]); + + return 0; +} + +static int parse_bit_init_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry) +{ + /* + * Parses the init table segment that the bit entry points to. + * + * See parse_script_table_pointers for layout + */ + struct nouveau_drm *drm = nouveau_drm(dev); + + if (bitentry->length < 14) { + NV_ERROR(drm, "Do not understand init table\n"); + return -EINVAL; + } + + parse_script_table_pointers(bios, bitentry->offset); + return 0; +} + +static int parse_bit_i_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry) +{ + /* + * BIT 'i' (info?) table + * + * offset + 0 (32 bits): BIOS version dword (as in B table) + * offset + 5 (8 bits): BIOS feature byte (same as for BMP?) + * offset + 13 (16 bits): pointer to table containing DAC load + * detection comparison values + * + * There's other things in the table, purpose unknown + */ + + struct nouveau_drm *drm = nouveau_drm(dev); + uint16_t daccmpoffset; + uint8_t dacver, dacheaderlen; + + if (bitentry->length < 6) { + NV_ERROR(drm, "BIT i table too short for needed information\n"); + return -EINVAL; + } + + /* + * bit 4 seems to indicate a mobile bios (doesn't suffer from BMP's + * Quadro identity crisis), other bits possibly as for BMP feature byte + */ + bios->feature_byte = bios->data[bitentry->offset + 5]; + bios->is_mobile = bios->feature_byte & FEATURE_MOBILE; + + if (bitentry->length < 15) { + NV_WARN(drm, "BIT i table not long enough for DAC load " + "detection comparison table\n"); + return -EINVAL; + } + + daccmpoffset = ROM16(bios->data[bitentry->offset + 13]); + + /* doesn't exist on g80 */ + if (!daccmpoffset) + return 0; + + /* + * The first value in the table, following the header, is the + * comparison value, the second entry is a comparison value for + * TV load detection. + */ + + dacver = bios->data[daccmpoffset]; + dacheaderlen = bios->data[daccmpoffset + 1]; + + if (dacver != 0x00 && dacver != 0x10) { + NV_WARN(drm, "DAC load detection comparison table version " + "%d.%d not known\n", dacver >> 4, dacver & 0xf); + return -ENOSYS; + } + + bios->dactestval = ROM32(bios->data[daccmpoffset + dacheaderlen]); + bios->tvdactestval = ROM32(bios->data[daccmpoffset + dacheaderlen + 4]); + + return 0; +} + +static int parse_bit_lvds_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry) +{ + /* + * Parses the LVDS table segment that the bit entry points to. + * Starting at bitentry->offset: + * + * offset + 0 (16 bits): LVDS strap xlate table pointer + */ + + struct nouveau_drm *drm = nouveau_drm(dev); + + if (bitentry->length != 2) { + NV_ERROR(drm, "Do not understand BIT LVDS table\n"); + return -EINVAL; + } + + /* + * No idea if it's still called the LVDS manufacturer table, but + * the concept's close enough. + */ + bios->fp.lvdsmanufacturerpointer = ROM16(bios->data[bitentry->offset]); + + return 0; +} + +static int +parse_bit_M_tbl_entry(struct drm_device *dev, struct nvbios *bios, + struct bit_entry *bitentry) +{ + /* + * offset + 2 (8 bits): number of options in an + * INIT_RAM_RESTRICT_ZM_REG_GROUP opcode option set + * offset + 3 (16 bits): pointer to strap xlate table for RAM + * restrict option selection + * + * There's a bunch of bits in this table other than the RAM restrict + * stuff that we don't use - their use currently unknown + */ + + /* + * Older bios versions don't have a sufficiently long table for + * what we want + */ + if (bitentry->length < 0x5) + return 0; + + if (bitentry->version < 2) { + bios->ram_restrict_group_count = bios->data[bitentry->offset + 2]; + bios->ram_restrict_tbl_ptr = ROM16(bios->data[bitentry->offset + 3]); + } else { + bios->ram_restrict_group_count = bios->data[bitentry->offset + 0]; + bios->ram_restrict_tbl_ptr = ROM16(bios->data[bitentry->offset + 1]); + } + + return 0; +} + +static int parse_bit_tmds_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry) +{ + /* + * Parses the pointer to the TMDS table + * + * Starting at bitentry->offset: + * + * offset + 0 (16 bits): TMDS table pointer + * + * The TMDS table is typically found just before the DCB table, with a + * characteristic signature of 0x11,0x13 (1.1 being version, 0x13 being + * length?) + * + * At offset +7 is a pointer to a script, which I don't know how to + * run yet. + * At offset +9 is a pointer to another script, likewise + * Offset +11 has a pointer to a table where the first word is a pxclk + * frequency and the second word a pointer to a script, which should be + * run if the comparison pxclk frequency is less than the pxclk desired. + * This repeats for decreasing comparison frequencies + * Offset +13 has a pointer to a similar table + * The selection of table (and possibly +7/+9 script) is dictated by + * "or" from the DCB. + */ + + struct nouveau_drm *drm = nouveau_drm(dev); + uint16_t tmdstableptr, script1, script2; + + if (bitentry->length != 2) { + NV_ERROR(drm, "Do not understand BIT TMDS table\n"); + return -EINVAL; + } + + tmdstableptr = ROM16(bios->data[bitentry->offset]); + if (!tmdstableptr) { + NV_ERROR(drm, "Pointer to TMDS table invalid\n"); + return -EINVAL; + } + + NV_INFO(drm, "TMDS table version %d.%d\n", + bios->data[tmdstableptr] >> 4, bios->data[tmdstableptr] & 0xf); + + /* nv50+ has v2.0, but we don't parse it atm */ + if (bios->data[tmdstableptr] != 0x11) + return -ENOSYS; + + /* + * These two scripts are odd: they don't seem to get run even when + * they are not stubbed. + */ + script1 = ROM16(bios->data[tmdstableptr + 7]); + script2 = ROM16(bios->data[tmdstableptr + 9]); + if (bios->data[script1] != 'q' || bios->data[script2] != 'q') + NV_WARN(drm, "TMDS table script pointers not stubbed\n"); + + bios->tmds.output0_script_ptr = ROM16(bios->data[tmdstableptr + 11]); + bios->tmds.output1_script_ptr = ROM16(bios->data[tmdstableptr + 13]); + + return 0; +} + +struct bit_table { + const char id; + int (* const parse_fn)(struct drm_device *, struct nvbios *, struct bit_entry *); +}; + +#define BIT_TABLE(id, funcid) ((struct bit_table){ id, parse_bit_##funcid##_tbl_entry }) + +int +bit_table(struct drm_device *dev, u8 id, struct bit_entry *bit) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvbios *bios = &drm->vbios; + u8 entries, *entry; + + if (bios->type != NVBIOS_BIT) + return -ENODEV; + + entries = bios->data[bios->offset + 10]; + entry = &bios->data[bios->offset + 12]; + while (entries--) { + if (entry[0] == id) { + bit->id = entry[0]; + bit->version = entry[1]; + bit->length = ROM16(entry[2]); + bit->offset = ROM16(entry[4]); + bit->data = ROMPTR(dev, entry[4]); + return 0; + } + + entry += bios->data[bios->offset + 9]; + } + + return -ENOENT; +} + +static int +parse_bit_table(struct nvbios *bios, const uint16_t bitoffset, + struct bit_table *table) +{ + struct drm_device *dev = bios->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct bit_entry bitentry; + + if (bit_table(dev, table->id, &bitentry) == 0) + return table->parse_fn(dev, bios, &bitentry); + + NV_INFO(drm, "BIT table '%c' not found\n", table->id); + return -ENOSYS; +} + +static int +parse_bit_structure(struct nvbios *bios, const uint16_t bitoffset) +{ + int ret; + + /* + * The only restriction on parsing order currently is having 'i' first + * for use of bios->*_version or bios->feature_byte while parsing; + * functions shouldn't be actually *doing* anything apart from pulling + * data from the image into the bios struct, thus no interdependencies + */ + ret = parse_bit_table(bios, bitoffset, &BIT_TABLE('i', i)); + if (ret) /* info? */ + return ret; + if (bios->major_version >= 0x60) /* g80+ */ + parse_bit_table(bios, bitoffset, &BIT_TABLE('A', A)); + parse_bit_table(bios, bitoffset, &BIT_TABLE('D', display)); + ret = parse_bit_table(bios, bitoffset, &BIT_TABLE('I', init)); + if (ret) + return ret; + parse_bit_table(bios, bitoffset, &BIT_TABLE('M', M)); /* memory? */ + parse_bit_table(bios, bitoffset, &BIT_TABLE('L', lvds)); + parse_bit_table(bios, bitoffset, &BIT_TABLE('T', tmds)); + + return 0; +} + +static int parse_bmp_structure(struct drm_device *dev, struct nvbios *bios, unsigned int offset) +{ + /* + * Parses the BMP structure for useful things, but does not act on them + * + * offset + 5: BMP major version + * offset + 6: BMP minor version + * offset + 9: BMP feature byte + * offset + 10: BCD encoded BIOS version + * + * offset + 18: init script table pointer (for bios versions < 5.10h) + * offset + 20: extra init script table pointer (for bios + * versions < 5.10h) + * + * offset + 24: memory init table pointer (used on early bios versions) + * offset + 26: SDR memory sequencing setup data table + * offset + 28: DDR memory sequencing setup data table + * + * offset + 54: index of I2C CRTC pair to use for CRT output + * offset + 55: index of I2C CRTC pair to use for TV output + * offset + 56: index of I2C CRTC pair to use for flat panel output + * offset + 58: write CRTC index for I2C pair 0 + * offset + 59: read CRTC index for I2C pair 0 + * offset + 60: write CRTC index for I2C pair 1 + * offset + 61: read CRTC index for I2C pair 1 + * + * offset + 67: maximum internal PLL frequency (single stage PLL) + * offset + 71: minimum internal PLL frequency (single stage PLL) + * + * offset + 75: script table pointers, as described in + * parse_script_table_pointers + * + * offset + 89: TMDS single link output A table pointer + * offset + 91: TMDS single link output B table pointer + * offset + 95: LVDS single link output A table pointer + * offset + 105: flat panel timings table pointer + * offset + 107: flat panel strapping translation table pointer + * offset + 117: LVDS manufacturer panel config table pointer + * offset + 119: LVDS manufacturer strapping translation table pointer + * + * offset + 142: PLL limits table pointer + * + * offset + 156: minimum pixel clock for LVDS dual link + */ + + struct nouveau_drm *drm = nouveau_drm(dev); + uint8_t *bmp = &bios->data[offset], bmp_version_major, bmp_version_minor; + uint16_t bmplength; + uint16_t legacy_scripts_offset, legacy_i2c_offset; + + /* load needed defaults in case we can't parse this info */ + bios->digital_min_front_porch = 0x4b; + bios->fmaxvco = 256000; + bios->fminvco = 128000; + bios->fp.duallink_transition_clk = 90000; + + bmp_version_major = bmp[5]; + bmp_version_minor = bmp[6]; + + NV_INFO(drm, "BMP version %d.%d\n", + bmp_version_major, bmp_version_minor); + + /* + * Make sure that 0x36 is blank and can't be mistaken for a DCB + * pointer on early versions + */ + if (bmp_version_major < 5) + *(uint16_t *)&bios->data[0x36] = 0; + + /* + * Seems that the minor version was 1 for all major versions prior + * to 5. Version 6 could theoretically exist, but I suspect BIT + * happened instead. + */ + if ((bmp_version_major < 5 && bmp_version_minor != 1) || bmp_version_major > 5) { + NV_ERROR(drm, "You have an unsupported BMP version. " + "Please send in your bios\n"); + return -ENOSYS; + } + + if (bmp_version_major == 0) + /* nothing that's currently useful in this version */ + return 0; + else if (bmp_version_major == 1) + bmplength = 44; /* exact for 1.01 */ + else if (bmp_version_major == 2) + bmplength = 48; /* exact for 2.01 */ + else if (bmp_version_major == 3) + bmplength = 54; + /* guessed - mem init tables added in this version */ + else if (bmp_version_major == 4 || bmp_version_minor < 0x1) + /* don't know if 5.0 exists... */ + bmplength = 62; + /* guessed - BMP I2C indices added in version 4*/ + else if (bmp_version_minor < 0x6) + bmplength = 67; /* exact for 5.01 */ + else if (bmp_version_minor < 0x10) + bmplength = 75; /* exact for 5.06 */ + else if (bmp_version_minor == 0x10) + bmplength = 89; /* exact for 5.10h */ + else if (bmp_version_minor < 0x14) + bmplength = 118; /* exact for 5.11h */ + else if (bmp_version_minor < 0x24) + /* + * Not sure of version where pll limits came in; + * certainly exist by 0x24 though. + */ + /* length not exact: this is long enough to get lvds members */ + bmplength = 123; + else if (bmp_version_minor < 0x27) + /* + * Length not exact: this is long enough to get pll limit + * member + */ + bmplength = 144; + else + /* + * Length not exact: this is long enough to get dual link + * transition clock. + */ + bmplength = 158; + + /* checksum */ + if (nv_cksum(bmp, 8)) { + NV_ERROR(drm, "Bad BMP checksum\n"); + return -EINVAL; + } + + /* + * Bit 4 seems to indicate either a mobile bios or a quadro card -- + * mobile behaviour consistent (nv11+), quadro only seen nv18gl-nv36gl + * (not nv10gl), bit 5 that the flat panel tables are present, and + * bit 6 a tv bios. + */ + bios->feature_byte = bmp[9]; + + if (bmp_version_major < 5 || bmp_version_minor < 0x10) + bios->old_style_init = true; + legacy_scripts_offset = 18; + if (bmp_version_major < 2) + legacy_scripts_offset -= 4; + bios->init_script_tbls_ptr = ROM16(bmp[legacy_scripts_offset]); + bios->extra_init_script_tbl_ptr = ROM16(bmp[legacy_scripts_offset + 2]); + + if (bmp_version_major > 2) { /* appears in BMP 3 */ + bios->legacy.mem_init_tbl_ptr = ROM16(bmp[24]); + bios->legacy.sdr_seq_tbl_ptr = ROM16(bmp[26]); + bios->legacy.ddr_seq_tbl_ptr = ROM16(bmp[28]); + } + + legacy_i2c_offset = 0x48; /* BMP version 2 & 3 */ + if (bmplength > 61) + legacy_i2c_offset = offset + 54; + bios->legacy.i2c_indices.crt = bios->data[legacy_i2c_offset]; + bios->legacy.i2c_indices.tv = bios->data[legacy_i2c_offset + 1]; + bios->legacy.i2c_indices.panel = bios->data[legacy_i2c_offset + 2]; + + if (bmplength > 74) { + bios->fmaxvco = ROM32(bmp[67]); + bios->fminvco = ROM32(bmp[71]); + } + if (bmplength > 88) + parse_script_table_pointers(bios, offset + 75); + if (bmplength > 94) { + bios->tmds.output0_script_ptr = ROM16(bmp[89]); + bios->tmds.output1_script_ptr = ROM16(bmp[91]); + /* + * Never observed in use with lvds scripts, but is reused for + * 18/24 bit panel interface default for EDID equipped panels + * (if_is_24bit not set directly to avoid any oscillation). + */ + bios->legacy.lvds_single_a_script_ptr = ROM16(bmp[95]); + } + if (bmplength > 108) { + bios->fp.fptablepointer = ROM16(bmp[105]); + bios->fp.fpxlatetableptr = ROM16(bmp[107]); + bios->fp.xlatwidth = 1; + } + if (bmplength > 120) { + bios->fp.lvdsmanufacturerpointer = ROM16(bmp[117]); + bios->fp.fpxlatemanufacturertableptr = ROM16(bmp[119]); + } +#if 0 + if (bmplength > 143) + bios->pll_limit_tbl_ptr = ROM16(bmp[142]); +#endif + + if (bmplength > 157) + bios->fp.duallink_transition_clk = ROM16(bmp[156]) * 10; + + return 0; +} + +static uint16_t findstr(uint8_t *data, int n, const uint8_t *str, int len) +{ + int i, j; + + for (i = 0; i <= (n - len); i++) { + for (j = 0; j < len; j++) + if (data[i + j] != str[j]) + break; + if (j == len) + return i; + } + + return 0; +} + +void * +olddcb_table(struct drm_device *dev) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + u8 *dcb = NULL; + + if (drm->device.info.family > NV_DEVICE_INFO_V0_TNT) + dcb = ROMPTR(dev, drm->vbios.data[0x36]); + if (!dcb) { + NV_WARN(drm, "No DCB data found in VBIOS\n"); + return NULL; + } + + if (dcb[0] >= 0x42) { + NV_WARN(drm, "DCB version 0x%02x unknown\n", dcb[0]); + return NULL; + } else + if (dcb[0] >= 0x30) { + if (ROM32(dcb[6]) == 0x4edcbdcb) + return dcb; + } else + if (dcb[0] >= 0x20) { + if (ROM32(dcb[4]) == 0x4edcbdcb) + return dcb; + } else + if (dcb[0] >= 0x15) { + if (!memcmp(&dcb[-7], "DEV_REC", 7)) + return dcb; + } else { + /* + * v1.4 (some NV15/16, NV11+) seems the same as v1.5, but + * always has the same single (crt) entry, even when tv-out + * present, so the conclusion is this version cannot really + * be used. + * + * v1.2 tables (some NV6/10, and NV15+) normally have the + * same 5 entries, which are not specific to the card and so + * no use. + * + * v1.2 does have an I2C table that read_dcb_i2c_table can + * handle, but cards exist (nv11 in #14821) with a bad i2c + * table pointer, so use the indices parsed in + * parse_bmp_structure. + * + * v1.1 (NV5+, maybe some NV4) is entirely unhelpful + */ + NV_WARN(drm, "No useful DCB data in VBIOS\n"); + return NULL; + } + + NV_WARN(drm, "DCB header validation failed\n"); + return NULL; +} + +void * +olddcb_outp(struct drm_device *dev, u8 idx) +{ + u8 *dcb = olddcb_table(dev); + if (dcb && dcb[0] >= 0x30) { + if (idx < dcb[2]) + return dcb + dcb[1] + (idx * dcb[3]); + } else + if (dcb && dcb[0] >= 0x20) { + u8 *i2c = ROMPTR(dev, dcb[2]); + u8 *ent = dcb + 8 + (idx * 8); + if (i2c && ent < i2c) + return ent; + } else + if (dcb && dcb[0] >= 0x15) { + u8 *i2c = ROMPTR(dev, dcb[2]); + u8 *ent = dcb + 4 + (idx * 10); + if (i2c && ent < i2c) + return ent; + } + + return NULL; +} + +int +olddcb_outp_foreach(struct drm_device *dev, void *data, + int (*exec)(struct drm_device *, void *, int idx, u8 *outp)) +{ + int ret, idx = -1; + u8 *outp = NULL; + while ((outp = olddcb_outp(dev, ++idx))) { + if (ROM32(outp[0]) == 0x00000000) + break; /* seen on an NV11 with DCB v1.5 */ + if (ROM32(outp[0]) == 0xffffffff) + break; /* seen on an NV17 with DCB v2.0 */ + + if ((outp[0] & 0x0f) == DCB_OUTPUT_UNUSED) + continue; + if ((outp[0] & 0x0f) == DCB_OUTPUT_EOL) + break; + + ret = exec(dev, data, idx, outp); + if (ret) + return ret; + } + + return 0; +} + +u8 * +olddcb_conntab(struct drm_device *dev) +{ + u8 *dcb = olddcb_table(dev); + if (dcb && dcb[0] >= 0x30 && dcb[1] >= 0x16) { + u8 *conntab = ROMPTR(dev, dcb[0x14]); + if (conntab && conntab[0] >= 0x30 && conntab[0] <= 0x40) + return conntab; + } + return NULL; +} + +u8 * +olddcb_conn(struct drm_device *dev, u8 idx) +{ + u8 *conntab = olddcb_conntab(dev); + if (conntab && idx < conntab[2]) + return conntab + conntab[1] + (idx * conntab[3]); + return NULL; +} + +static struct dcb_output *new_dcb_entry(struct dcb_table *dcb) +{ + struct dcb_output *entry = &dcb->entry[dcb->entries]; + + memset(entry, 0, sizeof(struct dcb_output)); + entry->index = dcb->entries++; + + return entry; +} + +static void fabricate_dcb_output(struct dcb_table *dcb, int type, int i2c, + int heads, int or) +{ + struct dcb_output *entry = new_dcb_entry(dcb); + + entry->type = type; + entry->i2c_index = i2c; + entry->heads = heads; + if (type != DCB_OUTPUT_ANALOG) + entry->location = !DCB_LOC_ON_CHIP; /* ie OFF CHIP */ + entry->or = or; +} + +static bool +parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb, + uint32_t conn, uint32_t conf, struct dcb_output *entry) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + int link = 0; + + entry->type = conn & 0xf; + entry->i2c_index = (conn >> 4) & 0xf; + entry->heads = (conn >> 8) & 0xf; + entry->connector = (conn >> 12) & 0xf; + entry->bus = (conn >> 16) & 0xf; + entry->location = (conn >> 20) & 0x3; + entry->or = (conn >> 24) & 0xf; + + switch (entry->type) { + case DCB_OUTPUT_ANALOG: + /* + * Although the rest of a CRT conf dword is usually + * zeros, mac biosen have stuff there so we must mask + */ + entry->crtconf.maxfreq = (dcb->version < 0x30) ? + (conf & 0xffff) * 10 : + (conf & 0xff) * 10000; + break; + case DCB_OUTPUT_LVDS: + { + uint32_t mask; + if (conf & 0x1) + entry->lvdsconf.use_straps_for_mode = true; + if (dcb->version < 0x22) { + mask = ~0xd; + /* + * The laptop in bug 14567 lies and claims to not use + * straps when it does, so assume all DCB 2.0 laptops + * use straps, until a broken EDID using one is produced + */ + entry->lvdsconf.use_straps_for_mode = true; + /* + * Both 0x4 and 0x8 show up in v2.0 tables; assume they + * mean the same thing (probably wrong, but might work) + */ + if (conf & 0x4 || conf & 0x8) + entry->lvdsconf.use_power_scripts = true; + } else { + mask = ~0x7; + if (conf & 0x2) + entry->lvdsconf.use_acpi_for_edid = true; + if (conf & 0x4) + entry->lvdsconf.use_power_scripts = true; + entry->lvdsconf.sor.link = (conf & 0x00000030) >> 4; + link = entry->lvdsconf.sor.link; + } + if (conf & mask) { + /* + * Until we even try to use these on G8x, it's + * useless reporting unknown bits. They all are. + */ + if (dcb->version >= 0x40) + break; + + NV_ERROR(drm, "Unknown LVDS configuration bits, " + "please report\n"); + } + break; + } + case DCB_OUTPUT_TV: + { + if (dcb->version >= 0x30) + entry->tvconf.has_component_output = conf & (0x8 << 4); + else + entry->tvconf.has_component_output = false; + + break; + } + case DCB_OUTPUT_DP: + entry->dpconf.sor.link = (conf & 0x00000030) >> 4; + entry->extdev = (conf & 0x0000ff00) >> 8; + switch ((conf & 0x00e00000) >> 21) { + case 0: + entry->dpconf.link_bw = 162000; + break; + case 1: + entry->dpconf.link_bw = 270000; + break; + default: + entry->dpconf.link_bw = 540000; + break; + } + entry->dpconf.link_nr = (conf & 0x0f000000) >> 24; + if (dcb->version < 0x41) { + switch (entry->dpconf.link_nr) { + case 0xf: + entry->dpconf.link_nr = 4; + break; + case 0x3: + entry->dpconf.link_nr = 2; + break; + default: + entry->dpconf.link_nr = 1; + break; + } + } + link = entry->dpconf.sor.link; + entry->i2c_index += NV_I2C_AUX(0); + break; + case DCB_OUTPUT_TMDS: + if (dcb->version >= 0x40) { + entry->tmdsconf.sor.link = (conf & 0x00000030) >> 4; + entry->extdev = (conf & 0x0000ff00) >> 8; + link = entry->tmdsconf.sor.link; + } + else if (dcb->version >= 0x30) + entry->tmdsconf.slave_addr = (conf & 0x00000700) >> 8; + else if (dcb->version >= 0x22) + entry->tmdsconf.slave_addr = (conf & 0x00000070) >> 4; + break; + case DCB_OUTPUT_EOL: + /* weird g80 mobile type that "nv" treats as a terminator */ + dcb->entries--; + return false; + default: + break; + } + + if (dcb->version < 0x40) { + /* Normal entries consist of a single bit, but dual link has + * the next most significant bit set too + */ + entry->duallink_possible = + ((1 << (ffs(entry->or) - 1)) * 3 == entry->or); + } else { + entry->duallink_possible = (entry->sorconf.link == 3); + } + + /* unsure what DCB version introduces this, 3.0? */ + if (conf & 0x100000) + entry->i2c_upper_default = true; + + entry->hasht = (entry->location << 4) | entry->type; + entry->hashm = (entry->heads << 8) | (link << 6) | entry->or; + return true; +} + +static bool +parse_dcb15_entry(struct drm_device *dev, struct dcb_table *dcb, + uint32_t conn, uint32_t conf, struct dcb_output *entry) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + + switch (conn & 0x0000000f) { + case 0: + entry->type = DCB_OUTPUT_ANALOG; + break; + case 1: + entry->type = DCB_OUTPUT_TV; + break; + case 2: + case 4: + if (conn & 0x10) + entry->type = DCB_OUTPUT_LVDS; + else + entry->type = DCB_OUTPUT_TMDS; + break; + case 3: + entry->type = DCB_OUTPUT_LVDS; + break; + default: + NV_ERROR(drm, "Unknown DCB type %d\n", conn & 0x0000000f); + return false; + } + + entry->i2c_index = (conn & 0x0003c000) >> 14; + entry->heads = ((conn & 0x001c0000) >> 18) + 1; + entry->or = entry->heads; /* same as heads, hopefully safe enough */ + entry->location = (conn & 0x01e00000) >> 21; + entry->bus = (conn & 0x0e000000) >> 25; + entry->duallink_possible = false; + + switch (entry->type) { + case DCB_OUTPUT_ANALOG: + entry->crtconf.maxfreq = (conf & 0xffff) * 10; + break; + case DCB_OUTPUT_TV: + entry->tvconf.has_component_output = false; + break; + case DCB_OUTPUT_LVDS: + if ((conn & 0x00003f00) >> 8 != 0x10) + entry->lvdsconf.use_straps_for_mode = true; + entry->lvdsconf.use_power_scripts = true; + break; + default: + break; + } + + return true; +} + +static +void merge_like_dcb_entries(struct drm_device *dev, struct dcb_table *dcb) +{ + /* + * DCB v2.0 lists each output combination separately. + * Here we merge compatible entries to have fewer outputs, with + * more options + */ + + struct nouveau_drm *drm = nouveau_drm(dev); + int i, newentries = 0; + + for (i = 0; i < dcb->entries; i++) { + struct dcb_output *ient = &dcb->entry[i]; + int j; + + for (j = i + 1; j < dcb->entries; j++) { + struct dcb_output *jent = &dcb->entry[j]; + + if (jent->type == 100) /* already merged entry */ + continue; + + /* merge heads field when all other fields the same */ + if (jent->i2c_index == ient->i2c_index && + jent->type == ient->type && + jent->location == ient->location && + jent->or == ient->or) { + NV_INFO(drm, "Merging DCB entries %d and %d\n", + i, j); + ient->heads |= jent->heads; + jent->type = 100; /* dummy value */ + } + } + } + + /* Compact entries merged into others out of dcb */ + for (i = 0; i < dcb->entries; i++) { + if (dcb->entry[i].type == 100) + continue; + + if (newentries != i) { + dcb->entry[newentries] = dcb->entry[i]; + dcb->entry[newentries].index = newentries; + } + newentries++; + } + + dcb->entries = newentries; +} + +static bool +apply_dcb_encoder_quirks(struct drm_device *dev, int idx, u32 *conn, u32 *conf) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct dcb_table *dcb = &drm->vbios.dcb; + + /* Dell Precision M6300 + * DCB entry 2: 02025312 00000010 + * DCB entry 3: 02026312 00000020 + * + * Identical, except apparently a different connector on a + * different SOR link. Not a clue how we're supposed to know + * which one is in use if it even shares an i2c line... + * + * Ignore the connector on the second SOR link to prevent + * nasty problems until this is sorted (assuming it's not a + * VBIOS bug). + */ + if (nv_match_device(dev, 0x040d, 0x1028, 0x019b)) { + if (*conn == 0x02026312 && *conf == 0x00000020) + return false; + } + + /* GeForce3 Ti 200 + * + * DCB reports an LVDS output that should be TMDS: + * DCB entry 1: f2005014 ffffffff + */ + if (nv_match_device(dev, 0x0201, 0x1462, 0x8851)) { + if (*conn == 0xf2005014 && *conf == 0xffffffff) { + fabricate_dcb_output(dcb, DCB_OUTPUT_TMDS, 1, 1, 1); + return false; + } + } + + /* XFX GT-240X-YA + * + * So many things wrong here, replace the entire encoder table.. + */ + if (nv_match_device(dev, 0x0ca3, 0x1682, 0x3003)) { + if (idx == 0) { + *conn = 0x02001300; /* VGA, connector 1 */ + *conf = 0x00000028; + } else + if (idx == 1) { + *conn = 0x01010312; /* DVI, connector 0 */ + *conf = 0x00020030; + } else + if (idx == 2) { + *conn = 0x01010310; /* VGA, connector 0 */ + *conf = 0x00000028; + } else + if (idx == 3) { + *conn = 0x02022362; /* HDMI, connector 2 */ + *conf = 0x00020010; + } else { + *conn = 0x0000000e; /* EOL */ + *conf = 0x00000000; + } + } + + /* Some other twisted XFX board (rhbz#694914) + * + * The DVI/VGA encoder combo that's supposed to represent the + * DVI-I connector actually point at two different ones, and + * the HDMI connector ends up paired with the VGA instead. + * + * Connector table is missing anything for VGA at all, pointing it + * an invalid conntab entry 2 so we figure it out ourself. + */ + if (nv_match_device(dev, 0x0615, 0x1682, 0x2605)) { + if (idx == 0) { + *conn = 0x02002300; /* VGA, connector 2 */ + *conf = 0x00000028; + } else + if (idx == 1) { + *conn = 0x01010312; /* DVI, connector 0 */ + *conf = 0x00020030; + } else + if (idx == 2) { + *conn = 0x04020310; /* VGA, connector 0 */ + *conf = 0x00000028; + } else + if (idx == 3) { + *conn = 0x02021322; /* HDMI, connector 1 */ + *conf = 0x00020010; + } else { + *conn = 0x0000000e; /* EOL */ + *conf = 0x00000000; + } + } + + /* fdo#50830: connector indices for VGA and DVI-I are backwards */ + if (nv_match_device(dev, 0x0421, 0x3842, 0xc793)) { + if (idx == 0 && *conn == 0x02000300) + *conn = 0x02011300; + else + if (idx == 1 && *conn == 0x04011310) + *conn = 0x04000310; + else + if (idx == 2 && *conn == 0x02011312) + *conn = 0x02000312; + } + + return true; +} + +static void +fabricate_dcb_encoder_table(struct drm_device *dev, struct nvbios *bios) +{ + struct dcb_table *dcb = &bios->dcb; + int all_heads = (nv_two_heads(dev) ? 3 : 1); + +#ifdef __powerpc__ + /* Apple iMac G4 NV17 */ + if (of_machine_is_compatible("PowerMac4,5")) { + fabricate_dcb_output(dcb, DCB_OUTPUT_TMDS, 0, all_heads, 1); + fabricate_dcb_output(dcb, DCB_OUTPUT_ANALOG, 1, all_heads, 2); + return; + } +#endif + + /* Make up some sane defaults */ + fabricate_dcb_output(dcb, DCB_OUTPUT_ANALOG, + bios->legacy.i2c_indices.crt, 1, 1); + + if (nv04_tv_identify(dev, bios->legacy.i2c_indices.tv) >= 0) + fabricate_dcb_output(dcb, DCB_OUTPUT_TV, + bios->legacy.i2c_indices.tv, + all_heads, 0); + + else if (bios->tmds.output0_script_ptr || + bios->tmds.output1_script_ptr) + fabricate_dcb_output(dcb, DCB_OUTPUT_TMDS, + bios->legacy.i2c_indices.panel, + all_heads, 1); +} + +static int +parse_dcb_entry(struct drm_device *dev, void *data, int idx, u8 *outp) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct dcb_table *dcb = &drm->vbios.dcb; + u32 conf = (dcb->version >= 0x20) ? ROM32(outp[4]) : ROM32(outp[6]); + u32 conn = ROM32(outp[0]); + bool ret; + + if (apply_dcb_encoder_quirks(dev, idx, &conn, &conf)) { + struct dcb_output *entry = new_dcb_entry(dcb); + + NV_INFO(drm, "DCB outp %02d: %08x %08x\n", idx, conn, conf); + + if (dcb->version >= 0x20) + ret = parse_dcb20_entry(dev, dcb, conn, conf, entry); + else + ret = parse_dcb15_entry(dev, dcb, conn, conf, entry); + if (!ret) + return 1; /* stop parsing */ + + /* Ignore the I2C index for on-chip TV-out, as there + * are cards with bogus values (nv31m in bug 23212), + * and it's otherwise useless. + */ + if (entry->type == DCB_OUTPUT_TV && + entry->location == DCB_LOC_ON_CHIP) + entry->i2c_index = 0x0f; + } + + return 0; +} + +static void +dcb_fake_connectors(struct nvbios *bios) +{ + struct dcb_table *dcbt = &bios->dcb; + u8 map[16] = { }; + int i, idx = 0; + + /* heuristic: if we ever get a non-zero connector field, assume + * that all the indices are valid and we don't need fake them. + * + * and, as usual, a blacklist of boards with bad bios data.. + */ + if (!nv_match_device(bios->dev, 0x0392, 0x107d, 0x20a2)) { + for (i = 0; i < dcbt->entries; i++) { + if (dcbt->entry[i].connector) + return; + } + } + + /* no useful connector info available, we need to make it up + * ourselves. the rule here is: anything on the same i2c bus + * is considered to be on the same connector. any output + * without an associated i2c bus is assigned its own unique + * connector index. + */ + for (i = 0; i < dcbt->entries; i++) { + u8 i2c = dcbt->entry[i].i2c_index; + if (i2c == 0x0f) { + dcbt->entry[i].connector = idx++; + } else { + if (!map[i2c]) + map[i2c] = ++idx; + dcbt->entry[i].connector = map[i2c] - 1; + } + } + + /* if we created more than one connector, destroy the connector + * table - just in case it has random, rather than stub, entries. + */ + if (i > 1) { + u8 *conntab = olddcb_conntab(bios->dev); + if (conntab) + conntab[0] = 0x00; + } +} + +static int +parse_dcb_table(struct drm_device *dev, struct nvbios *bios) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct dcb_table *dcb = &bios->dcb; + u8 *dcbt, *conn; + int idx; + + dcbt = olddcb_table(dev); + if (!dcbt) { + /* handle pre-DCB boards */ + if (bios->type == NVBIOS_BMP) { + fabricate_dcb_encoder_table(dev, bios); + return 0; + } + + return -EINVAL; + } + + NV_INFO(drm, "DCB version %d.%d\n", dcbt[0] >> 4, dcbt[0] & 0xf); + + dcb->version = dcbt[0]; + olddcb_outp_foreach(dev, NULL, parse_dcb_entry); + + /* + * apart for v2.1+ not being known for requiring merging, this + * guarantees dcbent->index is the index of the entry in the rom image + */ + if (dcb->version < 0x21) + merge_like_dcb_entries(dev, dcb); + + /* dump connector table entries to log, if any exist */ + idx = -1; + while ((conn = olddcb_conn(dev, ++idx))) { + if (conn[0] != 0xff) { + NV_INFO(drm, "DCB conn %02d: ", idx); + if (olddcb_conntab(dev)[3] < 4) + pr_cont("%04x\n", ROM16(conn[0])); + else + pr_cont("%08x\n", ROM32(conn[0])); + } + } + dcb_fake_connectors(bios); + return 0; +} + +static int load_nv17_hwsq_ucode_entry(struct drm_device *dev, struct nvbios *bios, uint16_t hwsq_offset, int entry) +{ + /* + * The header following the "HWSQ" signature has the number of entries, + * and the entry size + * + * An entry consists of a dword to write to the sequencer control reg + * (0x00001304), followed by the ucode bytes, written sequentially, + * starting at reg 0x00001400 + */ + + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvif_device *device = &drm->device; + uint8_t bytes_to_write; + uint16_t hwsq_entry_offset; + int i; + + if (bios->data[hwsq_offset] <= entry) { + NV_ERROR(drm, "Too few entries in HW sequencer table for " + "requested entry\n"); + return -ENOENT; + } + + bytes_to_write = bios->data[hwsq_offset + 1]; + + if (bytes_to_write != 36) { + NV_ERROR(drm, "Unknown HW sequencer entry size\n"); + return -EINVAL; + } + + NV_INFO(drm, "Loading NV17 power sequencing microcode\n"); + + hwsq_entry_offset = hwsq_offset + 2 + entry * bytes_to_write; + + /* set sequencer control */ + nvif_wr32(device, 0x00001304, ROM32(bios->data[hwsq_entry_offset])); + bytes_to_write -= 4; + + /* write ucode */ + for (i = 0; i < bytes_to_write; i += 4) + nvif_wr32(device, 0x00001400 + i, ROM32(bios->data[hwsq_entry_offset + i + 4])); + + /* twiddle NV_PBUS_DEBUG_4 */ + nvif_wr32(device, NV_PBUS_DEBUG_4, nvif_rd32(device, NV_PBUS_DEBUG_4) | 0x18); + + return 0; +} + +static int load_nv17_hw_sequencer_ucode(struct drm_device *dev, + struct nvbios *bios) +{ + /* + * BMP based cards, from NV17, need a microcode loading to correctly + * control the GPIO etc for LVDS panels + * + * BIT based cards seem to do this directly in the init scripts + * + * The microcode entries are found by the "HWSQ" signature. + */ + + const uint8_t hwsq_signature[] = { 'H', 'W', 'S', 'Q' }; + const int sz = sizeof(hwsq_signature); + int hwsq_offset; + + hwsq_offset = findstr(bios->data, bios->length, hwsq_signature, sz); + if (!hwsq_offset) + return 0; + + /* always use entry 0? */ + return load_nv17_hwsq_ucode_entry(dev, bios, hwsq_offset + sz, 0); +} + +uint8_t *nouveau_bios_embedded_edid(struct drm_device *dev) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvbios *bios = &drm->vbios; + const uint8_t edid_sig[] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; + uint16_t offset = 0; + uint16_t newoffset; + int searchlen = NV_PROM_SIZE; + + if (bios->fp.edid) + return bios->fp.edid; + + while (searchlen) { + newoffset = findstr(&bios->data[offset], searchlen, + edid_sig, 8); + if (!newoffset) + return NULL; + offset += newoffset; + if (!nv_cksum(&bios->data[offset], EDID1_LEN)) + break; + + searchlen -= offset; + offset++; + } + + NV_INFO(drm, "Found EDID in BIOS\n"); + + return bios->fp.edid = &bios->data[offset]; +} + +static bool NVInitVBIOS(struct drm_device *dev) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_bios *bios = nvxx_bios(&drm->device); + struct nvbios *legacy = &drm->vbios; + + memset(legacy, 0, sizeof(struct nvbios)); + spin_lock_init(&legacy->lock); + legacy->dev = dev; + + legacy->data = bios->data; + legacy->length = bios->size; + legacy->major_version = bios->version.major; + legacy->chip_version = bios->version.chip; + if (bios->bit_offset) { + legacy->type = NVBIOS_BIT; + legacy->offset = bios->bit_offset; + return !parse_bit_structure(legacy, legacy->offset + 6); + } else + if (bios->bmp_offset) { + legacy->type = NVBIOS_BMP; + legacy->offset = bios->bmp_offset; + return !parse_bmp_structure(dev, legacy, legacy->offset); + } + + return false; +} + +int +nouveau_run_vbios_init(struct drm_device *dev) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvbios *bios = &drm->vbios; + int ret = 0; + + /* Reset the BIOS head to 0. */ + bios->state.crtchead = 0; + + if (bios->major_version < 5) /* BMP only */ + load_nv17_hw_sequencer_ucode(dev, bios); + + if (bios->execute) { + bios->fp.last_script_invoc = 0; + bios->fp.lvds_init_run = false; + } + + return ret; +} + +static bool +nouveau_bios_posted(struct drm_device *dev) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + unsigned htotal; + + if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA) + return true; + + htotal = NVReadVgaCrtc(dev, 0, 0x06); + htotal |= (NVReadVgaCrtc(dev, 0, 0x07) & 0x01) << 8; + htotal |= (NVReadVgaCrtc(dev, 0, 0x07) & 0x20) << 4; + htotal |= (NVReadVgaCrtc(dev, 0, 0x25) & 0x01) << 10; + htotal |= (NVReadVgaCrtc(dev, 0, 0x41) & 0x01) << 11; + return (htotal != 0); +} + +int +nouveau_bios_init(struct drm_device *dev) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvbios *bios = &drm->vbios; + int ret; + + /* only relevant for PCI devices */ + if (!dev->pdev) + return 0; + + if (!NVInitVBIOS(dev)) + return -ENODEV; + + ret = parse_dcb_table(dev, bios); + if (ret) + return ret; + + if (!bios->major_version) /* we don't run version 0 bios */ + return 0; + + /* init script execution disabled */ + bios->execute = false; + + /* ... unless card isn't POSTed already */ + if (!nouveau_bios_posted(dev)) { + NV_INFO(drm, "Adaptor not initialised, " + "running VBIOS init tables.\n"); + bios->execute = true; + } + + ret = nouveau_run_vbios_init(dev); + if (ret) + return ret; + + /* feature_byte on BMP is poor, but init always sets CR4B */ + if (bios->major_version < 5) + bios->is_mobile = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_4B) & 0x40; + + /* all BIT systems need p_f_m_t for digital_min_front_porch */ + if (bios->is_mobile || bios->major_version >= 5) + ret = parse_fp_mode_table(dev, bios); + + /* allow subsequent scripts to execute */ + bios->execute = true; + + return 0; +} + +void +nouveau_bios_takedown(struct drm_device *dev) +{ +} diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_bios.h b/kernel/drivers/gpu/drm/nouveau/nouveau_bios.h new file mode 100644 index 000000000..0067586eb --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_bios.h @@ -0,0 +1,179 @@ +/* + * Copyright 2007-2008 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 (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 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 __NOUVEAU_DISPBIOS_H__ +#define __NOUVEAU_DISPBIOS_H__ + +#define DCB_MAX_NUM_ENTRIES 16 +#define DCB_MAX_NUM_I2C_ENTRIES 16 +#define DCB_MAX_NUM_GPIO_ENTRIES 32 +#define DCB_MAX_NUM_CONNECTOR_ENTRIES 16 + +#define DCB_LOC_ON_CHIP 0 + +#define ROM16(x) le16_to_cpu(*(u16 *)&(x)) +#define ROM32(x) le32_to_cpu(*(u32 *)&(x)) +#define ROM48(x) ({ u8 *p = &(x); (u64)ROM16(p[4]) << 32 | ROM32(p[0]); }) +#define ROM64(x) le64_to_cpu(*(u64 *)&(x)) +#define ROMPTR(d,x) ({ \ + struct nouveau_drm *drm = nouveau_drm((d)); \ + ROM16(x) ? &drm->vbios.data[ROM16(x)] : NULL; \ +}) + +struct bit_entry { + uint8_t id; + uint8_t version; + uint16_t length; + uint16_t offset; + uint8_t *data; +}; + +int bit_table(struct drm_device *, u8 id, struct bit_entry *); + +#include +#include + +struct dcb_table { + uint8_t version; + int entries; + struct dcb_output entry[DCB_MAX_NUM_ENTRIES]; +}; + +enum nouveau_or { + DCB_OUTPUT_A = (1 << 0), + DCB_OUTPUT_B = (1 << 1), + DCB_OUTPUT_C = (1 << 2) +}; + +enum LVDS_script { + /* Order *does* matter here */ + LVDS_INIT = 1, + LVDS_RESET, + LVDS_BACKLIGHT_ON, + LVDS_BACKLIGHT_OFF, + LVDS_PANEL_ON, + LVDS_PANEL_OFF +}; + +struct nvbios { + struct drm_device *dev; + enum { + NVBIOS_BMP, + NVBIOS_BIT + } type; + uint16_t offset; + uint32_t length; + uint8_t *data; + + uint8_t chip_version; + + uint32_t dactestval; + uint32_t tvdactestval; + uint8_t digital_min_front_porch; + bool fp_no_ddc; + + spinlock_t lock; + + bool execute; + + uint8_t major_version; + uint8_t feature_byte; + bool is_mobile; + + uint32_t fmaxvco, fminvco; + + bool old_style_init; + uint16_t init_script_tbls_ptr; + uint16_t extra_init_script_tbl_ptr; + + uint16_t ram_restrict_tbl_ptr; + uint8_t ram_restrict_group_count; + + struct dcb_table dcb; + + struct { + int crtchead; + } state; + + struct { + uint16_t fptablepointer; /* also used by tmds */ + uint16_t fpxlatetableptr; + int xlatwidth; + uint16_t lvdsmanufacturerpointer; + uint16_t fpxlatemanufacturertableptr; + uint16_t mode_ptr; + uint16_t xlated_entry; + bool power_off_for_reset; + bool reset_after_pclk_change; + bool dual_link; + bool link_c_increment; + bool if_is_24bit; + int duallink_transition_clk; + uint8_t strapless_is_24bit; + uint8_t *edid; + + /* will need resetting after suspend */ + int last_script_invoc; + bool lvds_init_run; + } fp; + + struct { + uint16_t output0_script_ptr; + uint16_t output1_script_ptr; + } tmds; + + struct { + uint16_t mem_init_tbl_ptr; + uint16_t sdr_seq_tbl_ptr; + uint16_t ddr_seq_tbl_ptr; + + struct { + uint8_t crt, tv, panel; + } i2c_indices; + + uint16_t lvds_single_a_script_ptr; + } legacy; +}; + +void *olddcb_table(struct drm_device *); +void *olddcb_outp(struct drm_device *, u8 idx); +int olddcb_outp_foreach(struct drm_device *, void *data, + int (*)(struct drm_device *, void *, int idx, u8 *outp)); +u8 *olddcb_conntab(struct drm_device *); +u8 *olddcb_conn(struct drm_device *, u8 idx); + +int nouveau_bios_init(struct drm_device *); +void nouveau_bios_takedown(struct drm_device *dev); +int nouveau_run_vbios_init(struct drm_device *); +struct dcb_connector_table_entry * +nouveau_bios_connector_entry(struct drm_device *, int index); +bool nouveau_bios_fp_mode(struct drm_device *, struct drm_display_mode *); +uint8_t *nouveau_bios_embedded_edid(struct drm_device *); +int nouveau_bios_parse_lvds_table(struct drm_device *, int pxclk, + bool *dl, bool *if_is_24bit); +int run_tmds_table(struct drm_device *, struct dcb_output *, + int head, int pxclk); +int call_lvds_script(struct drm_device *, struct dcb_output *, int head, + enum LVDS_script, int pxclk); + +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_bo.c b/kernel/drivers/gpu/drm/nouveau/nouveau_bo.c new file mode 100644 index 000000000..6edcce165 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -0,0 +1,1661 @@ +/* + * Copyright 2007 Dave Airlied + * 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 + * VA LINUX SYSTEMS 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. + */ +/* + * Authors: Dave Airlied + * Ben Skeggs + * Jeremy Kolb + */ + +#include +#include + +#include "nouveau_drm.h" +#include "nouveau_dma.h" +#include "nouveau_fence.h" + +#include "nouveau_bo.h" +#include "nouveau_ttm.h" +#include "nouveau_gem.h" + +/* + * NV10-NV40 tiling helpers + */ + +static void +nv10_bo_update_tile_region(struct drm_device *dev, struct nouveau_drm_tile *reg, + u32 addr, u32 size, u32 pitch, u32 flags) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + int i = reg - drm->tile.reg; + struct nvkm_fb *pfb = nvxx_fb(&drm->device); + struct nvkm_fb_tile *tile = &pfb->tile.region[i]; + struct nvkm_engine *engine; + + nouveau_fence_unref(®->fence); + + if (tile->pitch) + pfb->tile.fini(pfb, i, tile); + + if (pitch) + pfb->tile.init(pfb, i, addr, size, pitch, flags, tile); + + pfb->tile.prog(pfb, i, tile); + + if ((engine = nvkm_engine(pfb, NVDEV_ENGINE_GR))) + engine->tile_prog(engine, i); + if ((engine = nvkm_engine(pfb, NVDEV_ENGINE_MPEG))) + engine->tile_prog(engine, i); +} + +static struct nouveau_drm_tile * +nv10_bo_get_tile_region(struct drm_device *dev, int i) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_drm_tile *tile = &drm->tile.reg[i]; + + spin_lock(&drm->tile.lock); + + if (!tile->used && + (!tile->fence || nouveau_fence_done(tile->fence))) + tile->used = true; + else + tile = NULL; + + spin_unlock(&drm->tile.lock); + return tile; +} + +static void +nv10_bo_put_tile_region(struct drm_device *dev, struct nouveau_drm_tile *tile, + struct fence *fence) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + + if (tile) { + spin_lock(&drm->tile.lock); + tile->fence = (struct nouveau_fence *)fence_get(fence); + tile->used = false; + spin_unlock(&drm->tile.lock); + } +} + +static struct nouveau_drm_tile * +nv10_bo_set_tiling(struct drm_device *dev, u32 addr, + u32 size, u32 pitch, u32 flags) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_fb *pfb = nvxx_fb(&drm->device); + struct nouveau_drm_tile *tile, *found = NULL; + int i; + + for (i = 0; i < pfb->tile.regions; i++) { + tile = nv10_bo_get_tile_region(dev, i); + + if (pitch && !found) { + found = tile; + continue; + + } else if (tile && pfb->tile.region[i].pitch) { + /* Kill an unused tile region. */ + nv10_bo_update_tile_region(dev, tile, 0, 0, 0, 0); + } + + nv10_bo_put_tile_region(dev, tile, NULL); + } + + if (found) + nv10_bo_update_tile_region(dev, found, addr, size, + pitch, flags); + return found; +} + +static void +nouveau_bo_del_ttm(struct ttm_buffer_object *bo) +{ + struct nouveau_drm *drm = nouveau_bdev(bo->bdev); + struct drm_device *dev = drm->dev; + struct nouveau_bo *nvbo = nouveau_bo(bo); + + if (unlikely(nvbo->gem.filp)) + DRM_ERROR("bo %p still attached to GEM object\n", bo); + WARN_ON(nvbo->pin_refcnt > 0); + nv10_bo_put_tile_region(dev, nvbo->tile, NULL); + kfree(nvbo); +} + +static void +nouveau_bo_fixup_align(struct nouveau_bo *nvbo, u32 flags, + int *align, int *size) +{ + struct nouveau_drm *drm = nouveau_bdev(nvbo->bo.bdev); + struct nvif_device *device = &drm->device; + + if (device->info.family < NV_DEVICE_INFO_V0_TESLA) { + if (nvbo->tile_mode) { + if (device->info.chipset >= 0x40) { + *align = 65536; + *size = roundup(*size, 64 * nvbo->tile_mode); + + } else if (device->info.chipset >= 0x30) { + *align = 32768; + *size = roundup(*size, 64 * nvbo->tile_mode); + + } else if (device->info.chipset >= 0x20) { + *align = 16384; + *size = roundup(*size, 64 * nvbo->tile_mode); + + } else if (device->info.chipset >= 0x10) { + *align = 16384; + *size = roundup(*size, 32 * nvbo->tile_mode); + } + } + } else { + *size = roundup(*size, (1 << nvbo->page_shift)); + *align = max((1 << nvbo->page_shift), *align); + } + + *size = roundup(*size, PAGE_SIZE); +} + +int +nouveau_bo_new(struct drm_device *dev, int size, int align, + uint32_t flags, uint32_t tile_mode, uint32_t tile_flags, + struct sg_table *sg, struct reservation_object *robj, + struct nouveau_bo **pnvbo) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_bo *nvbo; + size_t acc_size; + int ret; + int type = ttm_bo_type_device; + int lpg_shift = 12; + int max_size; + + if (drm->client.vm) + lpg_shift = drm->client.vm->mmu->lpg_shift; + max_size = INT_MAX & ~((1 << lpg_shift) - 1); + + if (size <= 0 || size > max_size) { + NV_WARN(drm, "skipped size %x\n", (u32)size); + return -EINVAL; + } + + if (sg) + type = ttm_bo_type_sg; + + nvbo = kzalloc(sizeof(struct nouveau_bo), GFP_KERNEL); + if (!nvbo) + return -ENOMEM; + INIT_LIST_HEAD(&nvbo->head); + INIT_LIST_HEAD(&nvbo->entry); + INIT_LIST_HEAD(&nvbo->vma_list); + nvbo->tile_mode = tile_mode; + nvbo->tile_flags = tile_flags; + nvbo->bo.bdev = &drm->ttm.bdev; + + if (!nv_device_is_cpu_coherent(nvxx_device(&drm->device))) + nvbo->force_coherent = flags & TTM_PL_FLAG_UNCACHED; + + nvbo->page_shift = 12; + if (drm->client.vm) { + if (!(flags & TTM_PL_FLAG_TT) && size > 256 * 1024) + nvbo->page_shift = drm->client.vm->mmu->lpg_shift; + } + + nouveau_bo_fixup_align(nvbo, flags, &align, &size); + nvbo->bo.mem.num_pages = size >> PAGE_SHIFT; + nouveau_bo_placement_set(nvbo, flags, 0); + + acc_size = ttm_bo_dma_acc_size(&drm->ttm.bdev, size, + sizeof(struct nouveau_bo)); + + ret = ttm_bo_init(&drm->ttm.bdev, &nvbo->bo, size, + type, &nvbo->placement, + align >> PAGE_SHIFT, false, NULL, acc_size, sg, + robj, nouveau_bo_del_ttm); + if (ret) { + /* ttm will call nouveau_bo_del_ttm if it fails.. */ + return ret; + } + + *pnvbo = nvbo; + return 0; +} + +static void +set_placement_list(struct ttm_place *pl, unsigned *n, uint32_t type, uint32_t flags) +{ + *n = 0; + + if (type & TTM_PL_FLAG_VRAM) + pl[(*n)++].flags = TTM_PL_FLAG_VRAM | flags; + if (type & TTM_PL_FLAG_TT) + pl[(*n)++].flags = TTM_PL_FLAG_TT | flags; + if (type & TTM_PL_FLAG_SYSTEM) + pl[(*n)++].flags = TTM_PL_FLAG_SYSTEM | flags; +} + +static void +set_placement_range(struct nouveau_bo *nvbo, uint32_t type) +{ + struct nouveau_drm *drm = nouveau_bdev(nvbo->bo.bdev); + u32 vram_pages = drm->device.info.ram_size >> PAGE_SHIFT; + unsigned i, fpfn, lpfn; + + if (drm->device.info.family == NV_DEVICE_INFO_V0_CELSIUS && + nvbo->tile_mode && (type & TTM_PL_FLAG_VRAM) && + nvbo->bo.mem.num_pages < vram_pages / 4) { + /* + * Make sure that the color and depth buffers are handled + * by independent memory controller units. Up to a 9x + * speed up when alpha-blending and depth-test are enabled + * at the same time. + */ + if (nvbo->tile_flags & NOUVEAU_GEM_TILE_ZETA) { + fpfn = vram_pages / 2; + lpfn = ~0; + } else { + fpfn = 0; + lpfn = vram_pages / 2; + } + for (i = 0; i < nvbo->placement.num_placement; ++i) { + nvbo->placements[i].fpfn = fpfn; + nvbo->placements[i].lpfn = lpfn; + } + for (i = 0; i < nvbo->placement.num_busy_placement; ++i) { + nvbo->busy_placements[i].fpfn = fpfn; + nvbo->busy_placements[i].lpfn = lpfn; + } + } +} + +void +nouveau_bo_placement_set(struct nouveau_bo *nvbo, uint32_t type, uint32_t busy) +{ + struct ttm_placement *pl = &nvbo->placement; + uint32_t flags = (nvbo->force_coherent ? TTM_PL_FLAG_UNCACHED : + TTM_PL_MASK_CACHING) | + (nvbo->pin_refcnt ? TTM_PL_FLAG_NO_EVICT : 0); + + pl->placement = nvbo->placements; + set_placement_list(nvbo->placements, &pl->num_placement, + type, flags); + + pl->busy_placement = nvbo->busy_placements; + set_placement_list(nvbo->busy_placements, &pl->num_busy_placement, + type | busy, flags); + + set_placement_range(nvbo, type); +} + +int +nouveau_bo_pin(struct nouveau_bo *nvbo, uint32_t memtype, bool contig) +{ + struct nouveau_drm *drm = nouveau_bdev(nvbo->bo.bdev); + struct ttm_buffer_object *bo = &nvbo->bo; + bool force = false, evict = false; + int ret; + + ret = ttm_bo_reserve(bo, false, false, false, NULL); + if (ret) + return ret; + + if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA && + memtype == TTM_PL_FLAG_VRAM && contig) { + if (nvbo->tile_flags & NOUVEAU_GEM_TILE_NONCONTIG) { + if (bo->mem.mem_type == TTM_PL_VRAM) { + struct nvkm_mem *mem = bo->mem.mm_node; + if (!list_is_singular(&mem->regions)) + evict = true; + } + nvbo->tile_flags &= ~NOUVEAU_GEM_TILE_NONCONTIG; + force = true; + } + } + + if (nvbo->pin_refcnt) { + if (!(memtype & (1 << bo->mem.mem_type)) || evict) { + NV_ERROR(drm, "bo %p pinned elsewhere: " + "0x%08x vs 0x%08x\n", bo, + 1 << bo->mem.mem_type, memtype); + ret = -EBUSY; + } + nvbo->pin_refcnt++; + goto out; + } + + if (evict) { + nouveau_bo_placement_set(nvbo, TTM_PL_FLAG_TT, 0); + ret = nouveau_bo_validate(nvbo, false, false); + if (ret) + goto out; + } + + nvbo->pin_refcnt++; + nouveau_bo_placement_set(nvbo, memtype, 0); + + /* drop pin_refcnt temporarily, so we don't trip the assertion + * in nouveau_bo_move() that makes sure we're not trying to + * move a pinned buffer + */ + nvbo->pin_refcnt--; + ret = nouveau_bo_validate(nvbo, false, false); + if (ret) + goto out; + nvbo->pin_refcnt++; + + switch (bo->mem.mem_type) { + case TTM_PL_VRAM: + drm->gem.vram_available -= bo->mem.size; + break; + case TTM_PL_TT: + drm->gem.gart_available -= bo->mem.size; + break; + default: + break; + } + +out: + if (force && ret) + nvbo->tile_flags |= NOUVEAU_GEM_TILE_NONCONTIG; + ttm_bo_unreserve(bo); + return ret; +} + +int +nouveau_bo_unpin(struct nouveau_bo *nvbo) +{ + struct nouveau_drm *drm = nouveau_bdev(nvbo->bo.bdev); + struct ttm_buffer_object *bo = &nvbo->bo; + int ret, ref; + + ret = ttm_bo_reserve(bo, false, false, false, NULL); + if (ret) + return ret; + + ref = --nvbo->pin_refcnt; + WARN_ON_ONCE(ref < 0); + if (ref) + goto out; + + nouveau_bo_placement_set(nvbo, bo->mem.placement, 0); + + ret = nouveau_bo_validate(nvbo, false, false); + if (ret == 0) { + switch (bo->mem.mem_type) { + case TTM_PL_VRAM: + drm->gem.vram_available += bo->mem.size; + break; + case TTM_PL_TT: + drm->gem.gart_available += bo->mem.size; + break; + default: + break; + } + } + +out: + ttm_bo_unreserve(bo); + return ret; +} + +int +nouveau_bo_map(struct nouveau_bo *nvbo) +{ + int ret; + + ret = ttm_bo_reserve(&nvbo->bo, false, false, false, NULL); + if (ret) + return ret; + + /* + * TTM buffers allocated using the DMA API already have a mapping, let's + * use it instead. + */ + if (!nvbo->force_coherent) + ret = ttm_bo_kmap(&nvbo->bo, 0, nvbo->bo.mem.num_pages, + &nvbo->kmap); + + ttm_bo_unreserve(&nvbo->bo); + return ret; +} + +void +nouveau_bo_unmap(struct nouveau_bo *nvbo) +{ + if (!nvbo) + return; + + /* + * TTM buffers allocated using the DMA API already had a coherent + * mapping which we used, no need to unmap. + */ + if (!nvbo->force_coherent) + ttm_bo_kunmap(&nvbo->kmap); +} + +void +nouveau_bo_sync_for_device(struct nouveau_bo *nvbo) +{ + struct nouveau_drm *drm = nouveau_bdev(nvbo->bo.bdev); + struct nvkm_device *device = nvxx_device(&drm->device); + struct ttm_dma_tt *ttm_dma = (struct ttm_dma_tt *)nvbo->bo.ttm; + int i; + + if (!ttm_dma) + return; + + /* Don't waste time looping if the object is coherent */ + if (nvbo->force_coherent) + return; + + for (i = 0; i < ttm_dma->ttm.num_pages; i++) + dma_sync_single_for_device(nv_device_base(device), + ttm_dma->dma_address[i], PAGE_SIZE, DMA_TO_DEVICE); +} + +void +nouveau_bo_sync_for_cpu(struct nouveau_bo *nvbo) +{ + struct nouveau_drm *drm = nouveau_bdev(nvbo->bo.bdev); + struct nvkm_device *device = nvxx_device(&drm->device); + struct ttm_dma_tt *ttm_dma = (struct ttm_dma_tt *)nvbo->bo.ttm; + int i; + + if (!ttm_dma) + return; + + /* Don't waste time looping if the object is coherent */ + if (nvbo->force_coherent) + return; + + for (i = 0; i < ttm_dma->ttm.num_pages; i++) + dma_sync_single_for_cpu(nv_device_base(device), + ttm_dma->dma_address[i], PAGE_SIZE, DMA_FROM_DEVICE); +} + +int +nouveau_bo_validate(struct nouveau_bo *nvbo, bool interruptible, + bool no_wait_gpu) +{ + int ret; + + ret = ttm_bo_validate(&nvbo->bo, &nvbo->placement, + interruptible, no_wait_gpu); + if (ret) + return ret; + + nouveau_bo_sync_for_device(nvbo); + + return 0; +} + +static inline void * +_nouveau_bo_mem_index(struct nouveau_bo *nvbo, unsigned index, void *mem, u8 sz) +{ + struct ttm_dma_tt *dma_tt; + u8 *m = mem; + + index *= sz; + + if (m) { + /* kmap'd address, return the corresponding offset */ + m += index; + } else { + /* DMA-API mapping, lookup the right address */ + dma_tt = (struct ttm_dma_tt *)nvbo->bo.ttm; + m = dma_tt->cpu_address[index / PAGE_SIZE]; + m += index % PAGE_SIZE; + } + + return m; +} +#define nouveau_bo_mem_index(o, i, m) _nouveau_bo_mem_index(o, i, m, sizeof(*m)) + +void +nouveau_bo_wr16(struct nouveau_bo *nvbo, unsigned index, u16 val) +{ + bool is_iomem; + u16 *mem = ttm_kmap_obj_virtual(&nvbo->kmap, &is_iomem); + + mem = nouveau_bo_mem_index(nvbo, index, mem); + + if (is_iomem) + iowrite16_native(val, (void __force __iomem *)mem); + else + *mem = val; +} + +u32 +nouveau_bo_rd32(struct nouveau_bo *nvbo, unsigned index) +{ + bool is_iomem; + u32 *mem = ttm_kmap_obj_virtual(&nvbo->kmap, &is_iomem); + + mem = nouveau_bo_mem_index(nvbo, index, mem); + + if (is_iomem) + return ioread32_native((void __force __iomem *)mem); + else + return *mem; +} + +void +nouveau_bo_wr32(struct nouveau_bo *nvbo, unsigned index, u32 val) +{ + bool is_iomem; + u32 *mem = ttm_kmap_obj_virtual(&nvbo->kmap, &is_iomem); + + mem = nouveau_bo_mem_index(nvbo, index, mem); + + if (is_iomem) + iowrite32_native(val, (void __force __iomem *)mem); + else + *mem = val; +} + +static struct ttm_tt * +nouveau_ttm_tt_create(struct ttm_bo_device *bdev, unsigned long size, + uint32_t page_flags, struct page *dummy_read) +{ +#if __OS_HAS_AGP + struct nouveau_drm *drm = nouveau_bdev(bdev); + struct drm_device *dev = drm->dev; + + if (drm->agp.stat == ENABLED) { + return ttm_agp_tt_create(bdev, dev->agp->bridge, size, + page_flags, dummy_read); + } +#endif + + return nouveau_sgdma_create_ttm(bdev, size, page_flags, dummy_read); +} + +static int +nouveau_bo_invalidate_caches(struct ttm_bo_device *bdev, uint32_t flags) +{ + /* We'll do this from user space. */ + return 0; +} + +static int +nouveau_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, + struct ttm_mem_type_manager *man) +{ + struct nouveau_drm *drm = nouveau_bdev(bdev); + + switch (type) { + case TTM_PL_SYSTEM: + man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; + man->available_caching = TTM_PL_MASK_CACHING; + man->default_caching = TTM_PL_FLAG_CACHED; + break; + case TTM_PL_VRAM: + man->flags = TTM_MEMTYPE_FLAG_FIXED | + TTM_MEMTYPE_FLAG_MAPPABLE; + man->available_caching = TTM_PL_FLAG_UNCACHED | + TTM_PL_FLAG_WC; + man->default_caching = TTM_PL_FLAG_WC; + + if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA) { + /* Some BARs do not support being ioremapped WC */ + if (nvxx_bar(&drm->device)->iomap_uncached) { + man->available_caching = TTM_PL_FLAG_UNCACHED; + man->default_caching = TTM_PL_FLAG_UNCACHED; + } + + man->func = &nouveau_vram_manager; + man->io_reserve_fastpath = false; + man->use_io_reserve_lru = true; + } else { + man->func = &ttm_bo_manager_func; + } + break; + case TTM_PL_TT: + if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA) + man->func = &nouveau_gart_manager; + else + if (drm->agp.stat != ENABLED) + man->func = &nv04_gart_manager; + else + man->func = &ttm_bo_manager_func; + + if (drm->agp.stat == ENABLED) { + man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; + man->available_caching = TTM_PL_FLAG_UNCACHED | + TTM_PL_FLAG_WC; + man->default_caching = TTM_PL_FLAG_WC; + } else { + man->flags = TTM_MEMTYPE_FLAG_MAPPABLE | + TTM_MEMTYPE_FLAG_CMA; + man->available_caching = TTM_PL_MASK_CACHING; + man->default_caching = TTM_PL_FLAG_CACHED; + } + + break; + default: + return -EINVAL; + } + return 0; +} + +static void +nouveau_bo_evict_flags(struct ttm_buffer_object *bo, struct ttm_placement *pl) +{ + struct nouveau_bo *nvbo = nouveau_bo(bo); + + switch (bo->mem.mem_type) { + case TTM_PL_VRAM: + nouveau_bo_placement_set(nvbo, TTM_PL_FLAG_TT, + TTM_PL_FLAG_SYSTEM); + break; + default: + nouveau_bo_placement_set(nvbo, TTM_PL_FLAG_SYSTEM, 0); + break; + } + + *pl = nvbo->placement; +} + + +static int +nve0_bo_move_init(struct nouveau_channel *chan, u32 handle) +{ + int ret = RING_SPACE(chan, 2); + if (ret == 0) { + BEGIN_NVC0(chan, NvSubCopy, 0x0000, 1); + OUT_RING (chan, handle & 0x0000ffff); + FIRE_RING (chan); + } + return ret; +} + +static int +nve0_bo_move_copy(struct nouveau_channel *chan, struct ttm_buffer_object *bo, + struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem) +{ + struct nvkm_mem *node = old_mem->mm_node; + int ret = RING_SPACE(chan, 10); + if (ret == 0) { + BEGIN_NVC0(chan, NvSubCopy, 0x0400, 8); + OUT_RING (chan, upper_32_bits(node->vma[0].offset)); + OUT_RING (chan, lower_32_bits(node->vma[0].offset)); + OUT_RING (chan, upper_32_bits(node->vma[1].offset)); + OUT_RING (chan, lower_32_bits(node->vma[1].offset)); + OUT_RING (chan, PAGE_SIZE); + OUT_RING (chan, PAGE_SIZE); + OUT_RING (chan, PAGE_SIZE); + OUT_RING (chan, new_mem->num_pages); + BEGIN_IMC0(chan, NvSubCopy, 0x0300, 0x0386); + } + return ret; +} + +static int +nvc0_bo_move_init(struct nouveau_channel *chan, u32 handle) +{ + int ret = RING_SPACE(chan, 2); + if (ret == 0) { + BEGIN_NVC0(chan, NvSubCopy, 0x0000, 1); + OUT_RING (chan, handle); + } + return ret; +} + +static int +nvc0_bo_move_copy(struct nouveau_channel *chan, struct ttm_buffer_object *bo, + struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem) +{ + struct nvkm_mem *node = old_mem->mm_node; + u64 src_offset = node->vma[0].offset; + u64 dst_offset = node->vma[1].offset; + u32 page_count = new_mem->num_pages; + int ret; + + page_count = new_mem->num_pages; + while (page_count) { + int line_count = (page_count > 8191) ? 8191 : page_count; + + ret = RING_SPACE(chan, 11); + if (ret) + return ret; + + BEGIN_NVC0(chan, NvSubCopy, 0x030c, 8); + OUT_RING (chan, upper_32_bits(src_offset)); + OUT_RING (chan, lower_32_bits(src_offset)); + OUT_RING (chan, upper_32_bits(dst_offset)); + OUT_RING (chan, lower_32_bits(dst_offset)); + OUT_RING (chan, PAGE_SIZE); + OUT_RING (chan, PAGE_SIZE); + OUT_RING (chan, PAGE_SIZE); + OUT_RING (chan, line_count); + BEGIN_NVC0(chan, NvSubCopy, 0x0300, 1); + OUT_RING (chan, 0x00000110); + + page_count -= line_count; + src_offset += (PAGE_SIZE * line_count); + dst_offset += (PAGE_SIZE * line_count); + } + + return 0; +} + +static int +nvc0_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo, + struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem) +{ + struct nvkm_mem *node = old_mem->mm_node; + u64 src_offset = node->vma[0].offset; + u64 dst_offset = node->vma[1].offset; + u32 page_count = new_mem->num_pages; + int ret; + + page_count = new_mem->num_pages; + while (page_count) { + int line_count = (page_count > 2047) ? 2047 : page_count; + + ret = RING_SPACE(chan, 12); + if (ret) + return ret; + + BEGIN_NVC0(chan, NvSubCopy, 0x0238, 2); + OUT_RING (chan, upper_32_bits(dst_offset)); + OUT_RING (chan, lower_32_bits(dst_offset)); + BEGIN_NVC0(chan, NvSubCopy, 0x030c, 6); + OUT_RING (chan, upper_32_bits(src_offset)); + OUT_RING (chan, lower_32_bits(src_offset)); + OUT_RING (chan, PAGE_SIZE); /* src_pitch */ + OUT_RING (chan, PAGE_SIZE); /* dst_pitch */ + OUT_RING (chan, PAGE_SIZE); /* line_length */ + OUT_RING (chan, line_count); + BEGIN_NVC0(chan, NvSubCopy, 0x0300, 1); + OUT_RING (chan, 0x00100110); + + page_count -= line_count; + src_offset += (PAGE_SIZE * line_count); + dst_offset += (PAGE_SIZE * line_count); + } + + return 0; +} + +static int +nva3_bo_move_copy(struct nouveau_channel *chan, struct ttm_buffer_object *bo, + struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem) +{ + struct nvkm_mem *node = old_mem->mm_node; + u64 src_offset = node->vma[0].offset; + u64 dst_offset = node->vma[1].offset; + u32 page_count = new_mem->num_pages; + int ret; + + page_count = new_mem->num_pages; + while (page_count) { + int line_count = (page_count > 8191) ? 8191 : page_count; + + ret = RING_SPACE(chan, 11); + if (ret) + return ret; + + BEGIN_NV04(chan, NvSubCopy, 0x030c, 8); + OUT_RING (chan, upper_32_bits(src_offset)); + OUT_RING (chan, lower_32_bits(src_offset)); + OUT_RING (chan, upper_32_bits(dst_offset)); + OUT_RING (chan, lower_32_bits(dst_offset)); + OUT_RING (chan, PAGE_SIZE); + OUT_RING (chan, PAGE_SIZE); + OUT_RING (chan, PAGE_SIZE); + OUT_RING (chan, line_count); + BEGIN_NV04(chan, NvSubCopy, 0x0300, 1); + OUT_RING (chan, 0x00000110); + + page_count -= line_count; + src_offset += (PAGE_SIZE * line_count); + dst_offset += (PAGE_SIZE * line_count); + } + + return 0; +} + +static int +nv98_bo_move_exec(struct nouveau_channel *chan, struct ttm_buffer_object *bo, + struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem) +{ + struct nvkm_mem *node = old_mem->mm_node; + int ret = RING_SPACE(chan, 7); + if (ret == 0) { + BEGIN_NV04(chan, NvSubCopy, 0x0320, 6); + OUT_RING (chan, upper_32_bits(node->vma[0].offset)); + OUT_RING (chan, lower_32_bits(node->vma[0].offset)); + OUT_RING (chan, upper_32_bits(node->vma[1].offset)); + OUT_RING (chan, lower_32_bits(node->vma[1].offset)); + OUT_RING (chan, 0x00000000 /* COPY */); + OUT_RING (chan, new_mem->num_pages << PAGE_SHIFT); + } + return ret; +} + +static int +nv84_bo_move_exec(struct nouveau_channel *chan, struct ttm_buffer_object *bo, + struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem) +{ + struct nvkm_mem *node = old_mem->mm_node; + int ret = RING_SPACE(chan, 7); + if (ret == 0) { + BEGIN_NV04(chan, NvSubCopy, 0x0304, 6); + OUT_RING (chan, new_mem->num_pages << PAGE_SHIFT); + OUT_RING (chan, upper_32_bits(node->vma[0].offset)); + OUT_RING (chan, lower_32_bits(node->vma[0].offset)); + OUT_RING (chan, upper_32_bits(node->vma[1].offset)); + OUT_RING (chan, lower_32_bits(node->vma[1].offset)); + OUT_RING (chan, 0x00000000 /* MODE_COPY, QUERY_NONE */); + } + return ret; +} + +static int +nv50_bo_move_init(struct nouveau_channel *chan, u32 handle) +{ + int ret = RING_SPACE(chan, 6); + if (ret == 0) { + BEGIN_NV04(chan, NvSubCopy, 0x0000, 1); + OUT_RING (chan, handle); + BEGIN_NV04(chan, NvSubCopy, 0x0180, 3); + OUT_RING (chan, chan->drm->ntfy.handle); + OUT_RING (chan, chan->vram.handle); + OUT_RING (chan, chan->vram.handle); + } + + return ret; +} + +static int +nv50_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo, + struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem) +{ + struct nvkm_mem *node = old_mem->mm_node; + u64 length = (new_mem->num_pages << PAGE_SHIFT); + u64 src_offset = node->vma[0].offset; + u64 dst_offset = node->vma[1].offset; + int src_tiled = !!node->memtype; + int dst_tiled = !!((struct nvkm_mem *)new_mem->mm_node)->memtype; + int ret; + + while (length) { + u32 amount, stride, height; + + ret = RING_SPACE(chan, 18 + 6 * (src_tiled + dst_tiled)); + if (ret) + return ret; + + amount = min(length, (u64)(4 * 1024 * 1024)); + stride = 16 * 4; + height = amount / stride; + + if (src_tiled) { + BEGIN_NV04(chan, NvSubCopy, 0x0200, 7); + OUT_RING (chan, 0); + OUT_RING (chan, 0); + OUT_RING (chan, stride); + OUT_RING (chan, height); + OUT_RING (chan, 1); + OUT_RING (chan, 0); + OUT_RING (chan, 0); + } else { + BEGIN_NV04(chan, NvSubCopy, 0x0200, 1); + OUT_RING (chan, 1); + } + if (dst_tiled) { + BEGIN_NV04(chan, NvSubCopy, 0x021c, 7); + OUT_RING (chan, 0); + OUT_RING (chan, 0); + OUT_RING (chan, stride); + OUT_RING (chan, height); + OUT_RING (chan, 1); + OUT_RING (chan, 0); + OUT_RING (chan, 0); + } else { + BEGIN_NV04(chan, NvSubCopy, 0x021c, 1); + OUT_RING (chan, 1); + } + + BEGIN_NV04(chan, NvSubCopy, 0x0238, 2); + OUT_RING (chan, upper_32_bits(src_offset)); + OUT_RING (chan, upper_32_bits(dst_offset)); + BEGIN_NV04(chan, NvSubCopy, 0x030c, 8); + OUT_RING (chan, lower_32_bits(src_offset)); + OUT_RING (chan, lower_32_bits(dst_offset)); + OUT_RING (chan, stride); + OUT_RING (chan, stride); + OUT_RING (chan, stride); + OUT_RING (chan, height); + OUT_RING (chan, 0x00000101); + OUT_RING (chan, 0x00000000); + BEGIN_NV04(chan, NvSubCopy, NV_MEMORY_TO_MEMORY_FORMAT_NOP, 1); + OUT_RING (chan, 0); + + length -= amount; + src_offset += amount; + dst_offset += amount; + } + + return 0; +} + +static int +nv04_bo_move_init(struct nouveau_channel *chan, u32 handle) +{ + int ret = RING_SPACE(chan, 4); + if (ret == 0) { + BEGIN_NV04(chan, NvSubCopy, 0x0000, 1); + OUT_RING (chan, handle); + BEGIN_NV04(chan, NvSubCopy, 0x0180, 1); + OUT_RING (chan, chan->drm->ntfy.handle); + } + + return ret; +} + +static inline uint32_t +nouveau_bo_mem_ctxdma(struct ttm_buffer_object *bo, + struct nouveau_channel *chan, struct ttm_mem_reg *mem) +{ + if (mem->mem_type == TTM_PL_TT) + return NvDmaTT; + return chan->vram.handle; +} + +static int +nv04_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo, + struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem) +{ + u32 src_offset = old_mem->start << PAGE_SHIFT; + u32 dst_offset = new_mem->start << PAGE_SHIFT; + u32 page_count = new_mem->num_pages; + int ret; + + ret = RING_SPACE(chan, 3); + if (ret) + return ret; + + BEGIN_NV04(chan, NvSubCopy, NV_MEMORY_TO_MEMORY_FORMAT_DMA_SOURCE, 2); + OUT_RING (chan, nouveau_bo_mem_ctxdma(bo, chan, old_mem)); + OUT_RING (chan, nouveau_bo_mem_ctxdma(bo, chan, new_mem)); + + page_count = new_mem->num_pages; + while (page_count) { + int line_count = (page_count > 2047) ? 2047 : page_count; + + ret = RING_SPACE(chan, 11); + if (ret) + return ret; + + BEGIN_NV04(chan, NvSubCopy, + NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN, 8); + OUT_RING (chan, src_offset); + OUT_RING (chan, dst_offset); + OUT_RING (chan, PAGE_SIZE); /* src_pitch */ + OUT_RING (chan, PAGE_SIZE); /* dst_pitch */ + OUT_RING (chan, PAGE_SIZE); /* line_length */ + OUT_RING (chan, line_count); + OUT_RING (chan, 0x00000101); + OUT_RING (chan, 0x00000000); + BEGIN_NV04(chan, NvSubCopy, NV_MEMORY_TO_MEMORY_FORMAT_NOP, 1); + OUT_RING (chan, 0); + + page_count -= line_count; + src_offset += (PAGE_SIZE * line_count); + dst_offset += (PAGE_SIZE * line_count); + } + + return 0; +} + +static int +nouveau_bo_move_prep(struct nouveau_drm *drm, struct ttm_buffer_object *bo, + struct ttm_mem_reg *mem) +{ + struct nvkm_mem *old_node = bo->mem.mm_node; + struct nvkm_mem *new_node = mem->mm_node; + u64 size = (u64)mem->num_pages << PAGE_SHIFT; + int ret; + + ret = nvkm_vm_get(drm->client.vm, size, old_node->page_shift, + NV_MEM_ACCESS_RW, &old_node->vma[0]); + if (ret) + return ret; + + ret = nvkm_vm_get(drm->client.vm, size, new_node->page_shift, + NV_MEM_ACCESS_RW, &old_node->vma[1]); + if (ret) { + nvkm_vm_put(&old_node->vma[0]); + return ret; + } + + nvkm_vm_map(&old_node->vma[0], old_node); + nvkm_vm_map(&old_node->vma[1], new_node); + return 0; +} + +static int +nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, bool intr, + bool no_wait_gpu, struct ttm_mem_reg *new_mem) +{ + struct nouveau_drm *drm = nouveau_bdev(bo->bdev); + struct nouveau_channel *chan = drm->ttm.chan; + struct nouveau_cli *cli = (void *)nvif_client(&chan->device->base); + struct nouveau_fence *fence; + int ret; + + /* create temporary vmas for the transfer and attach them to the + * old nvkm_mem node, these will get cleaned up after ttm has + * destroyed the ttm_mem_reg + */ + if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA) { + ret = nouveau_bo_move_prep(drm, bo, new_mem); + if (ret) + return ret; + } + + mutex_lock_nested(&cli->mutex, SINGLE_DEPTH_NESTING); + ret = nouveau_fence_sync(nouveau_bo(bo), chan, true, intr); + if (ret == 0) { + ret = drm->ttm.move(chan, bo, &bo->mem, new_mem); + if (ret == 0) { + ret = nouveau_fence_new(chan, false, &fence); + if (ret == 0) { + ret = ttm_bo_move_accel_cleanup(bo, + &fence->base, + evict, + no_wait_gpu, + new_mem); + nouveau_fence_unref(&fence); + } + } + } + mutex_unlock(&cli->mutex); + return ret; +} + +void +nouveau_bo_move_init(struct nouveau_drm *drm) +{ + static const struct { + const char *name; + int engine; + u32 oclass; + int (*exec)(struct nouveau_channel *, + struct ttm_buffer_object *, + struct ttm_mem_reg *, struct ttm_mem_reg *); + int (*init)(struct nouveau_channel *, u32 handle); + } _methods[] = { + { "COPY", 4, 0xb0b5, nve0_bo_move_copy, nve0_bo_move_init }, + { "GRCE", 0, 0xb0b5, nve0_bo_move_copy, nvc0_bo_move_init }, + { "COPY", 4, 0xa0b5, nve0_bo_move_copy, nve0_bo_move_init }, + { "GRCE", 0, 0xa0b5, nve0_bo_move_copy, nvc0_bo_move_init }, + { "COPY1", 5, 0x90b8, nvc0_bo_move_copy, nvc0_bo_move_init }, + { "COPY0", 4, 0x90b5, nvc0_bo_move_copy, nvc0_bo_move_init }, + { "COPY", 0, 0x85b5, nva3_bo_move_copy, nv50_bo_move_init }, + { "CRYPT", 0, 0x74c1, nv84_bo_move_exec, nv50_bo_move_init }, + { "M2MF", 0, 0x9039, nvc0_bo_move_m2mf, nvc0_bo_move_init }, + { "M2MF", 0, 0x5039, nv50_bo_move_m2mf, nv50_bo_move_init }, + { "M2MF", 0, 0x0039, nv04_bo_move_m2mf, nv04_bo_move_init }, + {}, + { "CRYPT", 0, 0x88b4, nv98_bo_move_exec, nv50_bo_move_init }, + }, *mthd = _methods; + const char *name = "CPU"; + int ret; + + do { + struct nouveau_channel *chan; + + if (mthd->engine) + chan = drm->cechan; + else + chan = drm->channel; + if (chan == NULL) + continue; + + ret = nvif_object_init(chan->object, NULL, + mthd->oclass | (mthd->engine << 16), + mthd->oclass, NULL, 0, + &drm->ttm.copy); + if (ret == 0) { + ret = mthd->init(chan, drm->ttm.copy.handle); + if (ret) { + nvif_object_fini(&drm->ttm.copy); + continue; + } + + drm->ttm.move = mthd->exec; + drm->ttm.chan = chan; + name = mthd->name; + break; + } + } while ((++mthd)->exec); + + NV_INFO(drm, "MM: using %s for buffer copies\n", name); +} + +static int +nouveau_bo_move_flipd(struct ttm_buffer_object *bo, bool evict, bool intr, + bool no_wait_gpu, struct ttm_mem_reg *new_mem) +{ + struct ttm_place placement_memtype = { + .fpfn = 0, + .lpfn = 0, + .flags = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING + }; + struct ttm_placement placement; + struct ttm_mem_reg tmp_mem; + int ret; + + placement.num_placement = placement.num_busy_placement = 1; + placement.placement = placement.busy_placement = &placement_memtype; + + tmp_mem = *new_mem; + tmp_mem.mm_node = NULL; + ret = ttm_bo_mem_space(bo, &placement, &tmp_mem, intr, no_wait_gpu); + if (ret) + return ret; + + ret = ttm_tt_bind(bo->ttm, &tmp_mem); + if (ret) + goto out; + + ret = nouveau_bo_move_m2mf(bo, true, intr, no_wait_gpu, &tmp_mem); + if (ret) + goto out; + + ret = ttm_bo_move_ttm(bo, true, no_wait_gpu, new_mem); +out: + ttm_bo_mem_put(bo, &tmp_mem); + return ret; +} + +static int +nouveau_bo_move_flips(struct ttm_buffer_object *bo, bool evict, bool intr, + bool no_wait_gpu, struct ttm_mem_reg *new_mem) +{ + struct ttm_place placement_memtype = { + .fpfn = 0, + .lpfn = 0, + .flags = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING + }; + struct ttm_placement placement; + struct ttm_mem_reg tmp_mem; + int ret; + + placement.num_placement = placement.num_busy_placement = 1; + placement.placement = placement.busy_placement = &placement_memtype; + + tmp_mem = *new_mem; + tmp_mem.mm_node = NULL; + ret = ttm_bo_mem_space(bo, &placement, &tmp_mem, intr, no_wait_gpu); + if (ret) + return ret; + + ret = ttm_bo_move_ttm(bo, true, no_wait_gpu, &tmp_mem); + if (ret) + goto out; + + ret = nouveau_bo_move_m2mf(bo, true, intr, no_wait_gpu, new_mem); + if (ret) + goto out; + +out: + ttm_bo_mem_put(bo, &tmp_mem); + return ret; +} + +static void +nouveau_bo_move_ntfy(struct ttm_buffer_object *bo, struct ttm_mem_reg *new_mem) +{ + struct nouveau_bo *nvbo = nouveau_bo(bo); + struct nvkm_vma *vma; + + /* ttm can now (stupidly) pass the driver bos it didn't create... */ + if (bo->destroy != nouveau_bo_del_ttm) + return; + + list_for_each_entry(vma, &nvbo->vma_list, head) { + if (new_mem && new_mem->mem_type != TTM_PL_SYSTEM && + (new_mem->mem_type == TTM_PL_VRAM || + nvbo->page_shift != vma->vm->mmu->lpg_shift)) { + nvkm_vm_map(vma, new_mem->mm_node); + } else { + nvkm_vm_unmap(vma); + } + } +} + +static int +nouveau_bo_vm_bind(struct ttm_buffer_object *bo, struct ttm_mem_reg *new_mem, + struct nouveau_drm_tile **new_tile) +{ + struct nouveau_drm *drm = nouveau_bdev(bo->bdev); + struct drm_device *dev = drm->dev; + struct nouveau_bo *nvbo = nouveau_bo(bo); + u64 offset = new_mem->start << PAGE_SHIFT; + + *new_tile = NULL; + if (new_mem->mem_type != TTM_PL_VRAM) + return 0; + + if (drm->device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) { + *new_tile = nv10_bo_set_tiling(dev, offset, new_mem->size, + nvbo->tile_mode, + nvbo->tile_flags); + } + + return 0; +} + +static void +nouveau_bo_vm_cleanup(struct ttm_buffer_object *bo, + struct nouveau_drm_tile *new_tile, + struct nouveau_drm_tile **old_tile) +{ + struct nouveau_drm *drm = nouveau_bdev(bo->bdev); + struct drm_device *dev = drm->dev; + struct fence *fence = reservation_object_get_excl(bo->resv); + + nv10_bo_put_tile_region(dev, *old_tile, fence); + *old_tile = new_tile; +} + +static int +nouveau_bo_move(struct ttm_buffer_object *bo, bool evict, bool intr, + bool no_wait_gpu, struct ttm_mem_reg *new_mem) +{ + struct nouveau_drm *drm = nouveau_bdev(bo->bdev); + struct nouveau_bo *nvbo = nouveau_bo(bo); + struct ttm_mem_reg *old_mem = &bo->mem; + struct nouveau_drm_tile *new_tile = NULL; + int ret = 0; + + if (nvbo->pin_refcnt) + NV_WARN(drm, "Moving pinned object %p!\n", nvbo); + + if (drm->device.info.family < NV_DEVICE_INFO_V0_TESLA) { + ret = nouveau_bo_vm_bind(bo, new_mem, &new_tile); + if (ret) + return ret; + } + + /* Fake bo copy. */ + if (old_mem->mem_type == TTM_PL_SYSTEM && !bo->ttm) { + BUG_ON(bo->mem.mm_node != NULL); + bo->mem = *new_mem; + new_mem->mm_node = NULL; + goto out; + } + + /* Hardware assisted copy. */ + if (drm->ttm.move) { + if (new_mem->mem_type == TTM_PL_SYSTEM) + ret = nouveau_bo_move_flipd(bo, evict, intr, + no_wait_gpu, new_mem); + else if (old_mem->mem_type == TTM_PL_SYSTEM) + ret = nouveau_bo_move_flips(bo, evict, intr, + no_wait_gpu, new_mem); + else + ret = nouveau_bo_move_m2mf(bo, evict, intr, + no_wait_gpu, new_mem); + if (!ret) + goto out; + } + + /* Fallback to software copy. */ + ret = ttm_bo_wait(bo, true, intr, no_wait_gpu); + if (ret == 0) + ret = ttm_bo_move_memcpy(bo, evict, no_wait_gpu, new_mem); + +out: + if (drm->device.info.family < NV_DEVICE_INFO_V0_TESLA) { + if (ret) + nouveau_bo_vm_cleanup(bo, NULL, &new_tile); + else + nouveau_bo_vm_cleanup(bo, new_tile, &nvbo->tile); + } + + return ret; +} + +static int +nouveau_bo_verify_access(struct ttm_buffer_object *bo, struct file *filp) +{ + struct nouveau_bo *nvbo = nouveau_bo(bo); + + return drm_vma_node_verify_access(&nvbo->gem.vma_node, filp); +} + +static int +nouveau_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) +{ + struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type]; + struct nouveau_drm *drm = nouveau_bdev(bdev); + struct nvkm_mem *node = mem->mm_node; + int ret; + + mem->bus.addr = NULL; + mem->bus.offset = 0; + mem->bus.size = mem->num_pages << PAGE_SHIFT; + mem->bus.base = 0; + mem->bus.is_iomem = false; + if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE)) + return -EINVAL; + switch (mem->mem_type) { + case TTM_PL_SYSTEM: + /* System memory */ + return 0; + case TTM_PL_TT: +#if __OS_HAS_AGP + if (drm->agp.stat == ENABLED) { + mem->bus.offset = mem->start << PAGE_SHIFT; + mem->bus.base = drm->agp.base; + mem->bus.is_iomem = !drm->dev->agp->cant_use_aperture; + } +#endif + if (drm->device.info.family < NV_DEVICE_INFO_V0_TESLA || !node->memtype) + /* untiled */ + break; + /* fallthrough, tiled memory */ + case TTM_PL_VRAM: + mem->bus.offset = mem->start << PAGE_SHIFT; + mem->bus.base = nv_device_resource_start(nvxx_device(&drm->device), 1); + mem->bus.is_iomem = true; + if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA) { + struct nvkm_bar *bar = nvxx_bar(&drm->device); + + ret = bar->umap(bar, node, NV_MEM_ACCESS_RW, + &node->bar_vma); + if (ret) + return ret; + + mem->bus.offset = node->bar_vma.offset; + } + break; + default: + return -EINVAL; + } + return 0; +} + +static void +nouveau_ttm_io_mem_free(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) +{ + struct nouveau_drm *drm = nouveau_bdev(bdev); + struct nvkm_bar *bar = nvxx_bar(&drm->device); + struct nvkm_mem *node = mem->mm_node; + + if (!node->bar_vma.node) + return; + + bar->unmap(bar, &node->bar_vma); +} + +static int +nouveau_ttm_fault_reserve_notify(struct ttm_buffer_object *bo) +{ + struct nouveau_drm *drm = nouveau_bdev(bo->bdev); + struct nouveau_bo *nvbo = nouveau_bo(bo); + struct nvif_device *device = &drm->device; + u32 mappable = nv_device_resource_len(nvxx_device(device), 1) >> PAGE_SHIFT; + int i, ret; + + /* as long as the bo isn't in vram, and isn't tiled, we've got + * nothing to do here. + */ + if (bo->mem.mem_type != TTM_PL_VRAM) { + if (drm->device.info.family < NV_DEVICE_INFO_V0_TESLA || + !nouveau_bo_tile_layout(nvbo)) + return 0; + + if (bo->mem.mem_type == TTM_PL_SYSTEM) { + nouveau_bo_placement_set(nvbo, TTM_PL_TT, 0); + + ret = nouveau_bo_validate(nvbo, false, false); + if (ret) + return ret; + } + return 0; + } + + /* make sure bo is in mappable vram */ + if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA || + bo->mem.start + bo->mem.num_pages < mappable) + return 0; + + for (i = 0; i < nvbo->placement.num_placement; ++i) { + nvbo->placements[i].fpfn = 0; + nvbo->placements[i].lpfn = mappable; + } + + for (i = 0; i < nvbo->placement.num_busy_placement; ++i) { + nvbo->busy_placements[i].fpfn = 0; + nvbo->busy_placements[i].lpfn = mappable; + } + + nouveau_bo_placement_set(nvbo, TTM_PL_FLAG_VRAM, 0); + return nouveau_bo_validate(nvbo, false, false); +} + +static int +nouveau_ttm_tt_populate(struct ttm_tt *ttm) +{ + struct ttm_dma_tt *ttm_dma = (void *)ttm; + struct nouveau_drm *drm; + struct nvkm_device *device; + struct drm_device *dev; + struct device *pdev; + unsigned i; + int r; + bool slave = !!(ttm->page_flags & TTM_PAGE_FLAG_SG); + + if (ttm->state != tt_unpopulated) + return 0; + + if (slave && ttm->sg) { + /* make userspace faulting work */ + drm_prime_sg_to_page_addr_arrays(ttm->sg, ttm->pages, + ttm_dma->dma_address, ttm->num_pages); + ttm->state = tt_unbound; + return 0; + } + + drm = nouveau_bdev(ttm->bdev); + device = nvxx_device(&drm->device); + dev = drm->dev; + pdev = nv_device_base(device); + + /* + * Objects matching this condition have been marked as force_coherent, + * so use the DMA API for them. + */ + if (!nv_device_is_cpu_coherent(device) && + ttm->caching_state == tt_uncached) + return ttm_dma_populate(ttm_dma, dev->dev); + +#if __OS_HAS_AGP + if (drm->agp.stat == ENABLED) { + return ttm_agp_tt_populate(ttm); + } +#endif + +#ifdef CONFIG_SWIOTLB + if (swiotlb_nr_tbl()) { + return ttm_dma_populate((void *)ttm, dev->dev); + } +#endif + + r = ttm_pool_populate(ttm); + if (r) { + return r; + } + + for (i = 0; i < ttm->num_pages; i++) { + dma_addr_t addr; + + addr = dma_map_page(pdev, ttm->pages[i], 0, PAGE_SIZE, + DMA_BIDIRECTIONAL); + + if (dma_mapping_error(pdev, addr)) { + while (--i) { + dma_unmap_page(pdev, ttm_dma->dma_address[i], + PAGE_SIZE, DMA_BIDIRECTIONAL); + ttm_dma->dma_address[i] = 0; + } + ttm_pool_unpopulate(ttm); + return -EFAULT; + } + + ttm_dma->dma_address[i] = addr; + } + return 0; +} + +static void +nouveau_ttm_tt_unpopulate(struct ttm_tt *ttm) +{ + struct ttm_dma_tt *ttm_dma = (void *)ttm; + struct nouveau_drm *drm; + struct nvkm_device *device; + struct drm_device *dev; + struct device *pdev; + unsigned i; + bool slave = !!(ttm->page_flags & TTM_PAGE_FLAG_SG); + + if (slave) + return; + + drm = nouveau_bdev(ttm->bdev); + device = nvxx_device(&drm->device); + dev = drm->dev; + pdev = nv_device_base(device); + + /* + * Objects matching this condition have been marked as force_coherent, + * so use the DMA API for them. + */ + if (!nv_device_is_cpu_coherent(device) && + ttm->caching_state == tt_uncached) { + ttm_dma_unpopulate(ttm_dma, dev->dev); + return; + } + +#if __OS_HAS_AGP + if (drm->agp.stat == ENABLED) { + ttm_agp_tt_unpopulate(ttm); + return; + } +#endif + +#ifdef CONFIG_SWIOTLB + if (swiotlb_nr_tbl()) { + ttm_dma_unpopulate((void *)ttm, dev->dev); + return; + } +#endif + + for (i = 0; i < ttm->num_pages; i++) { + if (ttm_dma->dma_address[i]) { + dma_unmap_page(pdev, ttm_dma->dma_address[i], PAGE_SIZE, + DMA_BIDIRECTIONAL); + } + } + + ttm_pool_unpopulate(ttm); +} + +void +nouveau_bo_fence(struct nouveau_bo *nvbo, struct nouveau_fence *fence, bool exclusive) +{ + struct reservation_object *resv = nvbo->bo.resv; + + if (exclusive) + reservation_object_add_excl_fence(resv, &fence->base); + else if (fence) + reservation_object_add_shared_fence(resv, &fence->base); +} + +struct ttm_bo_driver nouveau_bo_driver = { + .ttm_tt_create = &nouveau_ttm_tt_create, + .ttm_tt_populate = &nouveau_ttm_tt_populate, + .ttm_tt_unpopulate = &nouveau_ttm_tt_unpopulate, + .invalidate_caches = nouveau_bo_invalidate_caches, + .init_mem_type = nouveau_bo_init_mem_type, + .evict_flags = nouveau_bo_evict_flags, + .move_notify = nouveau_bo_move_ntfy, + .move = nouveau_bo_move, + .verify_access = nouveau_bo_verify_access, + .fault_reserve_notify = &nouveau_ttm_fault_reserve_notify, + .io_mem_reserve = &nouveau_ttm_io_mem_reserve, + .io_mem_free = &nouveau_ttm_io_mem_free, +}; + +struct nvkm_vma * +nouveau_bo_vma_find(struct nouveau_bo *nvbo, struct nvkm_vm *vm) +{ + struct nvkm_vma *vma; + list_for_each_entry(vma, &nvbo->vma_list, head) { + if (vma->vm == vm) + return vma; + } + + return NULL; +} + +int +nouveau_bo_vma_add(struct nouveau_bo *nvbo, struct nvkm_vm *vm, + struct nvkm_vma *vma) +{ + const u32 size = nvbo->bo.mem.num_pages << PAGE_SHIFT; + int ret; + + ret = nvkm_vm_get(vm, size, nvbo->page_shift, + NV_MEM_ACCESS_RW, vma); + if (ret) + return ret; + + if ( nvbo->bo.mem.mem_type != TTM_PL_SYSTEM && + (nvbo->bo.mem.mem_type == TTM_PL_VRAM || + nvbo->page_shift != vma->vm->mmu->lpg_shift)) + nvkm_vm_map(vma, nvbo->bo.mem.mm_node); + + list_add_tail(&vma->head, &nvbo->vma_list); + vma->refcount = 1; + return 0; +} + +void +nouveau_bo_vma_del(struct nouveau_bo *nvbo, struct nvkm_vma *vma) +{ + if (vma->node) { + if (nvbo->bo.mem.mem_type != TTM_PL_SYSTEM) + nvkm_vm_unmap(vma); + nvkm_vm_put(vma); + list_del(&vma->head); + } +} diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_bo.h b/kernel/drivers/gpu/drm/nouveau/nouveau_bo.h new file mode 100644 index 000000000..e42360983 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_bo.h @@ -0,0 +1,108 @@ +#ifndef __NOUVEAU_BO_H__ +#define __NOUVEAU_BO_H__ + +#include + +struct nouveau_channel; +struct nouveau_fence; +struct nvkm_vma; + +struct nouveau_bo { + struct ttm_buffer_object bo; + struct ttm_placement placement; + u32 valid_domains; + struct ttm_place placements[3]; + struct ttm_place busy_placements[3]; + bool force_coherent; + struct ttm_bo_kmap_obj kmap; + struct list_head head; + + /* protected by ttm_bo_reserve() */ + struct drm_file *reserved_by; + struct list_head entry; + int pbbo_index; + bool validate_mapped; + + struct list_head vma_list; + unsigned page_shift; + + u32 tile_mode; + u32 tile_flags; + struct nouveau_drm_tile *tile; + + /* Only valid if allocated via nouveau_gem_new() and iff you hold a + * gem reference to it! For debugging, use gem.filp != NULL to test + * whether it is valid. */ + struct drm_gem_object gem; + + /* protect by the ttm reservation lock */ + int pin_refcnt; + + struct ttm_bo_kmap_obj dma_buf_vmap; +}; + +static inline struct nouveau_bo * +nouveau_bo(struct ttm_buffer_object *bo) +{ + return container_of(bo, struct nouveau_bo, bo); +} + +static inline int +nouveau_bo_ref(struct nouveau_bo *ref, struct nouveau_bo **pnvbo) +{ + struct nouveau_bo *prev; + + if (!pnvbo) + return -EINVAL; + prev = *pnvbo; + + *pnvbo = ref ? nouveau_bo(ttm_bo_reference(&ref->bo)) : NULL; + if (prev) { + struct ttm_buffer_object *bo = &prev->bo; + + ttm_bo_unref(&bo); + } + + return 0; +} + +extern struct ttm_bo_driver nouveau_bo_driver; + +void nouveau_bo_move_init(struct nouveau_drm *); +int nouveau_bo_new(struct drm_device *, int size, int align, u32 flags, + u32 tile_mode, u32 tile_flags, struct sg_table *sg, + struct reservation_object *robj, + struct nouveau_bo **); +int nouveau_bo_pin(struct nouveau_bo *, u32 flags, bool contig); +int nouveau_bo_unpin(struct nouveau_bo *); +int nouveau_bo_map(struct nouveau_bo *); +void nouveau_bo_unmap(struct nouveau_bo *); +void nouveau_bo_placement_set(struct nouveau_bo *, u32 type, u32 busy); +void nouveau_bo_wr16(struct nouveau_bo *, unsigned index, u16 val); +u32 nouveau_bo_rd32(struct nouveau_bo *, unsigned index); +void nouveau_bo_wr32(struct nouveau_bo *, unsigned index, u32 val); +void nouveau_bo_fence(struct nouveau_bo *, struct nouveau_fence *, bool exclusive); +int nouveau_bo_validate(struct nouveau_bo *, bool interruptible, + bool no_wait_gpu); +void nouveau_bo_sync_for_device(struct nouveau_bo *nvbo); +void nouveau_bo_sync_for_cpu(struct nouveau_bo *nvbo); + +struct nvkm_vma * +nouveau_bo_vma_find(struct nouveau_bo *, struct nvkm_vm *); + +int nouveau_bo_vma_add(struct nouveau_bo *, struct nvkm_vm *, + struct nvkm_vma *); +void nouveau_bo_vma_del(struct nouveau_bo *, struct nvkm_vma *); + +/* TODO: submit equivalent to TTM generic API upstream? */ +static inline void __iomem * +nvbo_kmap_obj_iovirtual(struct nouveau_bo *nvbo) +{ + bool is_iomem; + void __iomem *ioptr = (void __force __iomem *)ttm_kmap_obj_virtual( + &nvbo->kmap, &is_iomem); + WARN_ON_ONCE(ioptr && !is_iomem); + return ioptr; +} + +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_chan.c b/kernel/drivers/gpu/drm/nouveau/nouveau_chan.c new file mode 100644 index 000000000..0589babc5 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_chan.c @@ -0,0 +1,425 @@ +/* + * 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 + +/*XXX*/ +#include + +#include "nouveau_drm.h" +#include "nouveau_dma.h" +#include "nouveau_bo.h" +#include "nouveau_chan.h" +#include "nouveau_fence.h" +#include "nouveau_abi16.h" + +MODULE_PARM_DESC(vram_pushbuf, "Create DMA push buffers in VRAM"); +int nouveau_vram_pushbuf; +module_param_named(vram_pushbuf, nouveau_vram_pushbuf, int, 0400); + +int +nouveau_channel_idle(struct nouveau_channel *chan) +{ + struct nouveau_cli *cli = (void *)nvif_client(chan->object); + struct nouveau_fence *fence = NULL; + int ret; + + ret = nouveau_fence_new(chan, false, &fence); + if (!ret) { + ret = nouveau_fence_wait(fence, false, false); + nouveau_fence_unref(&fence); + } + + if (ret) + NV_PRINTK(error, cli, "failed to idle channel 0x%08x [%s]\n", + chan->object->handle, nvxx_client(&cli->base)->name); + return ret; +} + +void +nouveau_channel_del(struct nouveau_channel **pchan) +{ + struct nouveau_channel *chan = *pchan; + if (chan) { + if (chan->fence) { + nouveau_channel_idle(chan); + nouveau_fence(chan->drm)->context_del(chan); + } + nvif_object_fini(&chan->nvsw); + nvif_object_fini(&chan->gart); + nvif_object_fini(&chan->vram); + nvif_object_ref(NULL, &chan->object); + nvif_object_fini(&chan->push.ctxdma); + nouveau_bo_vma_del(chan->push.buffer, &chan->push.vma); + nouveau_bo_unmap(chan->push.buffer); + if (chan->push.buffer && chan->push.buffer->pin_refcnt) + nouveau_bo_unpin(chan->push.buffer); + nouveau_bo_ref(NULL, &chan->push.buffer); + nvif_device_ref(NULL, &chan->device); + kfree(chan); + } + *pchan = NULL; +} + +static int +nouveau_channel_prep(struct nouveau_drm *drm, struct nvif_device *device, + u32 handle, u32 size, struct nouveau_channel **pchan) +{ + struct nouveau_cli *cli = (void *)nvif_client(&device->base); + struct nvkm_mmu *mmu = nvxx_mmu(device); + struct nv_dma_v0 args = {}; + struct nouveau_channel *chan; + u32 target; + int ret; + + chan = *pchan = kzalloc(sizeof(*chan), GFP_KERNEL); + if (!chan) + return -ENOMEM; + + nvif_device_ref(device, &chan->device); + chan->drm = drm; + + /* allocate memory for dma push buffer */ + target = TTM_PL_FLAG_TT | TTM_PL_FLAG_UNCACHED; + if (nouveau_vram_pushbuf) + target = TTM_PL_FLAG_VRAM; + + ret = nouveau_bo_new(drm->dev, size, 0, target, 0, 0, NULL, NULL, + &chan->push.buffer); + if (ret == 0) { + ret = nouveau_bo_pin(chan->push.buffer, target, false); + if (ret == 0) + ret = nouveau_bo_map(chan->push.buffer); + } + + if (ret) { + nouveau_channel_del(pchan); + return ret; + } + + /* create dma object covering the *entire* memory space that the + * pushbuf lives in, this is because the GEM code requires that + * we be able to call out to other (indirect) push buffers + */ + chan->push.vma.offset = chan->push.buffer->bo.offset; + + if (device->info.family >= NV_DEVICE_INFO_V0_TESLA) { + ret = nouveau_bo_vma_add(chan->push.buffer, cli->vm, + &chan->push.vma); + if (ret) { + nouveau_channel_del(pchan); + return ret; + } + + args.target = NV_DMA_V0_TARGET_VM; + args.access = NV_DMA_V0_ACCESS_VM; + args.start = 0; + args.limit = cli->vm->mmu->limit - 1; + } else + if (chan->push.buffer->bo.mem.mem_type == TTM_PL_VRAM) { + if (device->info.family == NV_DEVICE_INFO_V0_TNT) { + /* nv04 vram pushbuf hack, retarget to its location in + * the framebuffer bar rather than direct vram access.. + * nfi why this exists, it came from the -nv ddx. + */ + args.target = NV_DMA_V0_TARGET_PCI; + args.access = NV_DMA_V0_ACCESS_RDWR; + args.start = nv_device_resource_start(nvxx_device(device), 1); + args.limit = args.start + device->info.ram_user - 1; + } else { + args.target = NV_DMA_V0_TARGET_VRAM; + args.access = NV_DMA_V0_ACCESS_RDWR; + args.start = 0; + args.limit = device->info.ram_user - 1; + } + } else { + if (chan->drm->agp.stat == ENABLED) { + args.target = NV_DMA_V0_TARGET_AGP; + args.access = NV_DMA_V0_ACCESS_RDWR; + args.start = chan->drm->agp.base; + args.limit = chan->drm->agp.base + + chan->drm->agp.size - 1; + } else { + args.target = NV_DMA_V0_TARGET_VM; + args.access = NV_DMA_V0_ACCESS_RDWR; + args.start = 0; + args.limit = mmu->limit - 1; + } + } + + ret = nvif_object_init(nvif_object(device), NULL, NVDRM_PUSH | + (handle & 0xffff), NV_DMA_FROM_MEMORY, + &args, sizeof(args), &chan->push.ctxdma); + if (ret) { + nouveau_channel_del(pchan); + return ret; + } + + return 0; +} + +static int +nouveau_channel_ind(struct nouveau_drm *drm, struct nvif_device *device, + u32 handle, u32 engine, struct nouveau_channel **pchan) +{ + static const u16 oclasses[] = { MAXWELL_CHANNEL_GPFIFO_A, + KEPLER_CHANNEL_GPFIFO_A, + FERMI_CHANNEL_GPFIFO, + G82_CHANNEL_GPFIFO, + NV50_CHANNEL_GPFIFO, + 0 }; + const u16 *oclass = oclasses; + union { + struct nv50_channel_gpfifo_v0 nv50; + struct kepler_channel_gpfifo_a_v0 kepler; + } args, *retn; + struct nouveau_channel *chan; + u32 size; + int ret; + + /* allocate dma push buffer */ + ret = nouveau_channel_prep(drm, device, handle, 0x12000, &chan); + *pchan = chan; + if (ret) + return ret; + + /* create channel object */ + do { + if (oclass[0] >= KEPLER_CHANNEL_GPFIFO_A) { + args.kepler.version = 0; + args.kepler.engine = engine; + args.kepler.pushbuf = chan->push.ctxdma.handle; + args.kepler.ilength = 0x02000; + args.kepler.ioffset = 0x10000 + chan->push.vma.offset; + size = sizeof(args.kepler); + } else { + args.nv50.version = 0; + args.nv50.pushbuf = chan->push.ctxdma.handle; + args.nv50.ilength = 0x02000; + args.nv50.ioffset = 0x10000 + chan->push.vma.offset; + size = sizeof(args.nv50); + } + + ret = nvif_object_new(nvif_object(device), handle, *oclass++, + &args, size, &chan->object); + if (ret == 0) { + retn = chan->object->data; + if (chan->object->oclass >= KEPLER_CHANNEL_GPFIFO_A) + chan->chid = retn->kepler.chid; + else + chan->chid = retn->nv50.chid; + return ret; + } + } while (*oclass); + + nouveau_channel_del(pchan); + return ret; +} + +static int +nouveau_channel_dma(struct nouveau_drm *drm, struct nvif_device *device, + u32 handle, struct nouveau_channel **pchan) +{ + static const u16 oclasses[] = { NV40_CHANNEL_DMA, + NV17_CHANNEL_DMA, + NV10_CHANNEL_DMA, + NV03_CHANNEL_DMA, + 0 }; + const u16 *oclass = oclasses; + struct nv03_channel_dma_v0 args, *retn; + struct nouveau_channel *chan; + int ret; + + /* allocate dma push buffer */ + ret = nouveau_channel_prep(drm, device, handle, 0x10000, &chan); + *pchan = chan; + if (ret) + return ret; + + /* create channel object */ + args.version = 0; + args.pushbuf = chan->push.ctxdma.handle; + args.offset = chan->push.vma.offset; + + do { + ret = nvif_object_new(nvif_object(device), handle, *oclass++, + &args, sizeof(args), &chan->object); + if (ret == 0) { + retn = chan->object->data; + chan->chid = retn->chid; + return ret; + } + } while (ret && *oclass); + + nouveau_channel_del(pchan); + return ret; +} + +static int +nouveau_channel_init(struct nouveau_channel *chan, u32 vram, u32 gart) +{ + struct nvif_device *device = chan->device; + struct nouveau_cli *cli = (void *)nvif_client(&device->base); + struct nvkm_mmu *mmu = nvxx_mmu(device); + struct nvkm_sw_chan *swch; + struct nv_dma_v0 args = {}; + int ret, i; + + nvif_object_map(chan->object); + + /* allocate dma objects to cover all allowed vram, and gart */ + if (device->info.family < NV_DEVICE_INFO_V0_FERMI) { + if (device->info.family >= NV_DEVICE_INFO_V0_TESLA) { + args.target = NV_DMA_V0_TARGET_VM; + args.access = NV_DMA_V0_ACCESS_VM; + args.start = 0; + args.limit = cli->vm->mmu->limit - 1; + } else { + args.target = NV_DMA_V0_TARGET_VRAM; + args.access = NV_DMA_V0_ACCESS_RDWR; + args.start = 0; + args.limit = device->info.ram_user - 1; + } + + ret = nvif_object_init(chan->object, NULL, vram, + NV_DMA_IN_MEMORY, &args, + sizeof(args), &chan->vram); + if (ret) + return ret; + + if (device->info.family >= NV_DEVICE_INFO_V0_TESLA) { + args.target = NV_DMA_V0_TARGET_VM; + args.access = NV_DMA_V0_ACCESS_VM; + args.start = 0; + args.limit = cli->vm->mmu->limit - 1; + } else + if (chan->drm->agp.stat == ENABLED) { + args.target = NV_DMA_V0_TARGET_AGP; + args.access = NV_DMA_V0_ACCESS_RDWR; + args.start = chan->drm->agp.base; + args.limit = chan->drm->agp.base + + chan->drm->agp.size - 1; + } else { + args.target = NV_DMA_V0_TARGET_VM; + args.access = NV_DMA_V0_ACCESS_RDWR; + args.start = 0; + args.limit = mmu->limit - 1; + } + + ret = nvif_object_init(chan->object, NULL, gart, + NV_DMA_IN_MEMORY, &args, + sizeof(args), &chan->gart); + if (ret) + return ret; + } + + /* initialise dma tracking parameters */ + switch (chan->object->oclass & 0x00ff) { + case 0x006b: + case 0x006e: + chan->user_put = 0x40; + chan->user_get = 0x44; + chan->dma.max = (0x10000 / 4) - 2; + break; + default: + chan->user_put = 0x40; + chan->user_get = 0x44; + chan->user_get_hi = 0x60; + chan->dma.ib_base = 0x10000 / 4; + chan->dma.ib_max = (0x02000 / 8) - 1; + chan->dma.ib_put = 0; + chan->dma.ib_free = chan->dma.ib_max - chan->dma.ib_put; + chan->dma.max = chan->dma.ib_base; + break; + } + + chan->dma.put = 0; + chan->dma.cur = chan->dma.put; + chan->dma.free = chan->dma.max - chan->dma.cur; + + ret = RING_SPACE(chan, NOUVEAU_DMA_SKIPS); + if (ret) + return ret; + + for (i = 0; i < NOUVEAU_DMA_SKIPS; i++) + OUT_RING(chan, 0x00000000); + + /* allocate software object class (used for fences on <= nv05) */ + if (device->info.family < NV_DEVICE_INFO_V0_CELSIUS) { + ret = nvif_object_init(chan->object, NULL, 0x006e, 0x006e, + NULL, 0, &chan->nvsw); + if (ret) + return ret; + + swch = (void *)nvxx_object(&chan->nvsw)->parent; + swch->flip = nouveau_flip_complete; + swch->flip_data = chan; + + ret = RING_SPACE(chan, 2); + if (ret) + return ret; + + BEGIN_NV04(chan, NvSubSw, 0x0000, 1); + OUT_RING (chan, chan->nvsw.handle); + FIRE_RING (chan); + } + + /* initialise synchronisation */ + return nouveau_fence(chan->drm)->context_new(chan); +} + +int +nouveau_channel_new(struct nouveau_drm *drm, struct nvif_device *device, + u32 handle, u32 arg0, u32 arg1, + struct nouveau_channel **pchan) +{ + struct nouveau_cli *cli = (void *)nvif_client(&device->base); + bool super; + int ret; + + /* hack until fencenv50 is fixed, and agp access relaxed */ + super = cli->base.super; + cli->base.super = true; + + ret = nouveau_channel_ind(drm, device, handle, arg0, pchan); + if (ret) { + NV_PRINTK(debug, cli, "ib channel create, %d\n", ret); + ret = nouveau_channel_dma(drm, device, handle, pchan); + if (ret) { + NV_PRINTK(debug, cli, "dma channel create, %d\n", ret); + goto done; + } + } + + ret = nouveau_channel_init(*pchan, arg0, arg1); + if (ret) { + NV_PRINTK(error, cli, "channel failed to initialise, %d\n", ret); + nouveau_channel_del(pchan); + } + +done: + cli->base.super = super; + return ret; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_chan.h b/kernel/drivers/gpu/drm/nouveau/nouveau_chan.h new file mode 100644 index 000000000..8b3640f69 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_chan.h @@ -0,0 +1,52 @@ +#ifndef __NOUVEAU_CHAN_H__ +#define __NOUVEAU_CHAN_H__ + +#include +struct nvif_device; + +struct nouveau_channel { + struct nvif_device *device; + struct nouveau_drm *drm; + + int chid; + + struct nvif_object vram; + struct nvif_object gart; + struct nvif_object nvsw; + + struct { + struct nouveau_bo *buffer; + struct nvkm_vma vma; + struct nvif_object ctxdma; + } push; + + /* TODO: this will be reworked in the near future */ + bool accel_done; + void *fence; + struct { + int max; + int free; + int cur; + int put; + int ib_base; + int ib_max; + int ib_free; + int ib_put; + } dma; + u32 user_get_hi; + u32 user_get; + u32 user_put; + + struct nvif_object *object; +}; + + +int nouveau_channel_new(struct nouveau_drm *, struct nvif_device *, + u32 handle, u32 arg0, u32 arg1, + struct nouveau_channel **); +void nouveau_channel_del(struct nouveau_channel **); +int nouveau_channel_idle(struct nouveau_channel *); + +extern int nouveau_vram_pushbuf; + +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_connector.c b/kernel/drivers/gpu/drm/nouveau/nouveau_connector.c new file mode 100644 index 000000000..3162040bc --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -0,0 +1,1275 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 + +#include + +#include +#include +#include + +#include "nouveau_reg.h" +#include "nouveau_drm.h" +#include "dispnv04/hw.h" +#include "nouveau_acpi.h" + +#include "nouveau_display.h" +#include "nouveau_connector.h" +#include "nouveau_encoder.h" +#include "nouveau_crtc.h" + +#include + +MODULE_PARM_DESC(tv_disable, "Disable TV-out detection"); +int nouveau_tv_disable = 0; +module_param_named(tv_disable, nouveau_tv_disable, int, 0400); + +MODULE_PARM_DESC(ignorelid, "Ignore ACPI lid status"); +int nouveau_ignorelid = 0; +module_param_named(ignorelid, nouveau_ignorelid, int, 0400); + +MODULE_PARM_DESC(duallink, "Allow dual-link TMDS (default: enabled)"); +int nouveau_duallink = 1; +module_param_named(duallink, nouveau_duallink, int, 0400); + +struct nouveau_encoder * +find_encoder(struct drm_connector *connector, int type) +{ + struct drm_device *dev = connector->dev; + struct nouveau_encoder *nv_encoder; + struct drm_encoder *enc; + int i, id; + + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { + id = connector->encoder_ids[i]; + if (!id) + break; + + enc = drm_encoder_find(dev, id); + if (!enc) + continue; + nv_encoder = nouveau_encoder(enc); + + if (type == DCB_OUTPUT_ANY || + (nv_encoder->dcb && nv_encoder->dcb->type == type)) + return nv_encoder; + } + + return NULL; +} + +struct nouveau_connector * +nouveau_encoder_connector_get(struct nouveau_encoder *encoder) +{ + struct drm_device *dev = to_drm_encoder(encoder)->dev; + struct drm_connector *drm_connector; + + list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head) { + if (drm_connector->encoder == to_drm_encoder(encoder)) + return nouveau_connector(drm_connector); + } + + return NULL; +} + +static void +nouveau_connector_destroy(struct drm_connector *connector) +{ + struct nouveau_connector *nv_connector = nouveau_connector(connector); + nvif_notify_fini(&nv_connector->hpd); + kfree(nv_connector->edid); + drm_connector_unregister(connector); + drm_connector_cleanup(connector); + if (nv_connector->aux.transfer) + drm_dp_aux_unregister(&nv_connector->aux); + kfree(connector); +} + +static struct nouveau_encoder * +nouveau_connector_ddc_detect(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct nouveau_connector *nv_connector = nouveau_connector(connector); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_gpio *gpio = nvxx_gpio(&drm->device); + struct nouveau_encoder *nv_encoder; + struct drm_encoder *encoder; + int i, panel = -ENODEV; + + /* 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 (nv_connector->type == DCB_CONNECTOR_eDP) { + panel = gpio->get(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff); + if (panel == 0) { + gpio->set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, 1); + msleep(300); + } + } + + for (i = 0; nv_encoder = NULL, i < DRM_CONNECTOR_MAX_ENCODER; i++) { + int id = connector->encoder_ids[i]; + if (id == 0) + break; + + encoder = drm_encoder_find(dev, id); + if (!encoder) + continue; + nv_encoder = nouveau_encoder(encoder); + + if (nv_encoder->dcb->type == DCB_OUTPUT_DP) { + int ret = nouveau_dp_detect(nv_encoder); + if (ret == 0) + break; + } else + if (nv_encoder->i2c) { + if (nv_probe_i2c(nv_encoder->i2c, 0x50)) + break; + } + } + + /* eDP panel not detected, restore panel power GPIO to previous + * state to avoid confusing the SOR for other output types. + */ + if (!nv_encoder && panel == 0) + gpio->set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, panel); + + return nv_encoder; +} + +static struct nouveau_encoder * +nouveau_connector_of_detect(struct drm_connector *connector) +{ +#ifdef __powerpc__ + struct drm_device *dev = connector->dev; + struct nouveau_connector *nv_connector = nouveau_connector(connector); + struct nouveau_encoder *nv_encoder; + struct device_node *cn, *dn = pci_device_to_OF_node(dev->pdev); + + if (!dn || + !((nv_encoder = find_encoder(connector, DCB_OUTPUT_TMDS)) || + (nv_encoder = find_encoder(connector, DCB_OUTPUT_ANALOG)))) + return NULL; + + for_each_child_of_node(dn, cn) { + const char *name = of_get_property(cn, "name", NULL); + const void *edid = of_get_property(cn, "EDID", NULL); + int idx = name ? name[strlen(name) - 1] - 'A' : 0; + + if (nv_encoder->dcb->i2c_index == idx && edid) { + nv_connector->edid = + kmemdup(edid, EDID_LENGTH, GFP_KERNEL); + of_node_put(cn); + return nv_encoder; + } + } +#endif + return NULL; +} + +static void +nouveau_connector_set_encoder(struct drm_connector *connector, + struct nouveau_encoder *nv_encoder) +{ + struct nouveau_connector *nv_connector = nouveau_connector(connector); + struct nouveau_drm *drm = nouveau_drm(connector->dev); + struct drm_device *dev = connector->dev; + + if (nv_connector->detected_encoder == nv_encoder) + return; + nv_connector->detected_encoder = nv_encoder; + + if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA) { + connector->interlace_allowed = true; + connector->doublescan_allowed = true; + } else + if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS || + nv_encoder->dcb->type == DCB_OUTPUT_TMDS) { + connector->doublescan_allowed = false; + connector->interlace_allowed = false; + } else { + connector->doublescan_allowed = true; + if (drm->device.info.family == NV_DEVICE_INFO_V0_KELVIN || + (drm->device.info.family == NV_DEVICE_INFO_V0_CELSIUS && + (dev->pdev->device & 0x0ff0) != 0x0100 && + (dev->pdev->device & 0x0ff0) != 0x0150)) + /* HW is broken */ + connector->interlace_allowed = false; + else + connector->interlace_allowed = true; + } + + if (nv_connector->type == DCB_CONNECTOR_DVI_I) { + drm_object_property_set_value(&connector->base, + dev->mode_config.dvi_i_subconnector_property, + nv_encoder->dcb->type == DCB_OUTPUT_TMDS ? + DRM_MODE_SUBCONNECTOR_DVID : + DRM_MODE_SUBCONNECTOR_DVIA); + } +} + +static enum drm_connector_status +nouveau_connector_detect(struct drm_connector *connector, bool force) +{ + struct drm_device *dev = connector->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_connector *nv_connector = nouveau_connector(connector); + struct nouveau_encoder *nv_encoder = NULL; + struct nouveau_encoder *nv_partner; + struct nvkm_i2c_port *i2c; + int type; + int ret; + enum drm_connector_status conn_status = connector_status_disconnected; + + /* Cleanup the previous EDID block. */ + if (nv_connector->edid) { + drm_mode_connector_update_edid_property(connector, NULL); + kfree(nv_connector->edid); + nv_connector->edid = NULL; + } + + ret = pm_runtime_get_sync(connector->dev->dev); + if (ret < 0 && ret != -EACCES) + return conn_status; + + nv_encoder = nouveau_connector_ddc_detect(connector); + if (nv_encoder && (i2c = nv_encoder->i2c) != NULL) { + nv_connector->edid = drm_get_edid(connector, &i2c->adapter); + drm_mode_connector_update_edid_property(connector, + nv_connector->edid); + if (!nv_connector->edid) { + NV_ERROR(drm, "DDC responded, but no EDID for %s\n", + connector->name); + goto detect_analog; + } + + /* Override encoder type for DVI-I based on whether EDID + * says the display is digital or analog, both use the + * same i2c channel so the value returned from ddc_detect + * isn't necessarily correct. + */ + nv_partner = NULL; + if (nv_encoder->dcb->type == DCB_OUTPUT_TMDS) + nv_partner = find_encoder(connector, DCB_OUTPUT_ANALOG); + if (nv_encoder->dcb->type == DCB_OUTPUT_ANALOG) + nv_partner = find_encoder(connector, DCB_OUTPUT_TMDS); + + if (nv_partner && ((nv_encoder->dcb->type == DCB_OUTPUT_ANALOG && + nv_partner->dcb->type == DCB_OUTPUT_TMDS) || + (nv_encoder->dcb->type == DCB_OUTPUT_TMDS && + nv_partner->dcb->type == DCB_OUTPUT_ANALOG))) { + if (nv_connector->edid->input & DRM_EDID_INPUT_DIGITAL) + type = DCB_OUTPUT_TMDS; + else + type = DCB_OUTPUT_ANALOG; + + nv_encoder = find_encoder(connector, type); + } + + nouveau_connector_set_encoder(connector, nv_encoder); + conn_status = connector_status_connected; + goto out; + } + + nv_encoder = nouveau_connector_of_detect(connector); + if (nv_encoder) { + nouveau_connector_set_encoder(connector, nv_encoder); + conn_status = connector_status_connected; + goto out; + } + +detect_analog: + nv_encoder = find_encoder(connector, DCB_OUTPUT_ANALOG); + if (!nv_encoder && !nouveau_tv_disable) + nv_encoder = find_encoder(connector, DCB_OUTPUT_TV); + if (nv_encoder && force) { + struct drm_encoder *encoder = to_drm_encoder(nv_encoder); + const struct drm_encoder_helper_funcs *helper = + encoder->helper_private; + + if (helper->detect(encoder, connector) == + connector_status_connected) { + nouveau_connector_set_encoder(connector, nv_encoder); + conn_status = connector_status_connected; + goto out; + } + + } + + out: + + pm_runtime_mark_last_busy(connector->dev->dev); + pm_runtime_put_autosuspend(connector->dev->dev); + + return conn_status; +} + +static enum drm_connector_status +nouveau_connector_detect_lvds(struct drm_connector *connector, bool force) +{ + struct drm_device *dev = connector->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_connector *nv_connector = nouveau_connector(connector); + struct nouveau_encoder *nv_encoder = NULL; + enum drm_connector_status status = connector_status_disconnected; + + /* Cleanup the previous EDID block. */ + if (nv_connector->edid) { + drm_mode_connector_update_edid_property(connector, NULL); + kfree(nv_connector->edid); + nv_connector->edid = NULL; + } + + nv_encoder = find_encoder(connector, DCB_OUTPUT_LVDS); + if (!nv_encoder) + return connector_status_disconnected; + + /* Try retrieving EDID via DDC */ + if (!drm->vbios.fp_no_ddc) { + status = nouveau_connector_detect(connector, force); + if (status == connector_status_connected) + goto out; + } + + /* On some laptops (Sony, i'm looking at you) there appears to + * be no direct way of accessing the panel's EDID. The only + * option available to us appears to be to ask ACPI for help.. + * + * It's important this check's before trying straps, one of the + * said manufacturer's laptops are configured in such a way + * the nouveau decides an entry in the VBIOS FP mode table is + * valid - it's not (rh#613284) + */ + if (nv_encoder->dcb->lvdsconf.use_acpi_for_edid) { + if ((nv_connector->edid = nouveau_acpi_edid(dev, connector))) { + status = connector_status_connected; + goto out; + } + } + + /* If no EDID found above, and the VBIOS indicates a hardcoded + * modeline is avalilable for the panel, set it as the panel's + * native mode and exit. + */ + if (nouveau_bios_fp_mode(dev, NULL) && (drm->vbios.fp_no_ddc || + nv_encoder->dcb->lvdsconf.use_straps_for_mode)) { + status = connector_status_connected; + goto out; + } + + /* Still nothing, some VBIOS images have a hardcoded EDID block + * stored for the panel stored in them. + */ + if (!drm->vbios.fp_no_ddc) { + struct edid *edid = + (struct edid *)nouveau_bios_embedded_edid(dev); + if (edid) { + nv_connector->edid = + kmemdup(edid, EDID_LENGTH, GFP_KERNEL); + if (nv_connector->edid) + status = connector_status_connected; + } + } + +out: +#if defined(CONFIG_ACPI_BUTTON) || \ + (defined(CONFIG_ACPI_BUTTON_MODULE) && defined(MODULE)) + if (status == connector_status_connected && + !nouveau_ignorelid && !acpi_lid_open()) + status = connector_status_unknown; +#endif + + drm_mode_connector_update_edid_property(connector, nv_connector->edid); + nouveau_connector_set_encoder(connector, nv_encoder); + return status; +} + +static void +nouveau_connector_force(struct drm_connector *connector) +{ + struct nouveau_drm *drm = nouveau_drm(connector->dev); + struct nouveau_connector *nv_connector = nouveau_connector(connector); + struct nouveau_encoder *nv_encoder; + int type; + + if (nv_connector->type == DCB_CONNECTOR_DVI_I) { + if (connector->force == DRM_FORCE_ON_DIGITAL) + type = DCB_OUTPUT_TMDS; + else + type = DCB_OUTPUT_ANALOG; + } else + type = DCB_OUTPUT_ANY; + + nv_encoder = find_encoder(connector, type); + if (!nv_encoder) { + NV_ERROR(drm, "can't find encoder to force %s on!\n", + connector->name); + connector->status = connector_status_disconnected; + return; + } + + nouveau_connector_set_encoder(connector, nv_encoder); +} + +static int +nouveau_connector_set_property(struct drm_connector *connector, + struct drm_property *property, uint64_t value) +{ + struct nouveau_display *disp = nouveau_display(connector->dev); + struct nouveau_connector *nv_connector = nouveau_connector(connector); + struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; + struct drm_encoder *encoder = to_drm_encoder(nv_encoder); + struct drm_device *dev = connector->dev; + struct nouveau_crtc *nv_crtc; + int ret; + + nv_crtc = NULL; + if (connector->encoder && connector->encoder->crtc) + nv_crtc = nouveau_crtc(connector->encoder->crtc); + + /* Scaling mode */ + if (property == dev->mode_config.scaling_mode_property) { + bool modeset = false; + + switch (value) { + case DRM_MODE_SCALE_NONE: + /* We allow 'None' for EDID modes, even on a fixed + * panel (some exist with support for lower refresh + * rates, which people might want to use for power + * saving purposes). + * + * Non-EDID modes will force the use of GPU scaling + * to the native mode regardless of this setting. + */ + switch (nv_connector->type) { + case DCB_CONNECTOR_LVDS: + case DCB_CONNECTOR_LVDS_SPWG: + case DCB_CONNECTOR_eDP: + /* ... except prior to G80, where the code + * doesn't support such things. + */ + if (disp->disp.oclass < NV50_DISP) + return -EINVAL; + break; + default: + break; + } + break; + case DRM_MODE_SCALE_FULLSCREEN: + case DRM_MODE_SCALE_CENTER: + case DRM_MODE_SCALE_ASPECT: + break; + default: + return -EINVAL; + } + + /* Changing between GPU and panel scaling requires a full + * modeset + */ + if ((nv_connector->scaling_mode == DRM_MODE_SCALE_NONE) || + (value == DRM_MODE_SCALE_NONE)) + modeset = true; + nv_connector->scaling_mode = value; + + if (!nv_crtc) + return 0; + + if (modeset || !nv_crtc->set_scale) { + ret = drm_crtc_helper_set_mode(&nv_crtc->base, + &nv_crtc->base.mode, + nv_crtc->base.x, + nv_crtc->base.y, NULL); + if (!ret) + return -EINVAL; + } else { + ret = nv_crtc->set_scale(nv_crtc, true); + if (ret) + return ret; + } + + return 0; + } + + /* Underscan */ + if (property == disp->underscan_property) { + if (nv_connector->underscan != value) { + nv_connector->underscan = value; + if (!nv_crtc || !nv_crtc->set_scale) + return 0; + + return nv_crtc->set_scale(nv_crtc, true); + } + + return 0; + } + + if (property == disp->underscan_hborder_property) { + if (nv_connector->underscan_hborder != value) { + nv_connector->underscan_hborder = value; + if (!nv_crtc || !nv_crtc->set_scale) + return 0; + + return nv_crtc->set_scale(nv_crtc, true); + } + + return 0; + } + + if (property == disp->underscan_vborder_property) { + if (nv_connector->underscan_vborder != value) { + nv_connector->underscan_vborder = value; + if (!nv_crtc || !nv_crtc->set_scale) + return 0; + + return nv_crtc->set_scale(nv_crtc, true); + } + + return 0; + } + + /* Dithering */ + if (property == disp->dithering_mode) { + nv_connector->dithering_mode = value; + if (!nv_crtc || !nv_crtc->set_dither) + return 0; + + return nv_crtc->set_dither(nv_crtc, true); + } + + if (property == disp->dithering_depth) { + nv_connector->dithering_depth = value; + if (!nv_crtc || !nv_crtc->set_dither) + return 0; + + return nv_crtc->set_dither(nv_crtc, true); + } + + if (nv_crtc && nv_crtc->set_color_vibrance) { + /* Hue */ + if (property == disp->vibrant_hue_property) { + nv_crtc->vibrant_hue = value - 90; + return nv_crtc->set_color_vibrance(nv_crtc, true); + } + /* Saturation */ + if (property == disp->color_vibrance_property) { + nv_crtc->color_vibrance = value - 100; + return nv_crtc->set_color_vibrance(nv_crtc, true); + } + } + + if (nv_encoder && nv_encoder->dcb->type == DCB_OUTPUT_TV) + return get_slave_funcs(encoder)->set_property( + encoder, connector, property, value); + + return -EINVAL; +} + +static struct drm_display_mode * +nouveau_connector_native_mode(struct drm_connector *connector) +{ + const struct drm_connector_helper_funcs *helper = connector->helper_private; + struct nouveau_drm *drm = nouveau_drm(connector->dev); + struct nouveau_connector *nv_connector = nouveau_connector(connector); + struct drm_device *dev = connector->dev; + struct drm_display_mode *mode, *largest = NULL; + int high_w = 0, high_h = 0, high_v = 0; + + list_for_each_entry(mode, &nv_connector->base.probed_modes, head) { + mode->vrefresh = drm_mode_vrefresh(mode); + if (helper->mode_valid(connector, mode) != MODE_OK || + (mode->flags & DRM_MODE_FLAG_INTERLACE)) + continue; + + /* Use preferred mode if there is one.. */ + if (mode->type & DRM_MODE_TYPE_PREFERRED) { + NV_DEBUG(drm, "native mode from preferred\n"); + return drm_mode_duplicate(dev, mode); + } + + /* Otherwise, take the resolution with the largest width, then + * height, then vertical refresh + */ + if (mode->hdisplay < high_w) + continue; + + if (mode->hdisplay == high_w && mode->vdisplay < high_h) + continue; + + if (mode->hdisplay == high_w && mode->vdisplay == high_h && + mode->vrefresh < high_v) + continue; + + high_w = mode->hdisplay; + high_h = mode->vdisplay; + high_v = mode->vrefresh; + largest = mode; + } + + NV_DEBUG(drm, "native mode from largest: %dx%d@%d\n", + high_w, high_h, high_v); + return largest ? drm_mode_duplicate(dev, largest) : NULL; +} + +struct moderec { + int hdisplay; + int vdisplay; +}; + +static struct moderec scaler_modes[] = { + { 1920, 1200 }, + { 1920, 1080 }, + { 1680, 1050 }, + { 1600, 1200 }, + { 1400, 1050 }, + { 1280, 1024 }, + { 1280, 960 }, + { 1152, 864 }, + { 1024, 768 }, + { 800, 600 }, + { 720, 400 }, + { 640, 480 }, + { 640, 400 }, + { 640, 350 }, + {} +}; + +static int +nouveau_connector_scaler_modes_add(struct drm_connector *connector) +{ + struct nouveau_connector *nv_connector = nouveau_connector(connector); + struct drm_display_mode *native = nv_connector->native_mode, *m; + struct drm_device *dev = connector->dev; + struct moderec *mode = &scaler_modes[0]; + int modes = 0; + + if (!native) + return 0; + + while (mode->hdisplay) { + if (mode->hdisplay <= native->hdisplay && + mode->vdisplay <= native->vdisplay && + (mode->hdisplay != native->hdisplay || + mode->vdisplay != native->vdisplay)) { + m = drm_cvt_mode(dev, mode->hdisplay, mode->vdisplay, + drm_mode_vrefresh(native), false, + false, false); + if (!m) + continue; + + drm_mode_probed_add(connector, m); + modes++; + } + + mode++; + } + + return modes; +} + +static void +nouveau_connector_detect_depth(struct drm_connector *connector) +{ + struct nouveau_drm *drm = nouveau_drm(connector->dev); + struct nouveau_connector *nv_connector = nouveau_connector(connector); + struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; + struct nvbios *bios = &drm->vbios; + struct drm_display_mode *mode = nv_connector->native_mode; + bool duallink; + + /* if the edid is feeling nice enough to provide this info, use it */ + if (nv_connector->edid && connector->display_info.bpc) + return; + + /* EDID 1.4 is *supposed* to be supported on eDP, but, Apple... */ + if (nv_connector->type == DCB_CONNECTOR_eDP) { + connector->display_info.bpc = 6; + return; + } + + /* we're out of options unless we're LVDS, default to 8bpc */ + if (nv_encoder->dcb->type != DCB_OUTPUT_LVDS) { + connector->display_info.bpc = 8; + return; + } + + connector->display_info.bpc = 6; + + /* LVDS: panel straps */ + if (bios->fp_no_ddc) { + if (bios->fp.if_is_24bit) + connector->display_info.bpc = 8; + return; + } + + /* LVDS: DDC panel, need to first determine the number of links to + * know which if_is_24bit flag to check... + */ + if (nv_connector->edid && + nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) + duallink = ((u8 *)nv_connector->edid)[121] == 2; + else + duallink = mode->clock >= bios->fp.duallink_transition_clk; + + if ((!duallink && (bios->fp.strapless_is_24bit & 1)) || + ( duallink && (bios->fp.strapless_is_24bit & 2))) + connector->display_info.bpc = 8; +} + +static int +nouveau_connector_get_modes(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_connector *nv_connector = nouveau_connector(connector); + struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; + struct drm_encoder *encoder = to_drm_encoder(nv_encoder); + int ret = 0; + + /* destroy the native mode, the attached monitor could have changed. + */ + if (nv_connector->native_mode) { + drm_mode_destroy(dev, nv_connector->native_mode); + nv_connector->native_mode = NULL; + } + + if (nv_connector->edid) + ret = drm_add_edid_modes(connector, nv_connector->edid); + else + if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS && + (nv_encoder->dcb->lvdsconf.use_straps_for_mode || + drm->vbios.fp_no_ddc) && nouveau_bios_fp_mode(dev, NULL)) { + struct drm_display_mode mode; + + nouveau_bios_fp_mode(dev, &mode); + nv_connector->native_mode = drm_mode_duplicate(dev, &mode); + } + + /* Determine display colour depth for everything except LVDS now, + * DP requires this before mode_valid() is called. + */ + if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS) + nouveau_connector_detect_depth(connector); + + /* Find the native mode if this is a digital panel, if we didn't + * find any modes through DDC previously add the native mode to + * the list of modes. + */ + if (!nv_connector->native_mode) + nv_connector->native_mode = + nouveau_connector_native_mode(connector); + if (ret == 0 && nv_connector->native_mode) { + struct drm_display_mode *mode; + + mode = drm_mode_duplicate(dev, nv_connector->native_mode); + drm_mode_probed_add(connector, mode); + ret = 1; + } + + /* Determine LVDS colour depth, must happen after determining + * "native" mode as some VBIOS tables require us to use the + * pixel clock as part of the lookup... + */ + if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) + nouveau_connector_detect_depth(connector); + + if (nv_encoder->dcb->type == DCB_OUTPUT_TV) + ret = get_slave_funcs(encoder)->get_modes(encoder, connector); + + if (nv_connector->type == DCB_CONNECTOR_LVDS || + nv_connector->type == DCB_CONNECTOR_LVDS_SPWG || + nv_connector->type == DCB_CONNECTOR_eDP) + ret += nouveau_connector_scaler_modes_add(connector); + + return ret; +} + +static unsigned +get_tmds_link_bandwidth(struct drm_connector *connector) +{ + struct nouveau_connector *nv_connector = nouveau_connector(connector); + struct nouveau_drm *drm = nouveau_drm(connector->dev); + struct dcb_output *dcb = nv_connector->detected_encoder->dcb; + + if (dcb->location != DCB_LOC_ON_CHIP || + drm->device.info.chipset >= 0x46) + return 165000; + else if (drm->device.info.chipset >= 0x40) + return 155000; + else if (drm->device.info.chipset >= 0x18) + return 135000; + else + return 112000; +} + +static int +nouveau_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct nouveau_connector *nv_connector = nouveau_connector(connector); + struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; + struct drm_encoder *encoder = to_drm_encoder(nv_encoder); + unsigned min_clock = 25000, max_clock = min_clock; + unsigned clock = mode->clock; + + switch (nv_encoder->dcb->type) { + case DCB_OUTPUT_LVDS: + if (nv_connector->native_mode && + (mode->hdisplay > nv_connector->native_mode->hdisplay || + mode->vdisplay > nv_connector->native_mode->vdisplay)) + return MODE_PANEL; + + min_clock = 0; + max_clock = 400000; + break; + case DCB_OUTPUT_TMDS: + max_clock = get_tmds_link_bandwidth(connector); + if (nouveau_duallink && nv_encoder->dcb->duallink_possible) + max_clock *= 2; + break; + case DCB_OUTPUT_ANALOG: + max_clock = nv_encoder->dcb->crtconf.maxfreq; + if (!max_clock) + max_clock = 350000; + break; + case DCB_OUTPUT_TV: + return get_slave_funcs(encoder)->mode_valid(encoder, mode); + case DCB_OUTPUT_DP: + max_clock = nv_encoder->dp.link_nr; + max_clock *= nv_encoder->dp.link_bw; + clock = clock * (connector->display_info.bpc * 3) / 10; + break; + default: + BUG_ON(1); + return MODE_BAD; + } + + if (clock < min_clock) + return MODE_CLOCK_LOW; + + if (clock > max_clock) + return MODE_CLOCK_HIGH; + + return MODE_OK; +} + +static struct drm_encoder * +nouveau_connector_best_encoder(struct drm_connector *connector) +{ + struct nouveau_connector *nv_connector = nouveau_connector(connector); + + if (nv_connector->detected_encoder) + return to_drm_encoder(nv_connector->detected_encoder); + + return NULL; +} + +static const struct drm_connector_helper_funcs +nouveau_connector_helper_funcs = { + .get_modes = nouveau_connector_get_modes, + .mode_valid = nouveau_connector_mode_valid, + .best_encoder = nouveau_connector_best_encoder, +}; + +static const struct drm_connector_funcs +nouveau_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .save = NULL, + .restore = NULL, + .detect = nouveau_connector_detect, + .destroy = nouveau_connector_destroy, + .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = nouveau_connector_set_property, + .force = nouveau_connector_force +}; + +static const struct drm_connector_funcs +nouveau_connector_funcs_lvds = { + .dpms = drm_helper_connector_dpms, + .save = NULL, + .restore = NULL, + .detect = nouveau_connector_detect_lvds, + .destroy = nouveau_connector_destroy, + .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = nouveau_connector_set_property, + .force = nouveau_connector_force +}; + +static void +nouveau_connector_dp_dpms(struct drm_connector *connector, int mode) +{ + struct nouveau_encoder *nv_encoder = NULL; + + if (connector->encoder) + nv_encoder = nouveau_encoder(connector->encoder); + if (nv_encoder && nv_encoder->dcb && + nv_encoder->dcb->type == DCB_OUTPUT_DP) { + if (mode == DRM_MODE_DPMS_ON) { + u8 data = DP_SET_POWER_D0; + nv_wraux(nv_encoder->i2c, DP_SET_POWER, &data, 1); + usleep_range(1000, 2000); + } else { + u8 data = DP_SET_POWER_D3; + nv_wraux(nv_encoder->i2c, DP_SET_POWER, &data, 1); + } + } + + drm_helper_connector_dpms(connector, mode); +} + +static const struct drm_connector_funcs +nouveau_connector_funcs_dp = { + .dpms = nouveau_connector_dp_dpms, + .save = NULL, + .restore = NULL, + .detect = nouveau_connector_detect, + .destroy = nouveau_connector_destroy, + .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = nouveau_connector_set_property, + .force = nouveau_connector_force +}; + +static int +nouveau_connector_hotplug(struct nvif_notify *notify) +{ + struct nouveau_connector *nv_connector = + container_of(notify, typeof(*nv_connector), hpd); + struct drm_connector *connector = &nv_connector->base; + struct nouveau_drm *drm = nouveau_drm(connector->dev); + const struct nvif_notify_conn_rep_v0 *rep = notify->data; + const char *name = connector->name; + + if (rep->mask & NVIF_NOTIFY_CONN_V0_IRQ) { + } else { + bool plugged = (rep->mask != NVIF_NOTIFY_CONN_V0_UNPLUG); + + NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", name); + + if (plugged) + drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON); + else + drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); + drm_helper_hpd_irq_event(connector->dev); + } + + return NVIF_NOTIFY_KEEP; +} + +static ssize_t +nouveau_connector_aux_xfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) +{ + struct nouveau_connector *nv_connector = + container_of(aux, typeof(*nv_connector), aux); + struct nouveau_encoder *nv_encoder; + struct nvkm_i2c_port *port; + int ret; + + nv_encoder = find_encoder(&nv_connector->base, DCB_OUTPUT_DP); + if (!nv_encoder || !(port = nv_encoder->i2c)) + return -ENODEV; + if (WARN_ON(msg->size > 16)) + return -E2BIG; + if (msg->size == 0) + return msg->size; + + ret = nvkm_i2c(port)->acquire(port, 0); + if (ret) + return ret; + + ret = port->func->aux(port, false, msg->request, msg->address, + msg->buffer, msg->size); + nvkm_i2c(port)->release(port); + if (ret >= 0) { + msg->reply = ret; + return msg->size; + } + + return ret; +} + +static int +drm_conntype_from_dcb(enum dcb_connector_type dcb) +{ + switch (dcb) { + case DCB_CONNECTOR_VGA : return DRM_MODE_CONNECTOR_VGA; + case DCB_CONNECTOR_TV_0 : + case DCB_CONNECTOR_TV_1 : + case DCB_CONNECTOR_TV_3 : return DRM_MODE_CONNECTOR_TV; + case DCB_CONNECTOR_DMS59_0 : + case DCB_CONNECTOR_DMS59_1 : + case DCB_CONNECTOR_DVI_I : return DRM_MODE_CONNECTOR_DVII; + case DCB_CONNECTOR_DVI_D : return DRM_MODE_CONNECTOR_DVID; + case DCB_CONNECTOR_LVDS : + case DCB_CONNECTOR_LVDS_SPWG: return DRM_MODE_CONNECTOR_LVDS; + case DCB_CONNECTOR_DMS59_DP0: + case DCB_CONNECTOR_DMS59_DP1: + case DCB_CONNECTOR_DP : return DRM_MODE_CONNECTOR_DisplayPort; + case DCB_CONNECTOR_eDP : return DRM_MODE_CONNECTOR_eDP; + case DCB_CONNECTOR_HDMI_0 : + case DCB_CONNECTOR_HDMI_1 : + case DCB_CONNECTOR_HDMI_C : return DRM_MODE_CONNECTOR_HDMIA; + default: + break; + } + + return DRM_MODE_CONNECTOR_Unknown; +} + +struct drm_connector * +nouveau_connector_create(struct drm_device *dev, int index) +{ + const struct drm_connector_funcs *funcs = &nouveau_connector_funcs; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_display *disp = nouveau_display(dev); + struct nouveau_connector *nv_connector = NULL; + struct drm_connector *connector; + int type, ret = 0; + bool dummy; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + nv_connector = nouveau_connector(connector); + if (nv_connector->index == index) + return connector; + } + + nv_connector = kzalloc(sizeof(*nv_connector), GFP_KERNEL); + if (!nv_connector) + return ERR_PTR(-ENOMEM); + + connector = &nv_connector->base; + nv_connector->index = index; + + /* attempt to parse vbios connector type and hotplug gpio */ + nv_connector->dcb = olddcb_conn(dev, index); + if (nv_connector->dcb) { + u32 entry = ROM16(nv_connector->dcb[0]); + if (olddcb_conntab(dev)[3] >= 4) + entry |= (u32)ROM16(nv_connector->dcb[2]) << 16; + + nv_connector->type = nv_connector->dcb[0]; + if (drm_conntype_from_dcb(nv_connector->type) == + DRM_MODE_CONNECTOR_Unknown) { + NV_WARN(drm, "unknown connector type %02x\n", + nv_connector->type); + nv_connector->type = DCB_CONNECTOR_NONE; + } + + /* Gigabyte NX85T */ + if (nv_match_device(dev, 0x0421, 0x1458, 0x344c)) { + if (nv_connector->type == DCB_CONNECTOR_HDMI_1) + nv_connector->type = DCB_CONNECTOR_DVI_I; + } + + /* Gigabyte GV-NX86T512H */ + if (nv_match_device(dev, 0x0402, 0x1458, 0x3455)) { + if (nv_connector->type == DCB_CONNECTOR_HDMI_1) + nv_connector->type = DCB_CONNECTOR_DVI_I; + } + } else { + nv_connector->type = DCB_CONNECTOR_NONE; + } + + /* no vbios data, or an unknown dcb connector type - attempt to + * figure out something suitable ourselves + */ + if (nv_connector->type == DCB_CONNECTOR_NONE) { + struct nouveau_drm *drm = nouveau_drm(dev); + struct dcb_table *dcbt = &drm->vbios.dcb; + u32 encoders = 0; + int i; + + for (i = 0; i < dcbt->entries; i++) { + if (dcbt->entry[i].connector == nv_connector->index) + encoders |= (1 << dcbt->entry[i].type); + } + + if (encoders & (1 << DCB_OUTPUT_DP)) { + if (encoders & (1 << DCB_OUTPUT_TMDS)) + nv_connector->type = DCB_CONNECTOR_DP; + else + nv_connector->type = DCB_CONNECTOR_eDP; + } else + if (encoders & (1 << DCB_OUTPUT_TMDS)) { + if (encoders & (1 << DCB_OUTPUT_ANALOG)) + nv_connector->type = DCB_CONNECTOR_DVI_I; + else + nv_connector->type = DCB_CONNECTOR_DVI_D; + } else + if (encoders & (1 << DCB_OUTPUT_ANALOG)) { + nv_connector->type = DCB_CONNECTOR_VGA; + } else + if (encoders & (1 << DCB_OUTPUT_LVDS)) { + nv_connector->type = DCB_CONNECTOR_LVDS; + } else + if (encoders & (1 << DCB_OUTPUT_TV)) { + nv_connector->type = DCB_CONNECTOR_TV_0; + } + } + + switch ((type = drm_conntype_from_dcb(nv_connector->type))) { + case DRM_MODE_CONNECTOR_LVDS: + ret = nouveau_bios_parse_lvds_table(dev, 0, &dummy, &dummy); + if (ret) { + NV_ERROR(drm, "Error parsing LVDS table, disabling\n"); + kfree(nv_connector); + return ERR_PTR(ret); + } + + funcs = &nouveau_connector_funcs_lvds; + break; + case DRM_MODE_CONNECTOR_DisplayPort: + case DRM_MODE_CONNECTOR_eDP: + nv_connector->aux.dev = dev->dev; + nv_connector->aux.transfer = nouveau_connector_aux_xfer; + ret = drm_dp_aux_register(&nv_connector->aux); + if (ret) { + NV_ERROR(drm, "failed to register aux channel\n"); + kfree(nv_connector); + return ERR_PTR(ret); + } + + funcs = &nouveau_connector_funcs_dp; + break; + default: + funcs = &nouveau_connector_funcs; + break; + } + + /* defaults, will get overridden in detect() */ + connector->interlace_allowed = false; + connector->doublescan_allowed = false; + + drm_connector_init(dev, connector, funcs, type); + drm_connector_helper_add(connector, &nouveau_connector_helper_funcs); + + /* Init DVI-I specific properties */ + if (nv_connector->type == DCB_CONNECTOR_DVI_I) + drm_object_attach_property(&connector->base, dev->mode_config.dvi_i_subconnector_property, 0); + + /* Add overscan compensation options to digital outputs */ + if (disp->underscan_property && + (type == DRM_MODE_CONNECTOR_DVID || + type == DRM_MODE_CONNECTOR_DVII || + type == DRM_MODE_CONNECTOR_HDMIA || + type == DRM_MODE_CONNECTOR_DisplayPort)) { + drm_object_attach_property(&connector->base, + disp->underscan_property, + UNDERSCAN_OFF); + drm_object_attach_property(&connector->base, + disp->underscan_hborder_property, + 0); + drm_object_attach_property(&connector->base, + disp->underscan_vborder_property, + 0); + } + + /* Add hue and saturation options */ + if (disp->vibrant_hue_property) + drm_object_attach_property(&connector->base, + disp->vibrant_hue_property, + 90); + if (disp->color_vibrance_property) + drm_object_attach_property(&connector->base, + disp->color_vibrance_property, + 150); + + /* default scaling mode */ + switch (nv_connector->type) { + case DCB_CONNECTOR_LVDS: + case DCB_CONNECTOR_LVDS_SPWG: + case DCB_CONNECTOR_eDP: + /* see note in nouveau_connector_set_property() */ + if (disp->disp.oclass < NV50_DISP) { + nv_connector->scaling_mode = DRM_MODE_SCALE_FULLSCREEN; + break; + } + nv_connector->scaling_mode = DRM_MODE_SCALE_NONE; + break; + default: + nv_connector->scaling_mode = DRM_MODE_SCALE_NONE; + break; + } + + /* scaling mode property */ + switch (nv_connector->type) { + case DCB_CONNECTOR_TV_0: + case DCB_CONNECTOR_TV_1: + case DCB_CONNECTOR_TV_3: + break; + case DCB_CONNECTOR_VGA: + if (disp->disp.oclass < NV50_DISP) + break; /* can only scale on DFPs */ + /* fall-through */ + default: + drm_object_attach_property(&connector->base, dev->mode_config. + scaling_mode_property, + nv_connector->scaling_mode); + break; + } + + /* dithering properties */ + switch (nv_connector->type) { + case DCB_CONNECTOR_TV_0: + case DCB_CONNECTOR_TV_1: + case DCB_CONNECTOR_TV_3: + case DCB_CONNECTOR_VGA: + break; + default: + if (disp->dithering_mode) { + drm_object_attach_property(&connector->base, + disp->dithering_mode, + nv_connector-> + dithering_mode); + nv_connector->dithering_mode = DITHERING_MODE_AUTO; + } + if (disp->dithering_depth) { + drm_object_attach_property(&connector->base, + disp->dithering_depth, + nv_connector-> + dithering_depth); + nv_connector->dithering_depth = DITHERING_DEPTH_AUTO; + } + break; + } + + ret = nvif_notify_init(&disp->disp, NULL, nouveau_connector_hotplug, + true, NV04_DISP_NTFY_CONN, + &(struct nvif_notify_conn_req_v0) { + .mask = NVIF_NOTIFY_CONN_V0_ANY, + .conn = index, + }, + sizeof(struct nvif_notify_conn_req_v0), + sizeof(struct nvif_notify_conn_rep_v0), + &nv_connector->hpd); + if (ret) + connector->polled = DRM_CONNECTOR_POLL_CONNECT; + else + connector->polled = DRM_CONNECTOR_POLL_HPD; + + drm_connector_register(connector); + return connector; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_connector.h b/kernel/drivers/gpu/drm/nouveau/nouveau_connector.h new file mode 100644 index 000000000..7446ee66e --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_connector.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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. + * + */ + +#ifndef __NOUVEAU_CONNECTOR_H__ +#define __NOUVEAU_CONNECTOR_H__ + +#include + +#include +#include +#include "nouveau_crtc.h" + +struct nvkm_i2c_port; + +enum nouveau_underscan_type { + UNDERSCAN_OFF, + UNDERSCAN_ON, + UNDERSCAN_AUTO, +}; + +/* the enum values specifically defined here match nv50/nvd0 hw values, and + * the code relies on this + */ +enum nouveau_dithering_mode { + DITHERING_MODE_OFF = 0x00, + DITHERING_MODE_ON = 0x01, + DITHERING_MODE_DYNAMIC2X2 = 0x10 | DITHERING_MODE_ON, + DITHERING_MODE_STATIC2X2 = 0x18 | DITHERING_MODE_ON, + DITHERING_MODE_TEMPORAL = 0x20 | DITHERING_MODE_ON, + DITHERING_MODE_AUTO +}; + +enum nouveau_dithering_depth { + DITHERING_DEPTH_6BPC = 0x00, + DITHERING_DEPTH_8BPC = 0x02, + DITHERING_DEPTH_AUTO +}; + +struct nouveau_connector { + struct drm_connector base; + enum dcb_connector_type type; + u8 index; + u8 *dcb; + + struct nvif_notify hpd; + + struct drm_dp_aux aux; + + int dithering_mode; + int dithering_depth; + int scaling_mode; + bool scaling_full; + enum nouveau_underscan_type underscan; + u32 underscan_hborder; + u32 underscan_vborder; + + struct nouveau_encoder *detected_encoder; + struct edid *edid; + struct drm_display_mode *native_mode; +}; + +static inline struct nouveau_connector *nouveau_connector( + struct drm_connector *con) +{ + return container_of(con, struct nouveau_connector, base); +} + +static inline struct nouveau_connector * +nouveau_crtc_connector_get(struct nouveau_crtc *nv_crtc) +{ + struct drm_device *dev = nv_crtc->base.dev; + struct drm_connector *connector; + struct drm_crtc *crtc = to_drm_crtc(nv_crtc); + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (connector->encoder && connector->encoder->crtc == crtc) + return nouveau_connector(connector); + } + + return NULL; +} + +struct drm_connector * +nouveau_connector_create(struct drm_device *, int index); + +extern int nouveau_tv_disable; +extern int nouveau_ignorelid; +extern int nouveau_duallink; + +#endif /* __NOUVEAU_CONNECTOR_H__ */ diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_crtc.h b/kernel/drivers/gpu/drm/nouveau/nouveau_crtc.h new file mode 100644 index 000000000..f19cb1c5f --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_crtc.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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. + * + */ + +#ifndef __NOUVEAU_CRTC_H__ +#define __NOUVEAU_CRTC_H__ + +#include + +struct nouveau_crtc { + struct drm_crtc base; + + int index; + struct nvif_notify vblank; + + uint32_t dpms_saved_fp_control; + uint32_t fp_users; + int saturation; + int color_vibrance; + int vibrant_hue; + int sharpness; + int last_dpms; + + int cursor_saved_x, cursor_saved_y; + + struct { + int cpp; + bool blanked; + uint32_t offset; + uint32_t handle; + } fb; + + struct { + struct nouveau_bo *nvbo; + bool visible; + uint32_t offset; + void (*set_offset)(struct nouveau_crtc *, uint32_t offset); + void (*set_pos)(struct nouveau_crtc *, int x, int y); + void (*hide)(struct nouveau_crtc *, bool update); + void (*show)(struct nouveau_crtc *, bool update); + } cursor; + + struct { + struct nouveau_bo *nvbo; + uint16_t r[256]; + uint16_t g[256]; + uint16_t b[256]; + int depth; + } lut; + + int (*set_dither)(struct nouveau_crtc *crtc, bool update); + int (*set_scale)(struct nouveau_crtc *crtc, bool update); + int (*set_color_vibrance)(struct nouveau_crtc *crtc, bool update); +}; + +static inline struct nouveau_crtc *nouveau_crtc(struct drm_crtc *crtc) +{ + return crtc ? container_of(crtc, struct nouveau_crtc, base) : NULL; +} + +static inline struct drm_crtc *to_drm_crtc(struct nouveau_crtc *crtc) +{ + return &crtc->base; +} + +int nv04_cursor_init(struct nouveau_crtc *); + +#endif /* __NOUVEAU_CRTC_H__ */ diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_debugfs.c b/kernel/drivers/gpu/drm/nouveau/nouveau_debugfs.c new file mode 100644 index 000000000..5392e07ed --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_debugfs.c @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2009 Red Hat + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * 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. + * + */ + +/* + * Authors: + * Ben Skeggs + */ + +#include "nouveau_debugfs.h" +#include "nouveau_drm.h" + +static int +nouveau_debugfs_vbios_image(struct seq_file *m, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct nouveau_drm *drm = nouveau_drm(node->minor->dev); + int i; + + for (i = 0; i < drm->vbios.length; i++) + seq_printf(m, "%c", drm->vbios.data[i]); + return 0; +} + +static struct drm_info_list nouveau_debugfs_list[] = { + { "vbios.rom", nouveau_debugfs_vbios_image, 0, NULL }, +}; +#define NOUVEAU_DEBUGFS_ENTRIES ARRAY_SIZE(nouveau_debugfs_list) + +int +nouveau_debugfs_init(struct drm_minor *minor) +{ + drm_debugfs_create_files(nouveau_debugfs_list, NOUVEAU_DEBUGFS_ENTRIES, + minor->debugfs_root, minor); + return 0; +} + +void +nouveau_debugfs_takedown(struct drm_minor *minor) +{ + drm_debugfs_remove_files(nouveau_debugfs_list, NOUVEAU_DEBUGFS_ENTRIES, + minor); +} diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_debugfs.h b/kernel/drivers/gpu/drm/nouveau/nouveau_debugfs.h new file mode 100644 index 000000000..a62af6fb5 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_debugfs.h @@ -0,0 +1,22 @@ +#ifndef __NOUVEAU_DEBUGFS_H__ +#define __NOUVEAU_DEBUGFS_H__ + +#include + +#if defined(CONFIG_DEBUG_FS) +extern int nouveau_debugfs_init(struct drm_minor *); +extern void nouveau_debugfs_takedown(struct drm_minor *); +#else +static inline int +nouveau_debugfs_init(struct drm_minor *minor) +{ + return 0; +} + +static inline void nouveau_debugfs_takedown(struct drm_minor *minor) +{ +} + +#endif + +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_display.c b/kernel/drivers/gpu/drm/nouveau/nouveau_display.c new file mode 100644 index 000000000..8670d90cd --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_display.c @@ -0,0 +1,910 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 +#include + +#include + +#include "nouveau_fbcon.h" +#include "dispnv04/hw.h" +#include "nouveau_crtc.h" +#include "nouveau_dma.h" +#include "nouveau_gem.h" +#include "nouveau_connector.h" +#include "nv50_display.h" + +#include "nouveau_fence.h" + +#include + +static int +nouveau_display_vblank_handler(struct nvif_notify *notify) +{ + struct nouveau_crtc *nv_crtc = + container_of(notify, typeof(*nv_crtc), vblank); + drm_handle_vblank(nv_crtc->base.dev, nv_crtc->index); + return NVIF_NOTIFY_KEEP; +} + +int +nouveau_display_vblank_enable(struct drm_device *dev, int head) +{ + struct drm_crtc *crtc; + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + if (nv_crtc->index == head) { + nvif_notify_get(&nv_crtc->vblank); + return 0; + } + } + return -EINVAL; +} + +void +nouveau_display_vblank_disable(struct drm_device *dev, int head) +{ + struct drm_crtc *crtc; + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + if (nv_crtc->index == head) { + nvif_notify_put(&nv_crtc->vblank); + return; + } + } +} + +static inline int +calc(int blanks, int blanke, int total, int line) +{ + if (blanke >= blanks) { + if (line >= blanks) + line -= total; + } else { + if (line >= blanks) + line -= total; + line -= blanke + 1; + } + return line; +} + +int +nouveau_display_scanoutpos_head(struct drm_crtc *crtc, int *vpos, int *hpos, + ktime_t *stime, ktime_t *etime) +{ + struct { + struct nv04_disp_mthd_v0 base; + struct nv04_disp_scanoutpos_v0 scan; + } args = { + .base.method = NV04_DISP_SCANOUTPOS, + .base.head = nouveau_crtc(crtc)->index, + }; + struct nouveau_display *disp = nouveau_display(crtc->dev); + int ret, retry = 1; + + do { + ret = nvif_mthd(&disp->disp, 0, &args, sizeof(args)); + if (ret != 0) + return 0; + + if (args.scan.vline) { + ret |= DRM_SCANOUTPOS_ACCURATE; + ret |= DRM_SCANOUTPOS_VALID; + break; + } + + if (retry) ndelay(crtc->linedur_ns); + } while (retry--); + + *hpos = args.scan.hline; + *vpos = calc(args.scan.vblanks, args.scan.vblanke, + args.scan.vtotal, args.scan.vline); + if (stime) *stime = ns_to_ktime(args.scan.time[0]); + if (etime) *etime = ns_to_ktime(args.scan.time[1]); + + if (*vpos < 0) + ret |= DRM_SCANOUTPOS_IN_VBLANK; + return ret; +} + +int +nouveau_display_scanoutpos(struct drm_device *dev, int head, unsigned int flags, + int *vpos, int *hpos, ktime_t *stime, ktime_t *etime) +{ + struct drm_crtc *crtc; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + if (nouveau_crtc(crtc)->index == head) { + return nouveau_display_scanoutpos_head(crtc, vpos, hpos, + stime, etime); + } + } + + return 0; +} + +int +nouveau_display_vblstamp(struct drm_device *dev, int head, int *max_error, + struct timeval *time, unsigned flags) +{ + struct drm_crtc *crtc; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + if (nouveau_crtc(crtc)->index == head) { + return drm_calc_vbltimestamp_from_scanoutpos(dev, + head, max_error, time, flags, crtc, + &crtc->hwmode); + } + } + + return -EINVAL; +} + +static void +nouveau_display_vblank_fini(struct drm_device *dev) +{ + struct drm_crtc *crtc; + + drm_vblank_cleanup(dev); + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + nvif_notify_fini(&nv_crtc->vblank); + } +} + +static int +nouveau_display_vblank_init(struct drm_device *dev) +{ + struct nouveau_display *disp = nouveau_display(dev); + struct drm_crtc *crtc; + int ret; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + ret = nvif_notify_init(&disp->disp, NULL, + nouveau_display_vblank_handler, false, + NV04_DISP_NTFY_VBLANK, + &(struct nvif_notify_head_req_v0) { + .head = nv_crtc->index, + }, + sizeof(struct nvif_notify_head_req_v0), + sizeof(struct nvif_notify_head_rep_v0), + &nv_crtc->vblank); + if (ret) { + nouveau_display_vblank_fini(dev); + return ret; + } + } + + ret = drm_vblank_init(dev, dev->mode_config.num_crtc); + if (ret) { + nouveau_display_vblank_fini(dev); + return ret; + } + + return 0; +} + +static void +nouveau_user_framebuffer_destroy(struct drm_framebuffer *drm_fb) +{ + struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb); + struct nouveau_display *disp = nouveau_display(drm_fb->dev); + + if (disp->fb_dtor) + disp->fb_dtor(drm_fb); + + if (fb->nvbo) + drm_gem_object_unreference_unlocked(&fb->nvbo->gem); + + drm_framebuffer_cleanup(drm_fb); + kfree(fb); +} + +static int +nouveau_user_framebuffer_create_handle(struct drm_framebuffer *drm_fb, + struct drm_file *file_priv, + unsigned int *handle) +{ + struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb); + + return drm_gem_handle_create(file_priv, &fb->nvbo->gem, handle); +} + +static const struct drm_framebuffer_funcs nouveau_framebuffer_funcs = { + .destroy = nouveau_user_framebuffer_destroy, + .create_handle = nouveau_user_framebuffer_create_handle, +}; + +int +nouveau_framebuffer_init(struct drm_device *dev, + struct nouveau_framebuffer *nv_fb, + struct drm_mode_fb_cmd2 *mode_cmd, + struct nouveau_bo *nvbo) +{ + struct nouveau_display *disp = nouveau_display(dev); + struct drm_framebuffer *fb = &nv_fb->base; + int ret; + + drm_helper_mode_fill_fb_struct(fb, mode_cmd); + nv_fb->nvbo = nvbo; + + ret = drm_framebuffer_init(dev, fb, &nouveau_framebuffer_funcs); + if (ret) + return ret; + + if (disp->fb_ctor) { + ret = disp->fb_ctor(fb); + if (ret) + disp->fb_dtor(fb); + } + + return ret; +} + +static struct drm_framebuffer * +nouveau_user_framebuffer_create(struct drm_device *dev, + struct drm_file *file_priv, + struct drm_mode_fb_cmd2 *mode_cmd) +{ + struct nouveau_framebuffer *nouveau_fb; + struct drm_gem_object *gem; + int ret = -ENOMEM; + + gem = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[0]); + if (!gem) + return ERR_PTR(-ENOENT); + + nouveau_fb = kzalloc(sizeof(struct nouveau_framebuffer), GFP_KERNEL); + if (!nouveau_fb) + goto err_unref; + + ret = nouveau_framebuffer_init(dev, nouveau_fb, mode_cmd, nouveau_gem_object(gem)); + if (ret) + goto err; + + return &nouveau_fb->base; + +err: + kfree(nouveau_fb); +err_unref: + drm_gem_object_unreference(gem); + return ERR_PTR(ret); +} + +static const struct drm_mode_config_funcs nouveau_mode_config_funcs = { + .fb_create = nouveau_user_framebuffer_create, + .output_poll_changed = nouveau_fbcon_output_poll_changed, +}; + + +struct nouveau_drm_prop_enum_list { + u8 gen_mask; + int type; + char *name; +}; + +static struct nouveau_drm_prop_enum_list underscan[] = { + { 6, UNDERSCAN_AUTO, "auto" }, + { 6, UNDERSCAN_OFF, "off" }, + { 6, UNDERSCAN_ON, "on" }, + {} +}; + +static struct nouveau_drm_prop_enum_list dither_mode[] = { + { 7, DITHERING_MODE_AUTO, "auto" }, + { 7, DITHERING_MODE_OFF, "off" }, + { 1, DITHERING_MODE_ON, "on" }, + { 6, DITHERING_MODE_STATIC2X2, "static 2x2" }, + { 6, DITHERING_MODE_DYNAMIC2X2, "dynamic 2x2" }, + { 4, DITHERING_MODE_TEMPORAL, "temporal" }, + {} +}; + +static struct nouveau_drm_prop_enum_list dither_depth[] = { + { 6, DITHERING_DEPTH_AUTO, "auto" }, + { 6, DITHERING_DEPTH_6BPC, "6 bpc" }, + { 6, DITHERING_DEPTH_8BPC, "8 bpc" }, + {} +}; + +#define PROP_ENUM(p,gen,n,list) do { \ + struct nouveau_drm_prop_enum_list *l = (list); \ + int c = 0; \ + while (l->gen_mask) { \ + if (l->gen_mask & (1 << (gen))) \ + c++; \ + l++; \ + } \ + if (c) { \ + p = drm_property_create(dev, DRM_MODE_PROP_ENUM, n, c); \ + l = (list); \ + c = 0; \ + while (p && l->gen_mask) { \ + if (l->gen_mask & (1 << (gen))) { \ + drm_property_add_enum(p, c, l->type, l->name); \ + c++; \ + } \ + l++; \ + } \ + } \ +} while(0) + +int +nouveau_display_init(struct drm_device *dev) +{ + struct nouveau_display *disp = nouveau_display(dev); + struct drm_connector *connector; + int ret; + + ret = disp->init(dev); + if (ret) + return ret; + + /* enable polling for external displays */ + drm_kms_helper_poll_enable(dev); + + /* enable hotplug interrupts */ + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + struct nouveau_connector *conn = nouveau_connector(connector); + nvif_notify_get(&conn->hpd); + } + + return ret; +} + +void +nouveau_display_fini(struct drm_device *dev) +{ + struct nouveau_display *disp = nouveau_display(dev); + struct drm_connector *connector; + int head; + + /* Make sure that drm and hw vblank irqs get properly disabled. */ + for (head = 0; head < dev->mode_config.num_crtc; head++) + drm_vblank_off(dev, head); + + /* disable hotplug interrupts */ + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + struct nouveau_connector *conn = nouveau_connector(connector); + nvif_notify_put(&conn->hpd); + } + + drm_kms_helper_poll_disable(dev); + disp->fini(dev); +} + +static void +nouveau_display_create_properties(struct drm_device *dev) +{ + struct nouveau_display *disp = nouveau_display(dev); + int gen; + + if (disp->disp.oclass < NV50_DISP) + gen = 0; + else + if (disp->disp.oclass < GF110_DISP) + gen = 1; + else + gen = 2; + + PROP_ENUM(disp->dithering_mode, gen, "dithering mode", dither_mode); + PROP_ENUM(disp->dithering_depth, gen, "dithering depth", dither_depth); + PROP_ENUM(disp->underscan_property, gen, "underscan", underscan); + + disp->underscan_hborder_property = + drm_property_create_range(dev, 0, "underscan hborder", 0, 128); + + disp->underscan_vborder_property = + drm_property_create_range(dev, 0, "underscan vborder", 0, 128); + + if (gen < 1) + return; + + /* -90..+90 */ + disp->vibrant_hue_property = + drm_property_create_range(dev, 0, "vibrant hue", 0, 180); + + /* -100..+100 */ + disp->color_vibrance_property = + drm_property_create_range(dev, 0, "color vibrance", 0, 200); +} + +int +nouveau_display_create(struct drm_device *dev) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_display *disp; + int ret; + + disp = drm->display = kzalloc(sizeof(*disp), GFP_KERNEL); + if (!disp) + return -ENOMEM; + + drm_mode_config_init(dev); + drm_mode_create_scaling_mode_property(dev); + drm_mode_create_dvi_i_properties(dev); + + dev->mode_config.funcs = &nouveau_mode_config_funcs; + dev->mode_config.fb_base = nv_device_resource_start(nvxx_device(&drm->device), 1); + + dev->mode_config.min_width = 0; + dev->mode_config.min_height = 0; + if (drm->device.info.family < NV_DEVICE_INFO_V0_CELSIUS) { + dev->mode_config.max_width = 2048; + dev->mode_config.max_height = 2048; + } else + if (drm->device.info.family < NV_DEVICE_INFO_V0_TESLA) { + dev->mode_config.max_width = 4096; + dev->mode_config.max_height = 4096; + } else { + dev->mode_config.max_width = 8192; + dev->mode_config.max_height = 8192; + } + + dev->mode_config.preferred_depth = 24; + dev->mode_config.prefer_shadow = 1; + + if (drm->device.info.chipset < 0x11) + dev->mode_config.async_page_flip = false; + else + dev->mode_config.async_page_flip = true; + + drm_kms_helper_poll_init(dev); + drm_kms_helper_poll_disable(dev); + + if (nouveau_modeset != 2 && drm->vbios.dcb.entries) { + static const u16 oclass[] = { + GM204_DISP, + GM107_DISP, + GK110_DISP, + GK104_DISP, + GF110_DISP, + GT214_DISP, + GT206_DISP, + GT200_DISP, + G82_DISP, + NV50_DISP, + NV04_DISP, + }; + int i; + + for (i = 0, ret = -ENODEV; ret && i < ARRAY_SIZE(oclass); i++) { + ret = nvif_object_init(nvif_object(&drm->device), NULL, + NVDRM_DISPLAY, oclass[i], + NULL, 0, &disp->disp); + } + + if (ret == 0) { + nouveau_display_create_properties(dev); + if (disp->disp.oclass < NV50_DISP) + ret = nv04_display_create(dev); + else + ret = nv50_display_create(dev); + } + } else { + ret = 0; + } + + if (ret) + goto disp_create_err; + + if (dev->mode_config.num_crtc) { + ret = nouveau_display_vblank_init(dev); + if (ret) + goto vblank_err; + } + + nouveau_backlight_init(dev); + return 0; + +vblank_err: + disp->dtor(dev); +disp_create_err: + drm_kms_helper_poll_fini(dev); + drm_mode_config_cleanup(dev); + return ret; +} + +void +nouveau_display_destroy(struct drm_device *dev) +{ + struct nouveau_display *disp = nouveau_display(dev); + + nouveau_backlight_exit(dev); + nouveau_display_vblank_fini(dev); + + drm_kms_helper_poll_fini(dev); + drm_mode_config_cleanup(dev); + + if (disp->dtor) + disp->dtor(dev); + + nvif_object_fini(&disp->disp); + + nouveau_drm(dev)->display = NULL; + kfree(disp); +} + +int +nouveau_display_suspend(struct drm_device *dev, bool runtime) +{ + struct drm_crtc *crtc; + + nouveau_display_fini(dev); + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct nouveau_framebuffer *nouveau_fb; + + nouveau_fb = nouveau_framebuffer(crtc->primary->fb); + if (!nouveau_fb || !nouveau_fb->nvbo) + continue; + + nouveau_bo_unpin(nouveau_fb->nvbo); + } + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + if (nv_crtc->cursor.nvbo) { + if (nv_crtc->cursor.set_offset) + nouveau_bo_unmap(nv_crtc->cursor.nvbo); + nouveau_bo_unpin(nv_crtc->cursor.nvbo); + } + } + + return 0; +} + +void +nouveau_display_resume(struct drm_device *dev, bool runtime) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct drm_crtc *crtc; + int ret, head; + + /* re-pin fb/cursors */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct nouveau_framebuffer *nouveau_fb; + + nouveau_fb = nouveau_framebuffer(crtc->primary->fb); + if (!nouveau_fb || !nouveau_fb->nvbo) + continue; + + ret = nouveau_bo_pin(nouveau_fb->nvbo, TTM_PL_FLAG_VRAM, true); + if (ret) + NV_ERROR(drm, "Could not pin framebuffer\n"); + } + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + if (!nv_crtc->cursor.nvbo) + continue; + + ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM, true); + if (!ret && nv_crtc->cursor.set_offset) + ret = nouveau_bo_map(nv_crtc->cursor.nvbo); + if (ret) + NV_ERROR(drm, "Could not pin/map cursor.\n"); + } + + nouveau_display_init(dev); + + /* Force CLUT to get re-loaded during modeset */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + + nv_crtc->lut.depth = 0; + } + + /* Make sure that drm and hw vblank irqs get resumed if needed. */ + for (head = 0; head < dev->mode_config.num_crtc; head++) + drm_vblank_on(dev, head); + + /* This should ensure we don't hit a locking problem when someone + * wakes us up via a connector. We should never go into suspend + * while the display is on anyways. + */ + if (runtime) + return; + + drm_helper_resume_force_mode(dev); + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + + if (!nv_crtc->cursor.nvbo) + continue; + + if (nv_crtc->cursor.set_offset) + nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.nvbo->bo.offset); + nv_crtc->cursor.set_pos(nv_crtc, nv_crtc->cursor_saved_x, + nv_crtc->cursor_saved_y); + } +} + +static int +nouveau_page_flip_emit(struct nouveau_channel *chan, + struct nouveau_bo *old_bo, + struct nouveau_bo *new_bo, + struct nouveau_page_flip_state *s, + struct nouveau_fence **pfence) +{ + struct nouveau_fence_chan *fctx = chan->fence; + struct nouveau_drm *drm = chan->drm; + struct drm_device *dev = drm->dev; + unsigned long flags; + int ret; + + /* Queue it to the pending list */ + spin_lock_irqsave(&dev->event_lock, flags); + list_add_tail(&s->head, &fctx->flip); + spin_unlock_irqrestore(&dev->event_lock, flags); + + /* Synchronize with the old framebuffer */ + ret = nouveau_fence_sync(old_bo, chan, false, false); + if (ret) + goto fail; + + /* Emit the pageflip */ + ret = RING_SPACE(chan, 2); + if (ret) + goto fail; + + if (drm->device.info.family < NV_DEVICE_INFO_V0_FERMI) + BEGIN_NV04(chan, NvSubSw, NV_SW_PAGE_FLIP, 1); + else + BEGIN_NVC0(chan, FermiSw, NV_SW_PAGE_FLIP, 1); + OUT_RING (chan, 0x00000000); + FIRE_RING (chan); + + ret = nouveau_fence_new(chan, false, pfence); + if (ret) + goto fail; + + return 0; +fail: + spin_lock_irqsave(&dev->event_lock, flags); + list_del(&s->head); + spin_unlock_irqrestore(&dev->event_lock, flags); + return ret; +} + +int +nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, + struct drm_pending_vblank_event *event, u32 flags) +{ + const int swap_interval = (flags & DRM_MODE_PAGE_FLIP_ASYNC) ? 0 : 1; + struct drm_device *dev = crtc->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_bo *old_bo = nouveau_framebuffer(crtc->primary->fb)->nvbo; + struct nouveau_bo *new_bo = nouveau_framebuffer(fb)->nvbo; + struct nouveau_page_flip_state *s; + struct nouveau_channel *chan; + struct nouveau_cli *cli; + struct nouveau_fence *fence; + int ret; + + chan = drm->channel; + if (!chan) + return -ENODEV; + cli = (void *)nvif_client(&chan->device->base); + + s = kzalloc(sizeof(*s), GFP_KERNEL); + if (!s) + return -ENOMEM; + + if (new_bo != old_bo) { + ret = nouveau_bo_pin(new_bo, TTM_PL_FLAG_VRAM, true); + if (ret) + goto fail_free; + } + + mutex_lock(&cli->mutex); + ret = ttm_bo_reserve(&new_bo->bo, true, false, false, NULL); + if (ret) + goto fail_unpin; + + /* synchronise rendering channel with the kernel's channel */ + ret = nouveau_fence_sync(new_bo, chan, false, true); + if (ret) { + ttm_bo_unreserve(&new_bo->bo); + goto fail_unpin; + } + + if (new_bo != old_bo) { + ttm_bo_unreserve(&new_bo->bo); + + ret = ttm_bo_reserve(&old_bo->bo, true, false, false, NULL); + if (ret) + goto fail_unpin; + } + + /* Initialize a page flip struct */ + *s = (struct nouveau_page_flip_state) + { { }, event, nouveau_crtc(crtc)->index, + fb->bits_per_pixel, fb->pitches[0], crtc->x, crtc->y, + new_bo->bo.offset }; + + /* Keep vblanks on during flip, for the target crtc of this flip */ + drm_vblank_get(dev, nouveau_crtc(crtc)->index); + + /* Emit a page flip */ + if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA) { + ret = nv50_display_flip_next(crtc, fb, chan, swap_interval); + if (ret) + goto fail_unreserve; + } else { + struct nv04_display *dispnv04 = nv04_display(dev); + int head = nouveau_crtc(crtc)->index; + + if (swap_interval) { + ret = RING_SPACE(chan, 8); + if (ret) + goto fail_unreserve; + + BEGIN_NV04(chan, NvSubImageBlit, 0x012c, 1); + OUT_RING (chan, 0); + BEGIN_NV04(chan, NvSubImageBlit, 0x0134, 1); + OUT_RING (chan, head); + BEGIN_NV04(chan, NvSubImageBlit, 0x0100, 1); + OUT_RING (chan, 0); + BEGIN_NV04(chan, NvSubImageBlit, 0x0130, 1); + OUT_RING (chan, 0); + } + + nouveau_bo_ref(new_bo, &dispnv04->image[head]); + } + + ret = nouveau_page_flip_emit(chan, old_bo, new_bo, s, &fence); + if (ret) + goto fail_unreserve; + mutex_unlock(&cli->mutex); + + /* Update the crtc struct and cleanup */ + crtc->primary->fb = fb; + + nouveau_bo_fence(old_bo, fence, false); + ttm_bo_unreserve(&old_bo->bo); + if (old_bo != new_bo) + nouveau_bo_unpin(old_bo); + nouveau_fence_unref(&fence); + return 0; + +fail_unreserve: + drm_vblank_put(dev, nouveau_crtc(crtc)->index); + ttm_bo_unreserve(&old_bo->bo); +fail_unpin: + mutex_unlock(&cli->mutex); + if (old_bo != new_bo) + nouveau_bo_unpin(new_bo); +fail_free: + kfree(s); + return ret; +} + +int +nouveau_finish_page_flip(struct nouveau_channel *chan, + struct nouveau_page_flip_state *ps) +{ + struct nouveau_fence_chan *fctx = chan->fence; + struct nouveau_drm *drm = chan->drm; + struct drm_device *dev = drm->dev; + struct nouveau_page_flip_state *s; + unsigned long flags; + int crtcid = -1; + + spin_lock_irqsave(&dev->event_lock, flags); + + if (list_empty(&fctx->flip)) { + NV_ERROR(drm, "unexpected pageflip\n"); + spin_unlock_irqrestore(&dev->event_lock, flags); + return -EINVAL; + } + + s = list_first_entry(&fctx->flip, struct nouveau_page_flip_state, head); + if (s->event) { + /* Vblank timestamps/counts are only correct on >= NV-50 */ + if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA) + crtcid = s->crtc; + + drm_send_vblank_event(dev, crtcid, s->event); + } + + /* Give up ownership of vblank for page-flipped crtc */ + drm_vblank_put(dev, s->crtc); + + list_del(&s->head); + if (ps) + *ps = *s; + kfree(s); + + spin_unlock_irqrestore(&dev->event_lock, flags); + return 0; +} + +int +nouveau_flip_complete(void *data) +{ + struct nouveau_channel *chan = data; + struct nouveau_drm *drm = chan->drm; + struct nouveau_page_flip_state state; + + if (!nouveau_finish_page_flip(chan, &state)) { + if (drm->device.info.family < NV_DEVICE_INFO_V0_TESLA) { + nv_set_crtc_base(drm->dev, state.crtc, state.offset + + state.y * state.pitch + + state.x * state.bpp / 8); + } + } + + return 0; +} + +int +nouveau_display_dumb_create(struct drm_file *file_priv, struct drm_device *dev, + struct drm_mode_create_dumb *args) +{ + struct nouveau_bo *bo; + uint32_t domain; + int ret; + + args->pitch = roundup(args->width * (args->bpp / 8), 256); + args->size = args->pitch * args->height; + args->size = roundup(args->size, PAGE_SIZE); + + /* Use VRAM if there is any ; otherwise fallback to system memory */ + if (nouveau_drm(dev)->device.info.ram_size != 0) + domain = NOUVEAU_GEM_DOMAIN_VRAM; + else + domain = NOUVEAU_GEM_DOMAIN_GART; + + ret = nouveau_gem_new(dev, args->size, 0, domain, 0, 0, &bo); + if (ret) + return ret; + + ret = drm_gem_handle_create(file_priv, &bo->gem, &args->handle); + drm_gem_object_unreference_unlocked(&bo->gem); + return ret; +} + +int +nouveau_display_dumb_map_offset(struct drm_file *file_priv, + struct drm_device *dev, + uint32_t handle, uint64_t *poffset) +{ + struct drm_gem_object *gem; + + gem = drm_gem_object_lookup(dev, file_priv, handle); + if (gem) { + struct nouveau_bo *bo = nouveau_gem_object(gem); + *poffset = drm_vma_node_offset_addr(&bo->bo.vma_node); + drm_gem_object_unreference_unlocked(gem); + return 0; + } + + return -ENOENT; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_display.h b/kernel/drivers/gpu/drm/nouveau/nouveau_display.h new file mode 100644 index 000000000..a6213e242 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_display.h @@ -0,0 +1,104 @@ +#ifndef __NOUVEAU_DISPLAY_H__ +#define __NOUVEAU_DISPLAY_H__ + +#include + +#include "nouveau_drm.h" + +struct nouveau_framebuffer { + struct drm_framebuffer base; + struct nouveau_bo *nvbo; + struct nvkm_vma vma; + u32 r_handle; + u32 r_format; + u32 r_pitch; + struct nvif_object h_base[4]; + struct nvif_object h_core; +}; + +static inline struct nouveau_framebuffer * +nouveau_framebuffer(struct drm_framebuffer *fb) +{ + return container_of(fb, struct nouveau_framebuffer, base); +} + +int nouveau_framebuffer_init(struct drm_device *, struct nouveau_framebuffer *, + struct drm_mode_fb_cmd2 *, struct nouveau_bo *); + +struct nouveau_page_flip_state { + struct list_head head; + struct drm_pending_vblank_event *event; + int crtc, bpp, pitch, x, y; + u64 offset; +}; + +struct nouveau_display { + void *priv; + void (*dtor)(struct drm_device *); + int (*init)(struct drm_device *); + void (*fini)(struct drm_device *); + + int (*fb_ctor)(struct drm_framebuffer *); + void (*fb_dtor)(struct drm_framebuffer *); + + struct nvif_object disp; + + struct drm_property *dithering_mode; + struct drm_property *dithering_depth; + struct drm_property *underscan_property; + struct drm_property *underscan_hborder_property; + struct drm_property *underscan_vborder_property; + /* not really hue and saturation: */ + struct drm_property *vibrant_hue_property; + struct drm_property *color_vibrance_property; +}; + +static inline struct nouveau_display * +nouveau_display(struct drm_device *dev) +{ + return nouveau_drm(dev)->display; +} + +int nouveau_display_create(struct drm_device *dev); +void nouveau_display_destroy(struct drm_device *dev); +int nouveau_display_init(struct drm_device *dev); +void nouveau_display_fini(struct drm_device *dev); +int nouveau_display_suspend(struct drm_device *dev, bool runtime); +void nouveau_display_resume(struct drm_device *dev, bool runtime); +int nouveau_display_vblank_enable(struct drm_device *, int); +void nouveau_display_vblank_disable(struct drm_device *, int); +int nouveau_display_scanoutpos(struct drm_device *, int, unsigned int, + int *, int *, ktime_t *, ktime_t *); +int nouveau_display_vblstamp(struct drm_device *, int, int *, + struct timeval *, unsigned); + +int nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, + struct drm_pending_vblank_event *event, + uint32_t page_flip_flags); +int nouveau_finish_page_flip(struct nouveau_channel *, + struct nouveau_page_flip_state *); + +int nouveau_display_dumb_create(struct drm_file *, struct drm_device *, + struct drm_mode_create_dumb *args); +int nouveau_display_dumb_map_offset(struct drm_file *, struct drm_device *, + u32 handle, u64 *offset); + +void nouveau_hdmi_mode_set(struct drm_encoder *, struct drm_display_mode *); + +int nouveau_crtc_set_config(struct drm_mode_set *set); +#ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT +extern int nouveau_backlight_init(struct drm_device *); +extern void nouveau_backlight_exit(struct drm_device *); +#else +static inline int +nouveau_backlight_init(struct drm_device *dev) +{ + return 0; +} + +static inline void +nouveau_backlight_exit(struct drm_device *dev) { +} +#endif + +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_dma.c b/kernel/drivers/gpu/drm/nouveau/nouveau_dma.c new file mode 100644 index 000000000..6d9245aa8 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_dma.c @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2007 Ben Skeggs. + * 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 "nouveau_drm.h" +#include "nouveau_dma.h" + +void +OUT_RINGp(struct nouveau_channel *chan, const void *data, unsigned nr_dwords) +{ + bool is_iomem; + u32 *mem = ttm_kmap_obj_virtual(&chan->push.buffer->kmap, &is_iomem); + mem = &mem[chan->dma.cur]; + if (is_iomem) + memcpy_toio((void __force __iomem *)mem, data, nr_dwords * 4); + else + memcpy(mem, data, nr_dwords * 4); + chan->dma.cur += nr_dwords; +} + +/* Fetch and adjust GPU GET pointer + * + * Returns: + * value >= 0, the adjusted GET pointer + * -EINVAL if GET pointer currently outside main push buffer + * -EBUSY if timeout exceeded + */ +static inline int +READ_GET(struct nouveau_channel *chan, uint64_t *prev_get, int *timeout) +{ + uint64_t val; + + val = nvif_rd32(chan, chan->user_get); + if (chan->user_get_hi) + val |= (uint64_t)nvif_rd32(chan, chan->user_get_hi) << 32; + + /* reset counter as long as GET is still advancing, this is + * to avoid misdetecting a GPU lockup if the GPU happens to + * just be processing an operation that takes a long time + */ + if (val != *prev_get) { + *prev_get = val; + *timeout = 0; + } + + if ((++*timeout & 0xff) == 0) { + udelay(1); + if (*timeout > 100000) + return -EBUSY; + } + + if (val < chan->push.vma.offset || + val > chan->push.vma.offset + (chan->dma.max << 2)) + return -EINVAL; + + return (val - chan->push.vma.offset) >> 2; +} + +void +nv50_dma_push(struct nouveau_channel *chan, struct nouveau_bo *bo, + int delta, int length) +{ + struct nouveau_cli *cli = (void *)nvif_client(&chan->device->base); + struct nouveau_bo *pb = chan->push.buffer; + struct nvkm_vma *vma; + int ip = (chan->dma.ib_put * 2) + chan->dma.ib_base; + u64 offset; + + vma = nouveau_bo_vma_find(bo, cli->vm); + BUG_ON(!vma); + offset = vma->offset + delta; + + BUG_ON(chan->dma.ib_free < 1); + + nouveau_bo_wr32(pb, ip++, lower_32_bits(offset)); + nouveau_bo_wr32(pb, ip++, upper_32_bits(offset) | length << 8); + + chan->dma.ib_put = (chan->dma.ib_put + 1) & chan->dma.ib_max; + + mb(); + /* Flush writes. */ + nouveau_bo_rd32(pb, 0); + + nvif_wr32(chan, 0x8c, chan->dma.ib_put); + chan->dma.ib_free--; +} + +static int +nv50_dma_push_wait(struct nouveau_channel *chan, int count) +{ + uint32_t cnt = 0, prev_get = 0; + + while (chan->dma.ib_free < count) { + uint32_t get = nvif_rd32(chan, 0x88); + if (get != prev_get) { + prev_get = get; + cnt = 0; + } + + if ((++cnt & 0xff) == 0) { + DRM_UDELAY(1); + if (cnt > 100000) + return -EBUSY; + } + + chan->dma.ib_free = get - chan->dma.ib_put; + if (chan->dma.ib_free <= 0) + chan->dma.ib_free += chan->dma.ib_max; + } + + return 0; +} + +static int +nv50_dma_wait(struct nouveau_channel *chan, int slots, int count) +{ + uint64_t prev_get = 0; + int ret, cnt = 0; + + ret = nv50_dma_push_wait(chan, slots + 1); + if (unlikely(ret)) + return ret; + + while (chan->dma.free < count) { + int get = READ_GET(chan, &prev_get, &cnt); + if (unlikely(get < 0)) { + if (get == -EINVAL) + continue; + + return get; + } + + if (get <= chan->dma.cur) { + chan->dma.free = chan->dma.max - chan->dma.cur; + if (chan->dma.free >= count) + break; + + FIRE_RING(chan); + do { + get = READ_GET(chan, &prev_get, &cnt); + if (unlikely(get < 0)) { + if (get == -EINVAL) + continue; + return get; + } + } while (get == 0); + chan->dma.cur = 0; + chan->dma.put = 0; + } + + chan->dma.free = get - chan->dma.cur - 1; + } + + return 0; +} + +int +nouveau_dma_wait(struct nouveau_channel *chan, int slots, int size) +{ + uint64_t prev_get = 0; + int cnt = 0, get; + + if (chan->dma.ib_max) + return nv50_dma_wait(chan, slots, size); + + while (chan->dma.free < size) { + get = READ_GET(chan, &prev_get, &cnt); + if (unlikely(get == -EBUSY)) + return -EBUSY; + + /* loop until we have a usable GET pointer. the value + * we read from the GPU may be outside the main ring if + * PFIFO is processing a buffer called from the main ring, + * discard these values until something sensible is seen. + * + * the other case we discard GET is while the GPU is fetching + * from the SKIPS area, so the code below doesn't have to deal + * with some fun corner cases. + */ + if (unlikely(get == -EINVAL) || get < NOUVEAU_DMA_SKIPS) + continue; + + if (get <= chan->dma.cur) { + /* engine is fetching behind us, or is completely + * idle (GET == PUT) so we have free space up until + * the end of the push buffer + * + * we can only hit that path once per call due to + * looping back to the beginning of the push buffer, + * we'll hit the fetching-ahead-of-us path from that + * point on. + * + * the *one* exception to that rule is if we read + * GET==PUT, in which case the below conditional will + * always succeed and break us out of the wait loop. + */ + chan->dma.free = chan->dma.max - chan->dma.cur; + if (chan->dma.free >= size) + break; + + /* not enough space left at the end of the push buffer, + * instruct the GPU to jump back to the start right + * after processing the currently pending commands. + */ + OUT_RING(chan, chan->push.vma.offset | 0x20000000); + + /* wait for GET to depart from the skips area. + * prevents writing GET==PUT and causing a race + * condition that causes us to think the GPU is + * idle when it's not. + */ + do { + get = READ_GET(chan, &prev_get, &cnt); + if (unlikely(get == -EBUSY)) + return -EBUSY; + if (unlikely(get == -EINVAL)) + continue; + } while (get <= NOUVEAU_DMA_SKIPS); + WRITE_PUT(NOUVEAU_DMA_SKIPS); + + /* we're now submitting commands at the start of + * the push buffer. + */ + chan->dma.cur = + chan->dma.put = NOUVEAU_DMA_SKIPS; + } + + /* engine fetching ahead of us, we have space up until the + * current GET pointer. the "- 1" is to ensure there's + * space left to emit a jump back to the beginning of the + * push buffer if we require it. we can never get GET == PUT + * here, so this is safe. + */ + chan->dma.free = get - chan->dma.cur - 1; + } + + return 0; +} + diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_dma.h b/kernel/drivers/gpu/drm/nouveau/nouveau_dma.h new file mode 100644 index 000000000..8da0a272c --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_dma.h @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2007 Ben Skeggs. + * 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. + * + */ + +#ifndef __NOUVEAU_DMA_H__ +#define __NOUVEAU_DMA_H__ + +#include "nouveau_bo.h" +#include "nouveau_chan.h" + +int nouveau_dma_wait(struct nouveau_channel *, int slots, int size); +void nv50_dma_push(struct nouveau_channel *, struct nouveau_bo *, + int delta, int length); + +/* + * There's a hw race condition where you can't jump to your PUT offset, + * to avoid this we jump to offset + SKIPS and fill the difference with + * NOPs. + * + * xf86-video-nv configures the DMA fetch size to 32 bytes, and uses + * a SKIPS value of 8. Lets assume that the race condition is to do + * with writing into the fetch area, we configure a fetch size of 128 + * bytes so we need a larger SKIPS value. + */ +#define NOUVEAU_DMA_SKIPS (128 / 4) + +/* Hardcoded object assignments to subchannels (subchannel id). */ +enum { + NvSubCtxSurf2D = 0, + NvSubSw = 1, + NvSubImageBlit = 2, + NvSubGdiRect = 3, + + NvSub2D = 3, /* DO NOT CHANGE - hardcoded for kepler gr fifo */ + NvSubCopy = 4, /* DO NOT CHANGE - hardcoded for kepler gr fifo */ + FermiSw = 5, /* DO NOT CHANGE (well.. 6/7 will work...) */ +}; + +/* Object handles - for stuff that's doesn't use handle == oclass. */ +enum { + NvDmaFB = 0x80000002, + NvDmaTT = 0x80000003, + NvNotify0 = 0x80000006, + NvSema = 0x8000000f, + NvEvoSema0 = 0x80000010, + NvEvoSema1 = 0x80000011, +}; + +#define NV_MEMORY_TO_MEMORY_FORMAT 0x00000039 +#define NV_MEMORY_TO_MEMORY_FORMAT_NAME 0x00000000 +#define NV_MEMORY_TO_MEMORY_FORMAT_SET_REF 0x00000050 +#define NV_MEMORY_TO_MEMORY_FORMAT_NOP 0x00000100 +#define NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY 0x00000104 +#define NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY_STYLE_WRITE 0x00000000 +#define NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY_STYLE_WRITE_LE_AWAKEN 0x00000001 +#define NV_MEMORY_TO_MEMORY_FORMAT_DMA_NOTIFY 0x00000180 +#define NV_MEMORY_TO_MEMORY_FORMAT_DMA_SOURCE 0x00000184 +#define NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN 0x0000030c + +#define NV50_MEMORY_TO_MEMORY_FORMAT 0x00005039 +#define NV50_MEMORY_TO_MEMORY_FORMAT_UNK200 0x00000200 +#define NV50_MEMORY_TO_MEMORY_FORMAT_UNK21C 0x0000021c +#define NV50_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN_HIGH 0x00000238 +#define NV50_MEMORY_TO_MEMORY_FORMAT_OFFSET_OUT_HIGH 0x0000023c + +static __must_check inline int +RING_SPACE(struct nouveau_channel *chan, int size) +{ + int ret; + + ret = nouveau_dma_wait(chan, 1, size); + if (ret) + return ret; + + chan->dma.free -= size; + return 0; +} + +static inline void +OUT_RING(struct nouveau_channel *chan, int data) +{ + nouveau_bo_wr32(chan->push.buffer, chan->dma.cur++, data); +} + +extern void +OUT_RINGp(struct nouveau_channel *chan, const void *data, unsigned nr_dwords); + +static inline void +BEGIN_NV04(struct nouveau_channel *chan, int subc, int mthd, int size) +{ + OUT_RING(chan, 0x00000000 | (subc << 13) | (size << 18) | mthd); +} + +static inline void +BEGIN_NI04(struct nouveau_channel *chan, int subc, int mthd, int size) +{ + OUT_RING(chan, 0x40000000 | (subc << 13) | (size << 18) | mthd); +} + +static inline void +BEGIN_NVC0(struct nouveau_channel *chan, int subc, int mthd, int size) +{ + OUT_RING(chan, 0x20000000 | (size << 16) | (subc << 13) | (mthd >> 2)); +} + +static inline void +BEGIN_NIC0(struct nouveau_channel *chan, int subc, int mthd, int size) +{ + OUT_RING(chan, 0x60000000 | (size << 16) | (subc << 13) | (mthd >> 2)); +} + +static inline void +BEGIN_IMC0(struct nouveau_channel *chan, int subc, int mthd, u16 data) +{ + OUT_RING(chan, 0x80000000 | (data << 16) | (subc << 13) | (mthd >> 2)); +} + +#define WRITE_PUT(val) do { \ + mb(); \ + nouveau_bo_rd32(chan->push.buffer, 0); \ + nvif_wr32(chan, chan->user_put, ((val) << 2) + chan->push.vma.offset); \ +} while (0) + +static inline void +FIRE_RING(struct nouveau_channel *chan) +{ + if (chan->dma.cur == chan->dma.put) + return; + chan->accel_done = true; + + if (chan->dma.ib_max) { + nv50_dma_push(chan, chan->push.buffer, chan->dma.put << 2, + (chan->dma.cur - chan->dma.put) << 2); + } else { + WRITE_PUT(chan->dma.cur); + } + + chan->dma.put = chan->dma.cur; +} + +static inline void +WIND_RING(struct nouveau_channel *chan) +{ + chan->dma.cur = chan->dma.put; +} + +/* FIFO methods */ +#define NV01_SUBCHAN_OBJECT 0x00000000 +#define NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH 0x00000010 +#define NV84_SUBCHAN_SEMAPHORE_ADDRESS_LOW 0x00000014 +#define NV84_SUBCHAN_SEMAPHORE_SEQUENCE 0x00000018 +#define NV84_SUBCHAN_SEMAPHORE_TRIGGER 0x0000001c +#define NV84_SUBCHAN_SEMAPHORE_TRIGGER_ACQUIRE_EQUAL 0x00000001 +#define NV84_SUBCHAN_SEMAPHORE_TRIGGER_WRITE_LONG 0x00000002 +#define NV84_SUBCHAN_SEMAPHORE_TRIGGER_ACQUIRE_GEQUAL 0x00000004 +#define NVC0_SUBCHAN_SEMAPHORE_TRIGGER_YIELD 0x00001000 +#define NV84_SUBCHAN_UEVENT 0x00000020 +#define NV84_SUBCHAN_WRCACHE_FLUSH 0x00000024 +#define NV10_SUBCHAN_REF_CNT 0x00000050 +#define NV11_SUBCHAN_DMA_SEMAPHORE 0x00000060 +#define NV11_SUBCHAN_SEMAPHORE_OFFSET 0x00000064 +#define NV11_SUBCHAN_SEMAPHORE_ACQUIRE 0x00000068 +#define NV11_SUBCHAN_SEMAPHORE_RELEASE 0x0000006c +#define NV40_SUBCHAN_YIELD 0x00000080 + +/* NV_SW object class */ +#define NV_SW_DMA_VBLSEM 0x0000018c +#define NV_SW_VBLSEM_OFFSET 0x00000400 +#define NV_SW_VBLSEM_RELEASE_VALUE 0x00000404 +#define NV_SW_VBLSEM_RELEASE 0x00000408 +#define NV_SW_PAGE_FLIP 0x00000500 + +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_dp.c b/kernel/drivers/gpu/drm/nouveau/nouveau_dp.c new file mode 100644 index 000000000..c3ef30b3a --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -0,0 +1,89 @@ +/* + * 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 + +#include "nouveau_drm.h" +#include "nouveau_connector.h" +#include "nouveau_encoder.h" +#include "nouveau_crtc.h" + +static void +nouveau_dp_probe_oui(struct drm_device *dev, struct nvkm_i2c_port *auxch, + u8 *dpcd) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + u8 buf[3]; + + if (!(dpcd[DP_DOWN_STREAM_PORT_COUNT] & DP_OUI_SUPPORT)) + return; + + if (!nv_rdaux(auxch, DP_SINK_OUI, buf, 3)) + NV_DEBUG(drm, "Sink OUI: %02hx%02hx%02hx\n", + buf[0], buf[1], buf[2]); + + if (!nv_rdaux(auxch, DP_BRANCH_OUI, buf, 3)) + NV_DEBUG(drm, "Branch OUI: %02hx%02hx%02hx\n", + buf[0], buf[1], buf[2]); + +} + +int +nouveau_dp_detect(struct nouveau_encoder *nv_encoder) +{ + struct drm_device *dev = nv_encoder->base.base.dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_i2c_port *auxch; + u8 *dpcd = nv_encoder->dp.dpcd; + int ret; + + auxch = nv_encoder->i2c; + if (!auxch) + return -ENODEV; + + ret = nv_rdaux(auxch, DP_DPCD_REV, dpcd, 8); + if (ret) + return ret; + + nv_encoder->dp.link_bw = 27000 * dpcd[1]; + nv_encoder->dp.link_nr = dpcd[2] & DP_MAX_LANE_COUNT_MASK; + + NV_DEBUG(drm, "display: %dx%d dpcd 0x%02x\n", + nv_encoder->dp.link_nr, nv_encoder->dp.link_bw, dpcd[0]); + NV_DEBUG(drm, "encoder: %dx%d\n", + nv_encoder->dcb->dpconf.link_nr, + nv_encoder->dcb->dpconf.link_bw); + + if (nv_encoder->dcb->dpconf.link_nr < nv_encoder->dp.link_nr) + nv_encoder->dp.link_nr = nv_encoder->dcb->dpconf.link_nr; + if (nv_encoder->dcb->dpconf.link_bw < nv_encoder->dp.link_bw) + nv_encoder->dp.link_bw = nv_encoder->dcb->dpconf.link_bw; + + NV_DEBUG(drm, "maximum: %dx%d\n", + nv_encoder->dp.link_nr, nv_encoder->dp.link_bw); + + nouveau_dp_probe_oui(dev, auxch, dpcd); + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_drm.c b/kernel/drivers/gpu/drm/nouveau/nouveau_drm.c new file mode 100644 index 000000000..89049335b --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -0,0 +1,1136 @@ +/* + * 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 "drmP.h" +#include "drm_crtc_helper.h" + +#include +#include +#include + +#include "nouveau_drm.h" +#include "nouveau_dma.h" +#include "nouveau_ttm.h" +#include "nouveau_gem.h" +#include "nouveau_agp.h" +#include "nouveau_vga.h" +#include "nouveau_sysfs.h" +#include "nouveau_hwmon.h" +#include "nouveau_acpi.h" +#include "nouveau_bios.h" +#include "nouveau_ioctl.h" +#include "nouveau_abi16.h" +#include "nouveau_fbcon.h" +#include "nouveau_fence.h" +#include "nouveau_debugfs.h" +#include "nouveau_usif.h" +#include "nouveau_connector.h" +#include "nouveau_platform.h" + +MODULE_PARM_DESC(config, "option string to pass to driver core"); +static char *nouveau_config; +module_param_named(config, nouveau_config, charp, 0400); + +MODULE_PARM_DESC(debug, "debug string to pass to driver core"); +static char *nouveau_debug; +module_param_named(debug, nouveau_debug, charp, 0400); + +MODULE_PARM_DESC(noaccel, "disable kernel/abi16 acceleration"); +static int nouveau_noaccel = 0; +module_param_named(noaccel, nouveau_noaccel, int, 0400); + +MODULE_PARM_DESC(modeset, "enable driver (default: auto, " + "0 = disabled, 1 = enabled, 2 = headless)"); +int nouveau_modeset = -1; +module_param_named(modeset, nouveau_modeset, int, 0400); + +MODULE_PARM_DESC(runpm, "disable (0), force enable (1), optimus only default (-1)"); +int nouveau_runtime_pm = -1; +module_param_named(runpm, nouveau_runtime_pm, int, 0400); + +static struct drm_driver driver_stub; +static struct drm_driver driver_pci; +static struct drm_driver driver_platform; + +static u64 +nouveau_pci_name(struct pci_dev *pdev) +{ + u64 name = (u64)pci_domain_nr(pdev->bus) << 32; + name |= pdev->bus->number << 16; + name |= PCI_SLOT(pdev->devfn) << 8; + return name | PCI_FUNC(pdev->devfn); +} + +static u64 +nouveau_platform_name(struct platform_device *platformdev) +{ + return platformdev->id; +} + +static u64 +nouveau_name(struct drm_device *dev) +{ + if (dev->pdev) + return nouveau_pci_name(dev->pdev); + else + return nouveau_platform_name(dev->platformdev); +} + +static int +nouveau_cli_create(u64 name, const char *sname, + int size, void **pcli) +{ + struct nouveau_cli *cli = *pcli = kzalloc(size, GFP_KERNEL); + if (cli) { + int ret = nvif_client_init(NULL, NULL, sname, name, + nouveau_config, nouveau_debug, + &cli->base); + if (ret == 0) { + mutex_init(&cli->mutex); + usif_client_init(cli); + } + return ret; + } + return -ENOMEM; +} + +static void +nouveau_cli_destroy(struct nouveau_cli *cli) +{ + nvkm_vm_ref(NULL, &nvxx_client(&cli->base)->vm, NULL); + nvif_client_fini(&cli->base); + usif_client_fini(cli); +} + +static void +nouveau_accel_fini(struct nouveau_drm *drm) +{ + nouveau_channel_del(&drm->channel); + nvif_object_fini(&drm->ntfy); + nvkm_gpuobj_ref(NULL, &drm->notify); + nvif_object_fini(&drm->nvsw); + nouveau_channel_del(&drm->cechan); + nvif_object_fini(&drm->ttm.copy); + if (drm->fence) + nouveau_fence(drm)->dtor(drm); +} + +static void +nouveau_accel_init(struct nouveau_drm *drm) +{ + struct nvif_device *device = &drm->device; + u32 arg0, arg1; + u32 sclass[16]; + int ret, i; + + if (nouveau_noaccel) + return; + + /* initialise synchronisation routines */ + /*XXX: this is crap, but the fence/channel stuff is a little + * backwards in some places. this will be fixed. + */ + ret = nvif_object_sclass(&device->base, sclass, ARRAY_SIZE(sclass)); + if (ret < 0) + return; + + for (ret = -ENOSYS, i = 0; ret && i < ARRAY_SIZE(sclass); i++) { + switch (sclass[i]) { + case NV03_CHANNEL_DMA: + ret = nv04_fence_create(drm); + break; + case NV10_CHANNEL_DMA: + ret = nv10_fence_create(drm); + break; + case NV17_CHANNEL_DMA: + case NV40_CHANNEL_DMA: + ret = nv17_fence_create(drm); + break; + case NV50_CHANNEL_GPFIFO: + ret = nv50_fence_create(drm); + break; + case G82_CHANNEL_GPFIFO: + ret = nv84_fence_create(drm); + break; + case FERMI_CHANNEL_GPFIFO: + case KEPLER_CHANNEL_GPFIFO_A: + case MAXWELL_CHANNEL_GPFIFO_A: + ret = nvc0_fence_create(drm); + break; + default: + break; + } + } + + if (ret) { + NV_ERROR(drm, "failed to initialise sync subsystem, %d\n", ret); + nouveau_accel_fini(drm); + return; + } + + if (device->info.family >= NV_DEVICE_INFO_V0_KEPLER) { + ret = nouveau_channel_new(drm, &drm->device, NVDRM_CHAN + 1, + KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_CE0| + KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_CE1, + 0, &drm->cechan); + if (ret) + NV_ERROR(drm, "failed to create ce channel, %d\n", ret); + + arg0 = KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_GR; + arg1 = 1; + } else + if (device->info.chipset >= 0xa3 && + device->info.chipset != 0xaa && + device->info.chipset != 0xac) { + ret = nouveau_channel_new(drm, &drm->device, NVDRM_CHAN + 1, + NvDmaFB, NvDmaTT, &drm->cechan); + if (ret) + NV_ERROR(drm, "failed to create ce channel, %d\n", ret); + + arg0 = NvDmaFB; + arg1 = NvDmaTT; + } else { + arg0 = NvDmaFB; + arg1 = NvDmaTT; + } + + ret = nouveau_channel_new(drm, &drm->device, NVDRM_CHAN, arg0, arg1, + &drm->channel); + if (ret) { + NV_ERROR(drm, "failed to create kernel channel, %d\n", ret); + nouveau_accel_fini(drm); + return; + } + + ret = nvif_object_init(drm->channel->object, NULL, NVDRM_NVSW, + nouveau_abi16_swclass(drm), NULL, 0, &drm->nvsw); + if (ret == 0) { + struct nvkm_sw_chan *swch; + ret = RING_SPACE(drm->channel, 2); + if (ret == 0) { + if (device->info.family < NV_DEVICE_INFO_V0_FERMI) { + BEGIN_NV04(drm->channel, NvSubSw, 0, 1); + OUT_RING (drm->channel, NVDRM_NVSW); + } else + if (device->info.family < NV_DEVICE_INFO_V0_KEPLER) { + BEGIN_NVC0(drm->channel, FermiSw, 0, 1); + OUT_RING (drm->channel, 0x001f0000); + } + } + swch = (void *)nvxx_object(&drm->nvsw)->parent; + swch->flip = nouveau_flip_complete; + swch->flip_data = drm->channel; + } + + if (ret) { + NV_ERROR(drm, "failed to allocate software object, %d\n", ret); + nouveau_accel_fini(drm); + return; + } + + if (device->info.family < NV_DEVICE_INFO_V0_FERMI) { + ret = nvkm_gpuobj_new(nvxx_object(&drm->device), NULL, 32, + 0, 0, &drm->notify); + if (ret) { + NV_ERROR(drm, "failed to allocate notifier, %d\n", ret); + nouveau_accel_fini(drm); + return; + } + + ret = nvif_object_init(drm->channel->object, NULL, NvNotify0, + NV_DMA_IN_MEMORY, + &(struct nv_dma_v0) { + .target = NV_DMA_V0_TARGET_VRAM, + .access = NV_DMA_V0_ACCESS_RDWR, + .start = drm->notify->addr, + .limit = drm->notify->addr + 31 + }, sizeof(struct nv_dma_v0), + &drm->ntfy); + if (ret) { + nouveau_accel_fini(drm); + return; + } + } + + + nouveau_bo_move_init(drm); +} + +static int nouveau_drm_probe(struct pci_dev *pdev, + const struct pci_device_id *pent) +{ + struct nvkm_device *device; + struct apertures_struct *aper; + bool boot = false; + int ret; + + /* remove conflicting drivers (vesafb, efifb etc) */ + aper = alloc_apertures(3); + if (!aper) + return -ENOMEM; + + aper->ranges[0].base = pci_resource_start(pdev, 1); + aper->ranges[0].size = pci_resource_len(pdev, 1); + aper->count = 1; + + if (pci_resource_len(pdev, 2)) { + aper->ranges[aper->count].base = pci_resource_start(pdev, 2); + aper->ranges[aper->count].size = pci_resource_len(pdev, 2); + aper->count++; + } + + if (pci_resource_len(pdev, 3)) { + aper->ranges[aper->count].base = pci_resource_start(pdev, 3); + aper->ranges[aper->count].size = pci_resource_len(pdev, 3); + aper->count++; + } + +#ifdef CONFIG_X86 + boot = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; +#endif + if (nouveau_modeset != 2) + remove_conflicting_framebuffers(aper, "nouveaufb", boot); + kfree(aper); + + ret = nvkm_device_create(pdev, NVKM_BUS_PCI, + nouveau_pci_name(pdev), pci_name(pdev), + nouveau_config, nouveau_debug, &device); + if (ret) + return ret; + + pci_set_master(pdev); + + ret = drm_get_pci_dev(pdev, pent, &driver_pci); + if (ret) { + nvkm_object_ref(NULL, (struct nvkm_object **)&device); + return ret; + } + + return 0; +} + +#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403 + +static void +nouveau_get_hdmi_dev(struct nouveau_drm *drm) +{ + struct pci_dev *pdev = drm->dev->pdev; + + if (!pdev) { + DRM_INFO("not a PCI device; no HDMI\n"); + drm->hdmi_device = NULL; + return; + } + + /* subfunction one is a hdmi audio device? */ + drm->hdmi_device = pci_get_bus_and_slot((unsigned int)pdev->bus->number, + PCI_DEVFN(PCI_SLOT(pdev->devfn), 1)); + + if (!drm->hdmi_device) { + NV_DEBUG(drm, "hdmi device not found %d %d %d\n", pdev->bus->number, PCI_SLOT(pdev->devfn), 1); + return; + } + + if ((drm->hdmi_device->class >> 8) != PCI_CLASS_MULTIMEDIA_HD_AUDIO) { + NV_DEBUG(drm, "possible hdmi device not audio %d\n", drm->hdmi_device->class); + pci_dev_put(drm->hdmi_device); + drm->hdmi_device = NULL; + return; + } +} + +static int +nouveau_drm_load(struct drm_device *dev, unsigned long flags) +{ + struct pci_dev *pdev = dev->pdev; + struct nouveau_drm *drm; + int ret; + + ret = nouveau_cli_create(nouveau_name(dev), "DRM", sizeof(*drm), + (void **)&drm); + if (ret) + return ret; + + dev->dev_private = drm; + drm->dev = dev; + nvxx_client(&drm->client.base)->debug = + nvkm_dbgopt(nouveau_debug, "DRM"); + + INIT_LIST_HEAD(&drm->clients); + spin_lock_init(&drm->tile.lock); + + nouveau_get_hdmi_dev(drm); + + /* make sure AGP controller is in a consistent state before we + * (possibly) execute vbios init tables (see nouveau_agp.h) + */ + if (pdev && drm_pci_device_is_agp(dev) && dev->agp) { + const u64 enables = NV_DEVICE_V0_DISABLE_IDENTIFY | + NV_DEVICE_V0_DISABLE_MMIO; + /* dummy device object, doesn't init anything, but allows + * agp code access to registers + */ + ret = nvif_device_init(&drm->client.base.base, NULL, + NVDRM_DEVICE, NV_DEVICE, + &(struct nv_device_v0) { + .device = ~0, + .disable = ~enables, + .debug0 = ~0, + }, sizeof(struct nv_device_v0), + &drm->device); + if (ret) + goto fail_device; + + nouveau_agp_reset(drm); + nvif_device_fini(&drm->device); + } + + ret = nvif_device_init(&drm->client.base.base, NULL, NVDRM_DEVICE, + NV_DEVICE, + &(struct nv_device_v0) { + .device = ~0, + .disable = 0, + .debug0 = 0, + }, sizeof(struct nv_device_v0), + &drm->device); + if (ret) + goto fail_device; + + dev->irq_enabled = true; + + /* workaround an odd issue on nvc1 by disabling the device's + * nosnoop capability. hopefully won't cause issues until a + * better fix is found - assuming there is one... + */ + if (drm->device.info.chipset == 0xc1) + nvif_mask(&drm->device, 0x00088080, 0x00000800, 0x00000000); + + nouveau_vga_init(drm); + nouveau_agp_init(drm); + + if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA) { + ret = nvkm_vm_new(nvxx_device(&drm->device), 0, (1ULL << 40), + 0x1000, &drm->client.vm); + if (ret) + goto fail_device; + + nvxx_client(&drm->client.base)->vm = drm->client.vm; + } + + ret = nouveau_ttm_init(drm); + if (ret) + goto fail_ttm; + + ret = nouveau_bios_init(dev); + if (ret) + goto fail_bios; + + ret = nouveau_display_create(dev); + if (ret) + goto fail_dispctor; + + if (dev->mode_config.num_crtc) { + ret = nouveau_display_init(dev); + if (ret) + goto fail_dispinit; + } + + nouveau_sysfs_init(dev); + nouveau_hwmon_init(dev); + nouveau_accel_init(drm); + nouveau_fbcon_init(dev); + + if (nouveau_runtime_pm != 0) { + pm_runtime_use_autosuspend(dev->dev); + pm_runtime_set_autosuspend_delay(dev->dev, 5000); + pm_runtime_set_active(dev->dev); + pm_runtime_allow(dev->dev); + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put(dev->dev); + } + return 0; + +fail_dispinit: + nouveau_display_destroy(dev); +fail_dispctor: + nouveau_bios_takedown(dev); +fail_bios: + nouveau_ttm_fini(drm); +fail_ttm: + nouveau_agp_fini(drm); + nouveau_vga_fini(drm); +fail_device: + nvif_device_fini(&drm->device); + nouveau_cli_destroy(&drm->client); + return ret; +} + +static int +nouveau_drm_unload(struct drm_device *dev) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + + pm_runtime_get_sync(dev->dev); + nouveau_fbcon_fini(dev); + nouveau_accel_fini(drm); + nouveau_hwmon_fini(dev); + nouveau_sysfs_fini(dev); + + if (dev->mode_config.num_crtc) + nouveau_display_fini(dev); + nouveau_display_destroy(dev); + + nouveau_bios_takedown(dev); + + nouveau_ttm_fini(drm); + nouveau_agp_fini(drm); + nouveau_vga_fini(drm); + + nvif_device_fini(&drm->device); + if (drm->hdmi_device) + pci_dev_put(drm->hdmi_device); + nouveau_cli_destroy(&drm->client); + return 0; +} + +void +nouveau_drm_device_remove(struct drm_device *dev) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_client *client; + struct nvkm_object *device; + + dev->irq_enabled = false; + client = nvxx_client(&drm->client.base); + device = client->device; + drm_put_dev(dev); + + nvkm_object_ref(NULL, &device); + nvkm_object_debug(); +} + +static void +nouveau_drm_remove(struct pci_dev *pdev) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + + nouveau_drm_device_remove(dev); +} + +static int +nouveau_do_suspend(struct drm_device *dev, bool runtime) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_cli *cli; + int ret; + + if (dev->mode_config.num_crtc) { + NV_INFO(drm, "suspending console...\n"); + nouveau_fbcon_set_suspend(dev, 1); + NV_INFO(drm, "suspending display...\n"); + ret = nouveau_display_suspend(dev, runtime); + if (ret) + return ret; + } + + NV_INFO(drm, "evicting buffers...\n"); + ttm_bo_evict_mm(&drm->ttm.bdev, TTM_PL_VRAM); + + NV_INFO(drm, "waiting for kernel channels to go idle...\n"); + if (drm->cechan) { + ret = nouveau_channel_idle(drm->cechan); + if (ret) + goto fail_display; + } + + if (drm->channel) { + ret = nouveau_channel_idle(drm->channel); + if (ret) + goto fail_display; + } + + NV_INFO(drm, "suspending client object trees...\n"); + if (drm->fence && nouveau_fence(drm)->suspend) { + if (!nouveau_fence(drm)->suspend(drm)) { + ret = -ENOMEM; + goto fail_display; + } + } + + list_for_each_entry(cli, &drm->clients, head) { + ret = nvif_client_suspend(&cli->base); + if (ret) + goto fail_client; + } + + NV_INFO(drm, "suspending kernel object tree...\n"); + ret = nvif_client_suspend(&drm->client.base); + if (ret) + goto fail_client; + + nouveau_agp_fini(drm); + return 0; + +fail_client: + list_for_each_entry_continue_reverse(cli, &drm->clients, head) { + nvif_client_resume(&cli->base); + } + + if (drm->fence && nouveau_fence(drm)->resume) + nouveau_fence(drm)->resume(drm); + +fail_display: + if (dev->mode_config.num_crtc) { + NV_INFO(drm, "resuming display...\n"); + nouveau_display_resume(dev, runtime); + } + return ret; +} + +static int +nouveau_do_resume(struct drm_device *dev, bool runtime) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_cli *cli; + + NV_INFO(drm, "re-enabling device...\n"); + + nouveau_agp_reset(drm); + + NV_INFO(drm, "resuming kernel object tree...\n"); + nvif_client_resume(&drm->client.base); + nouveau_agp_init(drm); + + NV_INFO(drm, "resuming client object trees...\n"); + if (drm->fence && nouveau_fence(drm)->resume) + nouveau_fence(drm)->resume(drm); + + list_for_each_entry(cli, &drm->clients, head) { + nvif_client_resume(&cli->base); + } + + nouveau_run_vbios_init(dev); + + if (dev->mode_config.num_crtc) { + NV_INFO(drm, "resuming display...\n"); + nouveau_display_resume(dev, runtime); + NV_INFO(drm, "resuming console...\n"); + nouveau_fbcon_set_suspend(dev, 0); + } + + return 0; +} + +int +nouveau_pmops_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct drm_device *drm_dev = pci_get_drvdata(pdev); + int ret; + + if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF || + drm_dev->switch_power_state == DRM_SWITCH_POWER_DYNAMIC_OFF) + return 0; + + ret = nouveau_do_suspend(drm_dev, false); + if (ret) + return ret; + + pci_save_state(pdev); + pci_disable_device(pdev); + pci_set_power_state(pdev, PCI_D3hot); + return 0; +} + +int +nouveau_pmops_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct drm_device *drm_dev = pci_get_drvdata(pdev); + int ret; + + if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF || + drm_dev->switch_power_state == DRM_SWITCH_POWER_DYNAMIC_OFF) + return 0; + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + ret = pci_enable_device(pdev); + if (ret) + return ret; + pci_set_master(pdev); + + return nouveau_do_resume(drm_dev, false); +} + +static int +nouveau_pmops_freeze(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct drm_device *drm_dev = pci_get_drvdata(pdev); + return nouveau_do_suspend(drm_dev, false); +} + +static int +nouveau_pmops_thaw(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct drm_device *drm_dev = pci_get_drvdata(pdev); + return nouveau_do_resume(drm_dev, false); +} + +static int +nouveau_pmops_runtime_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct drm_device *drm_dev = pci_get_drvdata(pdev); + int ret; + + if (nouveau_runtime_pm == 0) { + pm_runtime_forbid(dev); + return -EBUSY; + } + + /* are we optimus enabled? */ + if (nouveau_runtime_pm == -1 && !nouveau_is_optimus() && !nouveau_is_v1_dsm()) { + DRM_DEBUG_DRIVER("failing to power off - not optimus\n"); + pm_runtime_forbid(dev); + return -EBUSY; + } + + nv_debug_level(SILENT); + drm_kms_helper_poll_disable(drm_dev); + vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_OFF); + nouveau_switcheroo_optimus_dsm(); + ret = nouveau_do_suspend(drm_dev, true); + pci_save_state(pdev); + pci_disable_device(pdev); + pci_ignore_hotplug(pdev); + pci_set_power_state(pdev, PCI_D3cold); + drm_dev->switch_power_state = DRM_SWITCH_POWER_DYNAMIC_OFF; + return ret; +} + +static int +nouveau_pmops_runtime_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct drm_device *drm_dev = pci_get_drvdata(pdev); + struct nvif_device *device = &nouveau_drm(drm_dev)->device; + int ret; + + if (nouveau_runtime_pm == 0) + return -EINVAL; + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + ret = pci_enable_device(pdev); + if (ret) + return ret; + pci_set_master(pdev); + + ret = nouveau_do_resume(drm_dev, true); + drm_kms_helper_poll_enable(drm_dev); + /* do magic */ + nvif_mask(device, 0x88488, (1 << 25), (1 << 25)); + vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_ON); + drm_dev->switch_power_state = DRM_SWITCH_POWER_ON; + nv_debug_level(NORMAL); + return ret; +} + +static int +nouveau_pmops_runtime_idle(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct drm_device *drm_dev = pci_get_drvdata(pdev); + struct nouveau_drm *drm = nouveau_drm(drm_dev); + struct drm_crtc *crtc; + + if (nouveau_runtime_pm == 0) { + pm_runtime_forbid(dev); + return -EBUSY; + } + + /* are we optimus enabled? */ + if (nouveau_runtime_pm == -1 && !nouveau_is_optimus() && !nouveau_is_v1_dsm()) { + DRM_DEBUG_DRIVER("failing to power off - not optimus\n"); + pm_runtime_forbid(dev); + return -EBUSY; + } + + /* if we have a hdmi audio device - make sure it has a driver loaded */ + if (drm->hdmi_device) { + if (!drm->hdmi_device->driver) { + DRM_DEBUG_DRIVER("failing to power off - no HDMI audio driver loaded\n"); + pm_runtime_mark_last_busy(dev); + return -EBUSY; + } + } + + list_for_each_entry(crtc, &drm->dev->mode_config.crtc_list, head) { + if (crtc->enabled) { + DRM_DEBUG_DRIVER("failing to power off - crtc active\n"); + return -EBUSY; + } + } + pm_runtime_mark_last_busy(dev); + pm_runtime_autosuspend(dev); + /* we don't want the main rpm_idle to call suspend - we want to autosuspend */ + return 1; +} + +static int +nouveau_drm_open(struct drm_device *dev, struct drm_file *fpriv) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_cli *cli; + char name[32], tmpname[TASK_COMM_LEN]; + int ret; + + /* need to bring up power immediately if opening device */ + ret = pm_runtime_get_sync(dev->dev); + if (ret < 0 && ret != -EACCES) + return ret; + + get_task_comm(tmpname, current); + snprintf(name, sizeof(name), "%s[%d]", tmpname, pid_nr(fpriv->pid)); + + ret = nouveau_cli_create(nouveau_name(dev), name, sizeof(*cli), + (void **)&cli); + + if (ret) + goto out_suspend; + + cli->base.super = false; + + if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA) { + ret = nvkm_vm_new(nvxx_device(&drm->device), 0, (1ULL << 40), + 0x1000, &cli->vm); + if (ret) { + nouveau_cli_destroy(cli); + goto out_suspend; + } + + nvxx_client(&cli->base)->vm = cli->vm; + } + + fpriv->driver_priv = cli; + + mutex_lock(&drm->client.mutex); + list_add(&cli->head, &drm->clients); + mutex_unlock(&drm->client.mutex); + +out_suspend: + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put_autosuspend(dev->dev); + + return ret; +} + +static void +nouveau_drm_preclose(struct drm_device *dev, struct drm_file *fpriv) +{ + struct nouveau_cli *cli = nouveau_cli(fpriv); + struct nouveau_drm *drm = nouveau_drm(dev); + + pm_runtime_get_sync(dev->dev); + + if (cli->abi16) + nouveau_abi16_fini(cli->abi16); + + mutex_lock(&drm->client.mutex); + list_del(&cli->head); + mutex_unlock(&drm->client.mutex); + +} + +static void +nouveau_drm_postclose(struct drm_device *dev, struct drm_file *fpriv) +{ + struct nouveau_cli *cli = nouveau_cli(fpriv); + nouveau_cli_destroy(cli); + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put_autosuspend(dev->dev); +} + +static const struct drm_ioctl_desc +nouveau_ioctls[] = { + DRM_IOCTL_DEF_DRV(NOUVEAU_GETPARAM, nouveau_abi16_ioctl_getparam, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(NOUVEAU_SETPARAM, nouveau_abi16_ioctl_setparam, DRM_UNLOCKED|DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF_DRV(NOUVEAU_CHANNEL_ALLOC, nouveau_abi16_ioctl_channel_alloc, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(NOUVEAU_CHANNEL_FREE, nouveau_abi16_ioctl_channel_free, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(NOUVEAU_GROBJ_ALLOC, nouveau_abi16_ioctl_grobj_alloc, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(NOUVEAU_NOTIFIEROBJ_ALLOC, nouveau_abi16_ioctl_notifierobj_alloc, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(NOUVEAU_GPUOBJ_FREE, nouveau_abi16_ioctl_gpuobj_free, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_NEW, nouveau_gem_ioctl_new, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_PUSHBUF, nouveau_gem_ioctl_pushbuf, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_CPU_PREP, nouveau_gem_ioctl_cpu_prep, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_CPU_FINI, nouveau_gem_ioctl_cpu_fini, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_INFO, nouveau_gem_ioctl_info, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW), +}; + +long +nouveau_drm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct drm_file *filp = file->private_data; + struct drm_device *dev = filp->minor->dev; + long ret; + + ret = pm_runtime_get_sync(dev->dev); + if (ret < 0 && ret != -EACCES) + return ret; + + switch (_IOC_NR(cmd) - DRM_COMMAND_BASE) { + case DRM_NOUVEAU_NVIF: + ret = usif_ioctl(filp, (void __user *)arg, _IOC_SIZE(cmd)); + break; + default: + ret = drm_ioctl(file, cmd, arg); + break; + } + + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put_autosuspend(dev->dev); + return ret; +} + +static const struct file_operations +nouveau_driver_fops = { + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = nouveau_drm_ioctl, + .mmap = nouveau_ttm_mmap, + .poll = drm_poll, + .read = drm_read, +#if defined(CONFIG_COMPAT) + .compat_ioctl = nouveau_compat_ioctl, +#endif + .llseek = noop_llseek, +}; + +static struct drm_driver +driver_stub = { + .driver_features = + DRIVER_USE_AGP | + DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_RENDER, + + .load = nouveau_drm_load, + .unload = nouveau_drm_unload, + .open = nouveau_drm_open, + .preclose = nouveau_drm_preclose, + .postclose = nouveau_drm_postclose, + .lastclose = nouveau_vga_lastclose, + +#if defined(CONFIG_DEBUG_FS) + .debugfs_init = nouveau_debugfs_init, + .debugfs_cleanup = nouveau_debugfs_takedown, +#endif + + .get_vblank_counter = drm_vblank_count, + .enable_vblank = nouveau_display_vblank_enable, + .disable_vblank = nouveau_display_vblank_disable, + .get_scanout_position = nouveau_display_scanoutpos, + .get_vblank_timestamp = nouveau_display_vblstamp, + + .ioctls = nouveau_ioctls, + .num_ioctls = ARRAY_SIZE(nouveau_ioctls), + .fops = &nouveau_driver_fops, + + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, + .gem_prime_export = drm_gem_prime_export, + .gem_prime_import = drm_gem_prime_import, + .gem_prime_pin = nouveau_gem_prime_pin, + .gem_prime_res_obj = nouveau_gem_prime_res_obj, + .gem_prime_unpin = nouveau_gem_prime_unpin, + .gem_prime_get_sg_table = nouveau_gem_prime_get_sg_table, + .gem_prime_import_sg_table = nouveau_gem_prime_import_sg_table, + .gem_prime_vmap = nouveau_gem_prime_vmap, + .gem_prime_vunmap = nouveau_gem_prime_vunmap, + + .gem_free_object = nouveau_gem_object_del, + .gem_open_object = nouveau_gem_object_open, + .gem_close_object = nouveau_gem_object_close, + + .dumb_create = nouveau_display_dumb_create, + .dumb_map_offset = nouveau_display_dumb_map_offset, + .dumb_destroy = drm_gem_dumb_destroy, + + .name = DRIVER_NAME, + .desc = DRIVER_DESC, +#ifdef GIT_REVISION + .date = GIT_REVISION, +#else + .date = DRIVER_DATE, +#endif + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .patchlevel = DRIVER_PATCHLEVEL, +}; + +static struct pci_device_id +nouveau_drm_pci_table[] = { + { + PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID), + .class = PCI_BASE_CLASS_DISPLAY << 16, + .class_mask = 0xff << 16, + }, + { + PCI_DEVICE(PCI_VENDOR_ID_NVIDIA_SGS, PCI_ANY_ID), + .class = PCI_BASE_CLASS_DISPLAY << 16, + .class_mask = 0xff << 16, + }, + {} +}; + +static void nouveau_display_options(void) +{ + DRM_DEBUG_DRIVER("Loading Nouveau with parameters:\n"); + + DRM_DEBUG_DRIVER("... tv_disable : %d\n", nouveau_tv_disable); + DRM_DEBUG_DRIVER("... ignorelid : %d\n", nouveau_ignorelid); + DRM_DEBUG_DRIVER("... duallink : %d\n", nouveau_duallink); + DRM_DEBUG_DRIVER("... nofbaccel : %d\n", nouveau_nofbaccel); + DRM_DEBUG_DRIVER("... config : %s\n", nouveau_config); + DRM_DEBUG_DRIVER("... debug : %s\n", nouveau_debug); + DRM_DEBUG_DRIVER("... noaccel : %d\n", nouveau_noaccel); + DRM_DEBUG_DRIVER("... modeset : %d\n", nouveau_modeset); + DRM_DEBUG_DRIVER("... runpm : %d\n", nouveau_runtime_pm); + DRM_DEBUG_DRIVER("... vram_pushbuf : %d\n", nouveau_vram_pushbuf); + DRM_DEBUG_DRIVER("... pstate : %d\n", nouveau_pstate); +} + +static const struct dev_pm_ops nouveau_pm_ops = { + .suspend = nouveau_pmops_suspend, + .resume = nouveau_pmops_resume, + .freeze = nouveau_pmops_freeze, + .thaw = nouveau_pmops_thaw, + .poweroff = nouveau_pmops_freeze, + .restore = nouveau_pmops_resume, + .runtime_suspend = nouveau_pmops_runtime_suspend, + .runtime_resume = nouveau_pmops_runtime_resume, + .runtime_idle = nouveau_pmops_runtime_idle, +}; + +static struct pci_driver +nouveau_drm_pci_driver = { + .name = "nouveau", + .id_table = nouveau_drm_pci_table, + .probe = nouveau_drm_probe, + .remove = nouveau_drm_remove, + .driver.pm = &nouveau_pm_ops, +}; + +struct drm_device * +nouveau_platform_device_create_(struct platform_device *pdev, int size, + void **pobject) +{ + struct drm_device *drm; + int err; + + err = nvkm_device_create_(pdev, NVKM_BUS_PLATFORM, + nouveau_platform_name(pdev), + dev_name(&pdev->dev), nouveau_config, + nouveau_debug, size, pobject); + if (err) + return ERR_PTR(err); + + drm = drm_dev_alloc(&driver_platform, &pdev->dev); + if (!drm) { + err = -ENOMEM; + goto err_free; + } + + err = drm_dev_set_unique(drm, "%s", dev_name(&pdev->dev)); + if (err < 0) + goto err_free; + + drm->platformdev = pdev; + platform_set_drvdata(pdev, drm); + + return drm; + +err_free: + nvkm_object_ref(NULL, (struct nvkm_object **)pobject); + + return ERR_PTR(err); +} + +static int __init +nouveau_drm_init(void) +{ + driver_pci = driver_stub; + driver_pci.set_busid = drm_pci_set_busid; + driver_platform = driver_stub; + driver_platform.set_busid = drm_platform_set_busid; + + nouveau_display_options(); + + if (nouveau_modeset == -1) { +#ifdef CONFIG_VGA_CONSOLE + if (vgacon_text_force()) + nouveau_modeset = 0; +#endif + } + + if (!nouveau_modeset) + return 0; + +#ifdef CONFIG_NOUVEAU_PLATFORM_DRIVER + platform_driver_register(&nouveau_platform_driver); +#endif + + nouveau_register_dsm_handler(); + return drm_pci_init(&driver_pci, &nouveau_drm_pci_driver); +} + +static void __exit +nouveau_drm_exit(void) +{ + if (!nouveau_modeset) + return; + + drm_pci_exit(&driver_pci, &nouveau_drm_pci_driver); + nouveau_unregister_dsm_handler(); + +#ifdef CONFIG_NOUVEAU_PLATFORM_DRIVER + platform_driver_unregister(&nouveau_platform_driver); +#endif +} + +module_init(nouveau_drm_init); +module_exit(nouveau_drm_exit); + +MODULE_DEVICE_TABLE(pci, nouveau_drm_pci_table); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL and additional rights"); diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_drm.h b/kernel/drivers/gpu/drm/nouveau/nouveau_drm.h new file mode 100644 index 000000000..dd726523c --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_drm.h @@ -0,0 +1,202 @@ +#ifndef __NOUVEAU_DRMCLI_H__ +#define __NOUVEAU_DRMCLI_H__ + +#define DRIVER_AUTHOR "Nouveau Project" +#define DRIVER_EMAIL "nouveau@lists.freedesktop.org" + +#define DRIVER_NAME "nouveau" +#define DRIVER_DESC "nVidia Riva/TNT/GeForce/Quadro/Tesla" +#define DRIVER_DATE "20120801" + +#define DRIVER_MAJOR 1 +#define DRIVER_MINOR 2 +#define DRIVER_PATCHLEVEL 2 + +/* + * 1.1.1: + * - added support for tiled system memory buffer objects + * - added support for NOUVEAU_GETPARAM_GRAPH_UNITS on [nvc0,nve0]. + * - added support for compressed memory storage types on [nvc0,nve0]. + * - added support for software methods 0x600,0x644,0x6ac on nvc0 + * to control registers on the MPs to enable performance counters, + * and to control the warp error enable mask (OpenGL requires out of + * bounds access to local memory to be silently ignored / return 0). + * 1.1.2: + * - fixes multiple bugs in flip completion events and timestamping + * 1.2.0: + * - object api exposed to userspace + * - fermi,kepler,maxwell zbc + * 1.2.1: + * - allow concurrent access to bo's mapped read/write. + * 1.2.2: + * - add NOUVEAU_GEM_DOMAIN_COHERENT flag + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include "uapi/drm/nouveau_drm.h" + +struct nouveau_channel; +struct platform_device; + +#define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT) + +#include "nouveau_fence.h" +#include "nouveau_bios.h" + +struct nouveau_drm_tile { + struct nouveau_fence *fence; + bool used; +}; + +enum nouveau_drm_object_route { + NVDRM_OBJECT_NVIF = 0, + NVDRM_OBJECT_USIF, + NVDRM_OBJECT_ABI16, +}; + +enum nouveau_drm_notify_route { + NVDRM_NOTIFY_NVIF = 0, + NVDRM_NOTIFY_USIF +}; + +enum nouveau_drm_handle { + NVDRM_CLIENT = 0xffffffff, + NVDRM_DEVICE = 0xdddddddd, + NVDRM_CONTROL = 0xdddddddc, + NVDRM_DISPLAY = 0xd1500000, + NVDRM_PUSH = 0xbbbb0000, /* |= client chid */ + NVDRM_CHAN = 0xcccc0000, /* |= client chid */ + NVDRM_NVSW = 0x55550000, +}; + +struct nouveau_cli { + struct nvif_client base; + struct nvkm_vm *vm; /*XXX*/ + struct list_head head; + struct mutex mutex; + void *abi16; + struct list_head objects; + struct list_head notifys; +}; + +static inline struct nouveau_cli * +nouveau_cli(struct drm_file *fpriv) +{ + return fpriv ? fpriv->driver_priv : NULL; +} + +#include +#include + +extern int nouveau_runtime_pm; + +struct nouveau_drm { + struct nouveau_cli client; + struct drm_device *dev; + + struct nvif_device device; + struct list_head clients; + + struct { + enum { + UNKNOWN = 0, + DISABLE = 1, + ENABLED = 2 + } stat; + u32 base; + u32 size; + } agp; + + /* TTM interface support */ + struct { + struct drm_global_reference mem_global_ref; + struct ttm_bo_global_ref bo_global_ref; + struct ttm_bo_device bdev; + atomic_t validate_sequence; + int (*move)(struct nouveau_channel *, + struct ttm_buffer_object *, + struct ttm_mem_reg *, struct ttm_mem_reg *); + struct nouveau_channel *chan; + struct nvif_object copy; + int mtrr; + } ttm; + + /* GEM interface support */ + struct { + u64 vram_available; + u64 gart_available; + } gem; + + /* synchronisation */ + void *fence; + + /* context for accelerated drm-internal operations */ + struct nouveau_channel *cechan; + struct nouveau_channel *channel; + struct nvkm_gpuobj *notify; + struct nouveau_fbdev *fbcon; + struct nvif_object nvsw; + struct nvif_object ntfy; + + /* nv10-nv40 tiling regions */ + struct { + struct nouveau_drm_tile reg[15]; + spinlock_t lock; + } tile; + + /* modesetting */ + struct nvbios vbios; + struct nouveau_display *display; + struct backlight_device *backlight; + + /* power management */ + struct nouveau_hwmon *hwmon; + struct nouveau_sysfs *sysfs; + + /* display power reference */ + bool have_disp_power_ref; + + struct dev_pm_domain vga_pm_domain; + struct pci_dev *hdmi_device; +}; + +static inline struct nouveau_drm * +nouveau_drm(struct drm_device *dev) +{ + return dev->dev_private; +} + +int nouveau_pmops_suspend(struct device *); +int nouveau_pmops_resume(struct device *); + +#define nouveau_platform_device_create(p, u) \ + nouveau_platform_device_create_(p, sizeof(**u), (void **)u) +struct drm_device * +nouveau_platform_device_create_(struct platform_device *pdev, + int size, void **pobject); +void nouveau_drm_device_remove(struct drm_device *dev); + +#define NV_PRINTK(l,c,f,a...) do { \ + struct nouveau_cli *_cli = (c); \ + nv_##l(_cli->base.base.priv, f, ##a); \ +} while(0) +#define NV_FATAL(drm,f,a...) NV_PRINTK(fatal, &(drm)->client, f, ##a) +#define NV_ERROR(drm,f,a...) NV_PRINTK(error, &(drm)->client, f, ##a) +#define NV_WARN(drm,f,a...) NV_PRINTK(warn, &(drm)->client, f, ##a) +#define NV_INFO(drm,f,a...) NV_PRINTK(info, &(drm)->client, f, ##a) +#define NV_DEBUG(drm,f,a...) NV_PRINTK(debug, &(drm)->client, f, ##a) + +extern int nouveau_modeset; + +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_encoder.h b/kernel/drivers/gpu/drm/nouveau/nouveau_encoder.h new file mode 100644 index 000000000..c57a37e8e --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_encoder.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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. + * + */ + +#ifndef __NOUVEAU_ENCODER_H__ +#define __NOUVEAU_ENCODER_H__ + +#include + +#include +#include "dispnv04/disp.h" + +#define NV_DPMS_CLEARED 0x80 + +struct nvkm_i2c_port; + +struct nouveau_encoder { + struct drm_encoder_slave base; + + struct dcb_output *dcb; + int or; + struct nvkm_i2c_port *i2c; + + /* different to drm_encoder.crtc, this reflects what's + * actually programmed on the hw, not the proposed crtc */ + struct drm_crtc *crtc; + u32 ctrl; + + struct drm_display_mode mode; + int last_dpms; + + struct nv04_output_reg restore; + + union { + struct { + u8 dpcd[8]; + int link_nr; + int link_bw; + u32 datarate; + } dp; + }; +}; + +struct nouveau_encoder * +find_encoder(struct drm_connector *connector, int type); + +static inline struct nouveau_encoder *nouveau_encoder(struct drm_encoder *enc) +{ + struct drm_encoder_slave *slave = to_encoder_slave(enc); + + return container_of(slave, struct nouveau_encoder, base); +} + +static inline struct drm_encoder *to_drm_encoder(struct nouveau_encoder *enc) +{ + return &enc->base.base; +} + +static inline struct drm_encoder_slave_funcs * +get_slave_funcs(struct drm_encoder *enc) +{ + return to_encoder_slave(enc)->slave_funcs; +} + +/* nouveau_dp.c */ +int nouveau_dp_detect(struct nouveau_encoder *); + +struct nouveau_connector * +nouveau_encoder_connector_get(struct nouveau_encoder *encoder); + +#endif /* __NOUVEAU_ENCODER_H__ */ diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/kernel/drivers/gpu/drm/nouveau/nouveau_fbcon.c new file mode 100644 index 000000000..6751553ab --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -0,0 +1,572 @@ +/* + * Copyright © 2007 David Airlie + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to 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 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. + * + * Authors: + * David Airlie + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "nouveau_drm.h" +#include "nouveau_gem.h" +#include "nouveau_bo.h" +#include "nouveau_fbcon.h" +#include "nouveau_chan.h" + +#include "nouveau_crtc.h" + +MODULE_PARM_DESC(nofbaccel, "Disable fbcon acceleration"); +int nouveau_nofbaccel = 0; +module_param_named(nofbaccel, nouveau_nofbaccel, int, 0400); + +static void +nouveau_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect) +{ + struct nouveau_fbdev *fbcon = info->par; + struct nouveau_drm *drm = nouveau_drm(fbcon->dev); + struct nvif_device *device = &drm->device; + int ret; + + if (info->state != FBINFO_STATE_RUNNING) + return; + + ret = -ENODEV; + if (!in_interrupt() && !(info->flags & FBINFO_HWACCEL_DISABLED) && + mutex_trylock(&drm->client.mutex)) { + if (device->info.family < NV_DEVICE_INFO_V0_TESLA) + ret = nv04_fbcon_fillrect(info, rect); + else + if (device->info.family < NV_DEVICE_INFO_V0_FERMI) + ret = nv50_fbcon_fillrect(info, rect); + else + ret = nvc0_fbcon_fillrect(info, rect); + mutex_unlock(&drm->client.mutex); + } + + if (ret == 0) + return; + + if (ret != -ENODEV) + nouveau_fbcon_gpu_lockup(info); + cfb_fillrect(info, rect); +} + +static void +nouveau_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *image) +{ + struct nouveau_fbdev *fbcon = info->par; + struct nouveau_drm *drm = nouveau_drm(fbcon->dev); + struct nvif_device *device = &drm->device; + int ret; + + if (info->state != FBINFO_STATE_RUNNING) + return; + + ret = -ENODEV; + if (!in_interrupt() && !(info->flags & FBINFO_HWACCEL_DISABLED) && + mutex_trylock(&drm->client.mutex)) { + if (device->info.family < NV_DEVICE_INFO_V0_TESLA) + ret = nv04_fbcon_copyarea(info, image); + else + if (device->info.family < NV_DEVICE_INFO_V0_FERMI) + ret = nv50_fbcon_copyarea(info, image); + else + ret = nvc0_fbcon_copyarea(info, image); + mutex_unlock(&drm->client.mutex); + } + + if (ret == 0) + return; + + if (ret != -ENODEV) + nouveau_fbcon_gpu_lockup(info); + cfb_copyarea(info, image); +} + +static void +nouveau_fbcon_imageblit(struct fb_info *info, const struct fb_image *image) +{ + struct nouveau_fbdev *fbcon = info->par; + struct nouveau_drm *drm = nouveau_drm(fbcon->dev); + struct nvif_device *device = &drm->device; + int ret; + + if (info->state != FBINFO_STATE_RUNNING) + return; + + ret = -ENODEV; + if (!in_interrupt() && !(info->flags & FBINFO_HWACCEL_DISABLED) && + mutex_trylock(&drm->client.mutex)) { + if (device->info.family < NV_DEVICE_INFO_V0_TESLA) + ret = nv04_fbcon_imageblit(info, image); + else + if (device->info.family < NV_DEVICE_INFO_V0_FERMI) + ret = nv50_fbcon_imageblit(info, image); + else + ret = nvc0_fbcon_imageblit(info, image); + mutex_unlock(&drm->client.mutex); + } + + if (ret == 0) + return; + + if (ret != -ENODEV) + nouveau_fbcon_gpu_lockup(info); + cfb_imageblit(info, image); +} + +static int +nouveau_fbcon_sync(struct fb_info *info) +{ + struct nouveau_fbdev *fbcon = info->par; + struct nouveau_drm *drm = nouveau_drm(fbcon->dev); + struct nouveau_channel *chan = drm->channel; + int ret; + + if (!chan || !chan->accel_done || in_interrupt() || + info->state != FBINFO_STATE_RUNNING || + info->flags & FBINFO_HWACCEL_DISABLED) + return 0; + + if (!mutex_trylock(&drm->client.mutex)) + return 0; + + ret = nouveau_channel_idle(chan); + mutex_unlock(&drm->client.mutex); + if (ret) { + nouveau_fbcon_gpu_lockup(info); + return 0; + } + + chan->accel_done = false; + return 0; +} + +static struct fb_ops nouveau_fbcon_ops = { + .owner = THIS_MODULE, + .fb_check_var = drm_fb_helper_check_var, + .fb_set_par = drm_fb_helper_set_par, + .fb_fillrect = nouveau_fbcon_fillrect, + .fb_copyarea = nouveau_fbcon_copyarea, + .fb_imageblit = nouveau_fbcon_imageblit, + .fb_sync = nouveau_fbcon_sync, + .fb_pan_display = drm_fb_helper_pan_display, + .fb_blank = drm_fb_helper_blank, + .fb_setcmap = drm_fb_helper_setcmap, + .fb_debug_enter = drm_fb_helper_debug_enter, + .fb_debug_leave = drm_fb_helper_debug_leave, +}; + +static struct fb_ops nouveau_fbcon_sw_ops = { + .owner = THIS_MODULE, + .fb_check_var = drm_fb_helper_check_var, + .fb_set_par = drm_fb_helper_set_par, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_pan_display = drm_fb_helper_pan_display, + .fb_blank = drm_fb_helper_blank, + .fb_setcmap = drm_fb_helper_setcmap, + .fb_debug_enter = drm_fb_helper_debug_enter, + .fb_debug_leave = drm_fb_helper_debug_leave, +}; + +void +nouveau_fbcon_accel_save_disable(struct drm_device *dev) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + if (drm->fbcon) { + drm->fbcon->saved_flags = drm->fbcon->helper.fbdev->flags; + drm->fbcon->helper.fbdev->flags |= FBINFO_HWACCEL_DISABLED; + } +} + +void +nouveau_fbcon_accel_restore(struct drm_device *dev) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + if (drm->fbcon) { + drm->fbcon->helper.fbdev->flags = drm->fbcon->saved_flags; + } +} + +static void +nouveau_fbcon_accel_fini(struct drm_device *dev) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_fbdev *fbcon = drm->fbcon; + if (fbcon && drm->channel) { + console_lock(); + fbcon->helper.fbdev->flags |= FBINFO_HWACCEL_DISABLED; + console_unlock(); + nouveau_channel_idle(drm->channel); + nvif_object_fini(&fbcon->twod); + nvif_object_fini(&fbcon->blit); + nvif_object_fini(&fbcon->gdi); + nvif_object_fini(&fbcon->patt); + nvif_object_fini(&fbcon->rop); + nvif_object_fini(&fbcon->clip); + nvif_object_fini(&fbcon->surf2d); + } +} + +static void +nouveau_fbcon_accel_init(struct drm_device *dev) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_fbdev *fbcon = drm->fbcon; + struct fb_info *info = fbcon->helper.fbdev; + int ret; + + if (drm->device.info.family < NV_DEVICE_INFO_V0_TESLA) + ret = nv04_fbcon_accel_init(info); + else + if (drm->device.info.family < NV_DEVICE_INFO_V0_FERMI) + ret = nv50_fbcon_accel_init(info); + else + ret = nvc0_fbcon_accel_init(info); + + if (ret == 0) + info->fbops = &nouveau_fbcon_ops; +} + +static void nouveau_fbcon_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, + u16 blue, int regno) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + + nv_crtc->lut.r[regno] = red; + nv_crtc->lut.g[regno] = green; + nv_crtc->lut.b[regno] = blue; +} + +static void nouveau_fbcon_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, + u16 *blue, int regno) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + + *red = nv_crtc->lut.r[regno]; + *green = nv_crtc->lut.g[regno]; + *blue = nv_crtc->lut.b[regno]; +} + +static void +nouveau_fbcon_zfill(struct drm_device *dev, struct nouveau_fbdev *fbcon) +{ + struct fb_info *info = fbcon->helper.fbdev; + struct fb_fillrect rect; + + /* Clear the entire fbcon. The drm will program every connector + * with it's preferred mode. If the sizes differ, one display will + * quite likely have garbage around the console. + */ + rect.dx = rect.dy = 0; + rect.width = info->var.xres_virtual; + rect.height = info->var.yres_virtual; + rect.color = 0; + rect.rop = ROP_COPY; + info->fbops->fb_fillrect(info, &rect); +} + +static int +nouveau_fbcon_create(struct drm_fb_helper *helper, + struct drm_fb_helper_surface_size *sizes) +{ + struct nouveau_fbdev *fbcon = + container_of(helper, struct nouveau_fbdev, helper); + struct drm_device *dev = fbcon->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvif_device *device = &drm->device; + struct fb_info *info; + struct drm_framebuffer *fb; + struct nouveau_framebuffer *nouveau_fb; + struct nouveau_channel *chan; + struct nouveau_bo *nvbo; + struct drm_mode_fb_cmd2 mode_cmd; + struct pci_dev *pdev = dev->pdev; + int size, ret; + + mode_cmd.width = sizes->surface_width; + mode_cmd.height = sizes->surface_height; + + mode_cmd.pitches[0] = mode_cmd.width * (sizes->surface_bpp >> 3); + mode_cmd.pitches[0] = roundup(mode_cmd.pitches[0], 256); + + mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, + sizes->surface_depth); + + size = mode_cmd.pitches[0] * mode_cmd.height; + size = roundup(size, PAGE_SIZE); + + ret = nouveau_gem_new(dev, size, 0, NOUVEAU_GEM_DOMAIN_VRAM, + 0, 0x0000, &nvbo); + if (ret) { + NV_ERROR(drm, "failed to allocate framebuffer\n"); + goto out; + } + + ret = nouveau_bo_pin(nvbo, TTM_PL_FLAG_VRAM, false); + if (ret) { + NV_ERROR(drm, "failed to pin fb: %d\n", ret); + goto out_unref; + } + + ret = nouveau_bo_map(nvbo); + if (ret) { + NV_ERROR(drm, "failed to map fb: %d\n", ret); + goto out_unpin; + } + + chan = nouveau_nofbaccel ? NULL : drm->channel; + if (chan && device->info.family >= NV_DEVICE_INFO_V0_TESLA) { + ret = nouveau_bo_vma_add(nvbo, drm->client.vm, + &fbcon->nouveau_fb.vma); + if (ret) { + NV_ERROR(drm, "failed to map fb into chan: %d\n", ret); + chan = NULL; + } + } + + mutex_lock(&dev->struct_mutex); + + info = framebuffer_alloc(0, &pdev->dev); + if (!info) { + ret = -ENOMEM; + goto out_unlock; + } + info->skip_vt_switch = 1; + + ret = fb_alloc_cmap(&info->cmap, 256, 0); + if (ret) { + ret = -ENOMEM; + framebuffer_release(info); + goto out_unlock; + } + + info->par = fbcon; + + nouveau_framebuffer_init(dev, &fbcon->nouveau_fb, &mode_cmd, nvbo); + + nouveau_fb = &fbcon->nouveau_fb; + fb = &nouveau_fb->base; + + /* setup helper */ + fbcon->helper.fb = fb; + fbcon->helper.fbdev = info; + + strcpy(info->fix.id, "nouveaufb"); + if (!chan) + info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_DISABLED; + else + info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_COPYAREA | + FBINFO_HWACCEL_FILLRECT | + FBINFO_HWACCEL_IMAGEBLIT; + info->flags |= FBINFO_CAN_FORCE_OUTPUT; + info->fbops = &nouveau_fbcon_sw_ops; + info->fix.smem_start = nvbo->bo.mem.bus.base + + nvbo->bo.mem.bus.offset; + info->fix.smem_len = size; + + info->screen_base = nvbo_kmap_obj_iovirtual(nouveau_fb->nvbo); + info->screen_size = size; + + drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); + drm_fb_helper_fill_var(info, &fbcon->helper, sizes->fb_width, sizes->fb_height); + + /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */ + + mutex_unlock(&dev->struct_mutex); + + if (chan) + nouveau_fbcon_accel_init(dev); + nouveau_fbcon_zfill(dev, fbcon); + + /* To allow resizeing without swapping buffers */ + NV_INFO(drm, "allocated %dx%d fb: 0x%llx, bo %p\n", + nouveau_fb->base.width, nouveau_fb->base.height, + nvbo->bo.offset, nvbo); + + vga_switcheroo_client_fb_set(dev->pdev, info); + return 0; + +out_unlock: + mutex_unlock(&dev->struct_mutex); + if (chan) + nouveau_bo_vma_del(nvbo, &fbcon->nouveau_fb.vma); + nouveau_bo_unmap(nvbo); +out_unpin: + nouveau_bo_unpin(nvbo); +out_unref: + nouveau_bo_ref(NULL, &nvbo); +out: + return ret; +} + +void +nouveau_fbcon_output_poll_changed(struct drm_device *dev) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + if (drm->fbcon) + drm_fb_helper_hotplug_event(&drm->fbcon->helper); +} + +static int +nouveau_fbcon_destroy(struct drm_device *dev, struct nouveau_fbdev *fbcon) +{ + struct nouveau_framebuffer *nouveau_fb = &fbcon->nouveau_fb; + struct fb_info *info; + + if (fbcon->helper.fbdev) { + info = fbcon->helper.fbdev; + unregister_framebuffer(info); + if (info->cmap.len) + fb_dealloc_cmap(&info->cmap); + framebuffer_release(info); + } + + if (nouveau_fb->nvbo) { + nouveau_bo_unmap(nouveau_fb->nvbo); + nouveau_bo_vma_del(nouveau_fb->nvbo, &nouveau_fb->vma); + nouveau_bo_unpin(nouveau_fb->nvbo); + drm_gem_object_unreference_unlocked(&nouveau_fb->nvbo->gem); + nouveau_fb->nvbo = NULL; + } + drm_fb_helper_fini(&fbcon->helper); + drm_framebuffer_unregister_private(&nouveau_fb->base); + drm_framebuffer_cleanup(&nouveau_fb->base); + return 0; +} + +void nouveau_fbcon_gpu_lockup(struct fb_info *info) +{ + struct nouveau_fbdev *fbcon = info->par; + struct nouveau_drm *drm = nouveau_drm(fbcon->dev); + + NV_ERROR(drm, "GPU lockup - switching to software fbcon\n"); + info->flags |= FBINFO_HWACCEL_DISABLED; +} + +static const struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = { + .gamma_set = nouveau_fbcon_gamma_set, + .gamma_get = nouveau_fbcon_gamma_get, + .fb_probe = nouveau_fbcon_create, +}; + +void +nouveau_fbcon_set_suspend(struct drm_device *dev, int state) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + if (drm->fbcon) { + console_lock(); + if (state == FBINFO_STATE_RUNNING) + nouveau_fbcon_accel_restore(dev); + fb_set_suspend(drm->fbcon->helper.fbdev, state); + if (state != FBINFO_STATE_RUNNING) + nouveau_fbcon_accel_save_disable(dev); + console_unlock(); + } +} + +int +nouveau_fbcon_init(struct drm_device *dev) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_fbdev *fbcon; + int preferred_bpp; + int ret; + + if (!dev->mode_config.num_crtc || + (dev->pdev->class >> 8) != PCI_CLASS_DISPLAY_VGA) + return 0; + + fbcon = kzalloc(sizeof(struct nouveau_fbdev), GFP_KERNEL); + if (!fbcon) + return -ENOMEM; + + fbcon->dev = dev; + drm->fbcon = fbcon; + + drm_fb_helper_prepare(dev, &fbcon->helper, &nouveau_fbcon_helper_funcs); + + ret = drm_fb_helper_init(dev, &fbcon->helper, + dev->mode_config.num_crtc, 4); + if (ret) + goto free; + + ret = drm_fb_helper_single_add_all_connectors(&fbcon->helper); + if (ret) + goto fini; + + if (drm->device.info.ram_size <= 32 * 1024 * 1024) + preferred_bpp = 8; + else + if (drm->device.info.ram_size <= 64 * 1024 * 1024) + preferred_bpp = 16; + else + preferred_bpp = 32; + + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(dev); + + ret = drm_fb_helper_initial_config(&fbcon->helper, preferred_bpp); + if (ret) + goto fini; + + return 0; + +fini: + drm_fb_helper_fini(&fbcon->helper); +free: + kfree(fbcon); + return ret; +} + +void +nouveau_fbcon_fini(struct drm_device *dev) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + + if (!drm->fbcon) + return; + + nouveau_fbcon_accel_fini(dev); + nouveau_fbcon_destroy(dev, drm->fbcon); + kfree(drm->fbcon); + drm->fbcon = NULL; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_fbcon.h b/kernel/drivers/gpu/drm/nouveau/nouveau_fbcon.h new file mode 100644 index 000000000..1e2e9e27a --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_fbcon.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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. + * + */ + +#ifndef __NOUVEAU_FBCON_H__ +#define __NOUVEAU_FBCON_H__ + +#include + +#include "nouveau_display.h" + +struct nouveau_fbdev { + struct drm_fb_helper helper; + struct nouveau_framebuffer nouveau_fb; + struct list_head fbdev_list; + struct drm_device *dev; + unsigned int saved_flags; + struct nvif_object surf2d; + struct nvif_object clip; + struct nvif_object rop; + struct nvif_object patt; + struct nvif_object gdi; + struct nvif_object blit; + struct nvif_object twod; +}; + +void nouveau_fbcon_restore(void); + +int nv04_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region); +int nv04_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect); +int nv04_fbcon_imageblit(struct fb_info *info, const struct fb_image *image); +int nv04_fbcon_accel_init(struct fb_info *info); + +int nv50_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect); +int nv50_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region); +int nv50_fbcon_imageblit(struct fb_info *info, const struct fb_image *image); +int nv50_fbcon_accel_init(struct fb_info *info); + +int nvc0_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect); +int nvc0_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region); +int nvc0_fbcon_imageblit(struct fb_info *info, const struct fb_image *image); +int nvc0_fbcon_accel_init(struct fb_info *info); + +void nouveau_fbcon_gpu_lockup(struct fb_info *info); + +int nouveau_fbcon_init(struct drm_device *dev); +void nouveau_fbcon_fini(struct drm_device *dev); +void nouveau_fbcon_set_suspend(struct drm_device *dev, int state); +void nouveau_fbcon_accel_save_disable(struct drm_device *dev); +void nouveau_fbcon_accel_restore(struct drm_device *dev); + +void nouveau_fbcon_output_poll_changed(struct drm_device *dev); + +extern int nouveau_nofbaccel; + +#endif /* __NV50_FBCON_H__ */ + diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_fence.c b/kernel/drivers/gpu/drm/nouveau/nouveau_fence.c new file mode 100644 index 000000000..c6d56bef5 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_fence.c @@ -0,0 +1,590 @@ +/* + * Copyright (C) 2007 Ben Skeggs. + * 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 + +#include +#include +#include + +#include +#include + +#include "nouveau_drm.h" +#include "nouveau_dma.h" +#include "nouveau_fence.h" + +static const struct fence_ops nouveau_fence_ops_uevent; +static const struct fence_ops nouveau_fence_ops_legacy; + +static inline struct nouveau_fence * +from_fence(struct fence *fence) +{ + return container_of(fence, struct nouveau_fence, base); +} + +static inline struct nouveau_fence_chan * +nouveau_fctx(struct nouveau_fence *fence) +{ + return container_of(fence->base.lock, struct nouveau_fence_chan, lock); +} + +static int +nouveau_fence_signal(struct nouveau_fence *fence) +{ + int drop = 0; + + fence_signal_locked(&fence->base); + list_del(&fence->head); + rcu_assign_pointer(fence->channel, NULL); + + if (test_bit(FENCE_FLAG_USER_BITS, &fence->base.flags)) { + struct nouveau_fence_chan *fctx = nouveau_fctx(fence); + + if (!--fctx->notify_ref) + drop = 1; + } + + fence_put(&fence->base); + return drop; +} + +static struct nouveau_fence * +nouveau_local_fence(struct fence *fence, struct nouveau_drm *drm) { + struct nouveau_fence_priv *priv = (void*)drm->fence; + + if (fence->ops != &nouveau_fence_ops_legacy && + fence->ops != &nouveau_fence_ops_uevent) + return NULL; + + if (fence->context < priv->context_base || + fence->context >= priv->context_base + priv->contexts) + return NULL; + + return from_fence(fence); +} + +void +nouveau_fence_context_del(struct nouveau_fence_chan *fctx) +{ + struct nouveau_fence *fence; + + spin_lock_irq(&fctx->lock); + while (!list_empty(&fctx->pending)) { + fence = list_entry(fctx->pending.next, typeof(*fence), head); + + if (nouveau_fence_signal(fence)) + nvif_notify_put(&fctx->notify); + } + spin_unlock_irq(&fctx->lock); + + nvif_notify_fini(&fctx->notify); + fctx->dead = 1; + + /* + * Ensure that all accesses to fence->channel complete before freeing + * the channel. + */ + synchronize_rcu(); +} + +static void +nouveau_fence_context_put(struct kref *fence_ref) +{ + kfree(container_of(fence_ref, struct nouveau_fence_chan, fence_ref)); +} + +void +nouveau_fence_context_free(struct nouveau_fence_chan *fctx) +{ + kref_put(&fctx->fence_ref, nouveau_fence_context_put); +} + +static int +nouveau_fence_update(struct nouveau_channel *chan, struct nouveau_fence_chan *fctx) +{ + struct nouveau_fence *fence; + int drop = 0; + u32 seq = fctx->read(chan); + + while (!list_empty(&fctx->pending)) { + fence = list_entry(fctx->pending.next, typeof(*fence), head); + + if ((int)(seq - fence->base.seqno) < 0) + break; + + drop |= nouveau_fence_signal(fence); + } + + return drop; +} + +static int +nouveau_fence_wait_uevent_handler(struct nvif_notify *notify) +{ + struct nouveau_fence_chan *fctx = + container_of(notify, typeof(*fctx), notify); + unsigned long flags; + int ret = NVIF_NOTIFY_KEEP; + + spin_lock_irqsave(&fctx->lock, flags); + if (!list_empty(&fctx->pending)) { + struct nouveau_fence *fence; + struct nouveau_channel *chan; + + fence = list_entry(fctx->pending.next, typeof(*fence), head); + chan = rcu_dereference_protected(fence->channel, lockdep_is_held(&fctx->lock)); + if (nouveau_fence_update(fence->channel, fctx)) + ret = NVIF_NOTIFY_DROP; + } + spin_unlock_irqrestore(&fctx->lock, flags); + + return ret; +} + +void +nouveau_fence_context_new(struct nouveau_channel *chan, struct nouveau_fence_chan *fctx) +{ + struct nouveau_fence_priv *priv = (void*)chan->drm->fence; + struct nouveau_cli *cli = (void *)nvif_client(chan->object); + int ret; + + INIT_LIST_HEAD(&fctx->flip); + INIT_LIST_HEAD(&fctx->pending); + spin_lock_init(&fctx->lock); + fctx->context = priv->context_base + chan->chid; + + if (chan == chan->drm->cechan) + strcpy(fctx->name, "copy engine channel"); + else if (chan == chan->drm->channel) + strcpy(fctx->name, "generic kernel channel"); + else + strcpy(fctx->name, nvxx_client(&cli->base)->name); + + kref_init(&fctx->fence_ref); + if (!priv->uevent) + return; + + ret = nvif_notify_init(chan->object, NULL, + nouveau_fence_wait_uevent_handler, false, + G82_CHANNEL_DMA_V0_NTFY_UEVENT, + &(struct nvif_notify_uevent_req) { }, + sizeof(struct nvif_notify_uevent_req), + sizeof(struct nvif_notify_uevent_rep), + &fctx->notify); + + WARN_ON(ret); +} + +struct nouveau_fence_work { + struct work_struct work; + struct fence_cb cb; + void (*func)(void *); + void *data; +}; + +static void +nouveau_fence_work_handler(struct work_struct *kwork) +{ + struct nouveau_fence_work *work = container_of(kwork, typeof(*work), work); + work->func(work->data); + kfree(work); +} + +static void nouveau_fence_work_cb(struct fence *fence, struct fence_cb *cb) +{ + struct nouveau_fence_work *work = container_of(cb, typeof(*work), cb); + + schedule_work(&work->work); +} + +void +nouveau_fence_work(struct fence *fence, + void (*func)(void *), void *data) +{ + struct nouveau_fence_work *work; + + if (fence_is_signaled(fence)) + goto err; + + work = kmalloc(sizeof(*work), GFP_KERNEL); + if (!work) { + /* + * this might not be a nouveau fence any more, + * so force a lazy wait here + */ + WARN_ON(nouveau_fence_wait((struct nouveau_fence *)fence, + true, false)); + goto err; + } + + INIT_WORK(&work->work, nouveau_fence_work_handler); + work->func = func; + work->data = data; + + if (fence_add_callback(fence, &work->cb, nouveau_fence_work_cb) < 0) + goto err_free; + return; + +err_free: + kfree(work); +err: + func(data); +} + +int +nouveau_fence_emit(struct nouveau_fence *fence, struct nouveau_channel *chan) +{ + struct nouveau_fence_chan *fctx = chan->fence; + struct nouveau_fence_priv *priv = (void*)chan->drm->fence; + int ret; + + fence->channel = chan; + fence->timeout = jiffies + (15 * HZ); + + if (priv->uevent) + fence_init(&fence->base, &nouveau_fence_ops_uevent, + &fctx->lock, fctx->context, ++fctx->sequence); + else + fence_init(&fence->base, &nouveau_fence_ops_legacy, + &fctx->lock, fctx->context, ++fctx->sequence); + kref_get(&fctx->fence_ref); + + trace_fence_emit(&fence->base); + ret = fctx->emit(fence); + if (!ret) { + fence_get(&fence->base); + spin_lock_irq(&fctx->lock); + + if (nouveau_fence_update(chan, fctx)) + nvif_notify_put(&fctx->notify); + + list_add_tail(&fence->head, &fctx->pending); + spin_unlock_irq(&fctx->lock); + } + + return ret; +} + +bool +nouveau_fence_done(struct nouveau_fence *fence) +{ + if (fence->base.ops == &nouveau_fence_ops_legacy || + fence->base.ops == &nouveau_fence_ops_uevent) { + struct nouveau_fence_chan *fctx = nouveau_fctx(fence); + struct nouveau_channel *chan; + unsigned long flags; + + if (test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->base.flags)) + return true; + + spin_lock_irqsave(&fctx->lock, flags); + chan = rcu_dereference_protected(fence->channel, lockdep_is_held(&fctx->lock)); + if (chan && nouveau_fence_update(chan, fctx)) + nvif_notify_put(&fctx->notify); + spin_unlock_irqrestore(&fctx->lock, flags); + } + return fence_is_signaled(&fence->base); +} + +static long +nouveau_fence_wait_legacy(struct fence *f, bool intr, long wait) +{ + struct nouveau_fence *fence = from_fence(f); + unsigned long sleep_time = NSEC_PER_MSEC / 1000; + unsigned long t = jiffies, timeout = t + wait; + + while (!nouveau_fence_done(fence)) { + ktime_t kt; + + t = jiffies; + + if (wait != MAX_SCHEDULE_TIMEOUT && time_after_eq(t, timeout)) { + __set_current_state(TASK_RUNNING); + return 0; + } + + __set_current_state(intr ? TASK_INTERRUPTIBLE : + TASK_UNINTERRUPTIBLE); + + kt = ktime_set(0, sleep_time); + schedule_hrtimeout(&kt, HRTIMER_MODE_REL); + sleep_time *= 2; + if (sleep_time > NSEC_PER_MSEC) + sleep_time = NSEC_PER_MSEC; + + if (intr && signal_pending(current)) + return -ERESTARTSYS; + } + + __set_current_state(TASK_RUNNING); + + return timeout - t; +} + +static int +nouveau_fence_wait_busy(struct nouveau_fence *fence, bool intr) +{ + int ret = 0; + + while (!nouveau_fence_done(fence)) { + if (time_after_eq(jiffies, fence->timeout)) { + ret = -EBUSY; + break; + } + + __set_current_state(intr ? + TASK_INTERRUPTIBLE : + TASK_UNINTERRUPTIBLE); + + if (intr && signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + } + + __set_current_state(TASK_RUNNING); + return ret; +} + +int +nouveau_fence_wait(struct nouveau_fence *fence, bool lazy, bool intr) +{ + long ret; + + if (!lazy) + return nouveau_fence_wait_busy(fence, intr); + + ret = fence_wait_timeout(&fence->base, intr, 15 * HZ); + if (ret < 0) + return ret; + else if (!ret) + return -EBUSY; + else + return 0; +} + +int +nouveau_fence_sync(struct nouveau_bo *nvbo, struct nouveau_channel *chan, bool exclusive, bool intr) +{ + struct nouveau_fence_chan *fctx = chan->fence; + struct fence *fence; + struct reservation_object *resv = nvbo->bo.resv; + struct reservation_object_list *fobj; + struct nouveau_fence *f; + int ret = 0, i; + + if (!exclusive) { + ret = reservation_object_reserve_shared(resv); + + if (ret) + return ret; + } + + fobj = reservation_object_get_list(resv); + fence = reservation_object_get_excl(resv); + + if (fence && (!exclusive || !fobj || !fobj->shared_count)) { + struct nouveau_channel *prev = NULL; + bool must_wait = true; + + f = nouveau_local_fence(fence, chan->drm); + if (f) { + rcu_read_lock(); + prev = rcu_dereference(f->channel); + if (prev && (prev == chan || fctx->sync(f, prev, chan) == 0)) + must_wait = false; + rcu_read_unlock(); + } + + if (must_wait) + ret = fence_wait(fence, intr); + + return ret; + } + + if (!exclusive || !fobj) + return ret; + + for (i = 0; i < fobj->shared_count && !ret; ++i) { + struct nouveau_channel *prev = NULL; + bool must_wait = true; + + fence = rcu_dereference_protected(fobj->shared[i], + reservation_object_held(resv)); + + f = nouveau_local_fence(fence, chan->drm); + if (f) { + rcu_read_lock(); + prev = rcu_dereference(f->channel); + if (prev && (prev == chan || fctx->sync(f, prev, chan) == 0)) + must_wait = false; + rcu_read_unlock(); + } + + if (must_wait) + ret = fence_wait(fence, intr); + } + + return ret; +} + +void +nouveau_fence_unref(struct nouveau_fence **pfence) +{ + if (*pfence) + fence_put(&(*pfence)->base); + *pfence = NULL; +} + +int +nouveau_fence_new(struct nouveau_channel *chan, bool sysmem, + struct nouveau_fence **pfence) +{ + struct nouveau_fence *fence; + int ret = 0; + + if (unlikely(!chan->fence)) + return -ENODEV; + + fence = kzalloc(sizeof(*fence), GFP_KERNEL); + if (!fence) + return -ENOMEM; + + fence->sysmem = sysmem; + + ret = nouveau_fence_emit(fence, chan); + if (ret) + nouveau_fence_unref(&fence); + + *pfence = fence; + return ret; +} + +static const char *nouveau_fence_get_get_driver_name(struct fence *fence) +{ + return "nouveau"; +} + +static const char *nouveau_fence_get_timeline_name(struct fence *f) +{ + struct nouveau_fence *fence = from_fence(f); + struct nouveau_fence_chan *fctx = nouveau_fctx(fence); + + return !fctx->dead ? fctx->name : "dead channel"; +} + +/* + * In an ideal world, read would not assume the channel context is still alive. + * This function may be called from another device, running into free memory as a + * result. The drm node should still be there, so we can derive the index from + * the fence context. + */ +static bool nouveau_fence_is_signaled(struct fence *f) +{ + struct nouveau_fence *fence = from_fence(f); + struct nouveau_fence_chan *fctx = nouveau_fctx(fence); + struct nouveau_channel *chan; + bool ret = false; + + rcu_read_lock(); + chan = rcu_dereference(fence->channel); + if (chan) + ret = (int)(fctx->read(chan) - fence->base.seqno) >= 0; + rcu_read_unlock(); + + return ret; +} + +static bool nouveau_fence_no_signaling(struct fence *f) +{ + struct nouveau_fence *fence = from_fence(f); + + /* + * caller should have a reference on the fence, + * else fence could get freed here + */ + WARN_ON(atomic_read(&fence->base.refcount.refcount) <= 1); + + /* + * This needs uevents to work correctly, but fence_add_callback relies on + * being able to enable signaling. It will still get signaled eventually, + * just not right away. + */ + if (nouveau_fence_is_signaled(f)) { + list_del(&fence->head); + + fence_put(&fence->base); + return false; + } + + return true; +} + +static void nouveau_fence_release(struct fence *f) +{ + struct nouveau_fence *fence = from_fence(f); + struct nouveau_fence_chan *fctx = nouveau_fctx(fence); + + kref_put(&fctx->fence_ref, nouveau_fence_context_put); + fence_free(&fence->base); +} + +static const struct fence_ops nouveau_fence_ops_legacy = { + .get_driver_name = nouveau_fence_get_get_driver_name, + .get_timeline_name = nouveau_fence_get_timeline_name, + .enable_signaling = nouveau_fence_no_signaling, + .signaled = nouveau_fence_is_signaled, + .wait = nouveau_fence_wait_legacy, + .release = nouveau_fence_release +}; + +static bool nouveau_fence_enable_signaling(struct fence *f) +{ + struct nouveau_fence *fence = from_fence(f); + struct nouveau_fence_chan *fctx = nouveau_fctx(fence); + bool ret; + + if (!fctx->notify_ref++) + nvif_notify_get(&fctx->notify); + + ret = nouveau_fence_no_signaling(f); + if (ret) + set_bit(FENCE_FLAG_USER_BITS, &fence->base.flags); + else if (!--fctx->notify_ref) + nvif_notify_put(&fctx->notify); + + return ret; +} + +static const struct fence_ops nouveau_fence_ops_uevent = { + .get_driver_name = nouveau_fence_get_get_driver_name, + .get_timeline_name = nouveau_fence_get_timeline_name, + .enable_signaling = nouveau_fence_enable_signaling, + .signaled = nouveau_fence_is_signaled, + .wait = fence_default_wait, + .release = NULL +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_fence.h b/kernel/drivers/gpu/drm/nouveau/nouveau_fence.h new file mode 100644 index 000000000..d9241d824 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_fence.h @@ -0,0 +1,107 @@ +#ifndef __NOUVEAU_FENCE_H__ +#define __NOUVEAU_FENCE_H__ + +#include +#include + +struct nouveau_drm; +struct nouveau_bo; + +struct nouveau_fence { + struct fence base; + + struct list_head head; + + bool sysmem; + + struct nouveau_channel __rcu *channel; + unsigned long timeout; +}; + +int nouveau_fence_new(struct nouveau_channel *, bool sysmem, + struct nouveau_fence **); +void nouveau_fence_unref(struct nouveau_fence **); + +int nouveau_fence_emit(struct nouveau_fence *, struct nouveau_channel *); +bool nouveau_fence_done(struct nouveau_fence *); +void nouveau_fence_work(struct fence *, void (*)(void *), void *); +int nouveau_fence_wait(struct nouveau_fence *, bool lazy, bool intr); +int nouveau_fence_sync(struct nouveau_bo *, struct nouveau_channel *, bool exclusive, bool intr); + +struct nouveau_fence_chan { + spinlock_t lock; + struct kref fence_ref; + + struct list_head pending; + struct list_head flip; + + int (*emit)(struct nouveau_fence *); + int (*sync)(struct nouveau_fence *, struct nouveau_channel *, + struct nouveau_channel *); + u32 (*read)(struct nouveau_channel *); + int (*emit32)(struct nouveau_channel *, u64, u32); + int (*sync32)(struct nouveau_channel *, u64, u32); + + u32 sequence; + u32 context; + char name[32]; + + struct nvif_notify notify; + int notify_ref, dead; +}; + +struct nouveau_fence_priv { + void (*dtor)(struct nouveau_drm *); + bool (*suspend)(struct nouveau_drm *); + void (*resume)(struct nouveau_drm *); + int (*context_new)(struct nouveau_channel *); + void (*context_del)(struct nouveau_channel *); + + u32 contexts, context_base; + bool uevent; +}; + +#define nouveau_fence(drm) ((struct nouveau_fence_priv *)(drm)->fence) + +void nouveau_fence_context_new(struct nouveau_channel *, struct nouveau_fence_chan *); +void nouveau_fence_context_del(struct nouveau_fence_chan *); +void nouveau_fence_context_free(struct nouveau_fence_chan *); + +int nv04_fence_create(struct nouveau_drm *); +int nv04_fence_mthd(struct nouveau_channel *, u32, u32, u32); + +int nv10_fence_emit(struct nouveau_fence *); +int nv17_fence_sync(struct nouveau_fence *, struct nouveau_channel *, + struct nouveau_channel *); +u32 nv10_fence_read(struct nouveau_channel *); +void nv10_fence_context_del(struct nouveau_channel *); +void nv10_fence_destroy(struct nouveau_drm *); +int nv10_fence_create(struct nouveau_drm *); + +int nv17_fence_create(struct nouveau_drm *); +void nv17_fence_resume(struct nouveau_drm *drm); + +int nv50_fence_create(struct nouveau_drm *); +int nv84_fence_create(struct nouveau_drm *); +int nvc0_fence_create(struct nouveau_drm *); + +int nouveau_flip_complete(void *chan); + +struct nv84_fence_chan { + struct nouveau_fence_chan base; + struct nvkm_vma vma; + struct nvkm_vma vma_gart; + struct nvkm_vma dispc_vma[4]; +}; + +struct nv84_fence_priv { + struct nouveau_fence_priv base; + struct nouveau_bo *bo; + struct nouveau_bo *bo_gart; + u32 *suspend; +}; + +u64 nv84_fence_crtc(struct nouveau_channel *, int); +int nv84_fence_context_new(struct nouveau_channel *); + +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_gem.c b/kernel/drivers/gpu/drm/nouveau/nouveau_gem.c new file mode 100644 index 000000000..0e690bf19 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -0,0 +1,925 @@ +/* + * Copyright (C) 2008 Ben Skeggs. + * 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 "nouveau_drm.h" +#include "nouveau_dma.h" +#include "nouveau_fence.h" +#include "nouveau_abi16.h" + +#include "nouveau_ttm.h" +#include "nouveau_gem.h" + +void +nouveau_gem_object_del(struct drm_gem_object *gem) +{ + struct nouveau_bo *nvbo = nouveau_gem_object(gem); + struct nouveau_drm *drm = nouveau_bdev(nvbo->bo.bdev); + struct ttm_buffer_object *bo = &nvbo->bo; + struct device *dev = drm->dev->dev; + int ret; + + ret = pm_runtime_get_sync(dev); + if (WARN_ON(ret < 0 && ret != -EACCES)) + return; + + if (gem->import_attach) + drm_prime_gem_destroy(gem, nvbo->bo.sg); + + drm_gem_object_release(gem); + + /* reset filp so nouveau_bo_del_ttm() can test for it */ + gem->filp = NULL; + ttm_bo_unref(&bo); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); +} + +int +nouveau_gem_object_open(struct drm_gem_object *gem, struct drm_file *file_priv) +{ + struct nouveau_cli *cli = nouveau_cli(file_priv); + struct nouveau_bo *nvbo = nouveau_gem_object(gem); + struct nouveau_drm *drm = nouveau_bdev(nvbo->bo.bdev); + struct nvkm_vma *vma; + struct device *dev = drm->dev->dev; + int ret; + + if (!cli->vm) + return 0; + + ret = ttm_bo_reserve(&nvbo->bo, false, false, false, NULL); + if (ret) + return ret; + + vma = nouveau_bo_vma_find(nvbo, cli->vm); + if (!vma) { + vma = kzalloc(sizeof(*vma), GFP_KERNEL); + if (!vma) { + ret = -ENOMEM; + goto out; + } + + ret = pm_runtime_get_sync(dev); + if (ret < 0 && ret != -EACCES) + goto out; + + ret = nouveau_bo_vma_add(nvbo, cli->vm, vma); + if (ret) + kfree(vma); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + } else { + vma->refcount++; + } + +out: + ttm_bo_unreserve(&nvbo->bo); + return ret; +} + +static void +nouveau_gem_object_delete(void *data) +{ + struct nvkm_vma *vma = data; + nvkm_vm_unmap(vma); + nvkm_vm_put(vma); + kfree(vma); +} + +static void +nouveau_gem_object_unmap(struct nouveau_bo *nvbo, struct nvkm_vma *vma) +{ + const bool mapped = nvbo->bo.mem.mem_type != TTM_PL_SYSTEM; + struct reservation_object *resv = nvbo->bo.resv; + struct reservation_object_list *fobj; + struct fence *fence = NULL; + + fobj = reservation_object_get_list(resv); + + list_del(&vma->head); + + if (fobj && fobj->shared_count > 1) + ttm_bo_wait(&nvbo->bo, true, false, false); + else if (fobj && fobj->shared_count == 1) + fence = rcu_dereference_protected(fobj->shared[0], + reservation_object_held(resv)); + else + fence = reservation_object_get_excl(nvbo->bo.resv); + + if (fence && mapped) { + nouveau_fence_work(fence, nouveau_gem_object_delete, vma); + } else { + if (mapped) + nvkm_vm_unmap(vma); + nvkm_vm_put(vma); + kfree(vma); + } +} + +void +nouveau_gem_object_close(struct drm_gem_object *gem, struct drm_file *file_priv) +{ + struct nouveau_cli *cli = nouveau_cli(file_priv); + struct nouveau_bo *nvbo = nouveau_gem_object(gem); + struct nouveau_drm *drm = nouveau_bdev(nvbo->bo.bdev); + struct device *dev = drm->dev->dev; + struct nvkm_vma *vma; + int ret; + + if (!cli->vm) + return; + + ret = ttm_bo_reserve(&nvbo->bo, false, false, false, NULL); + if (ret) + return; + + vma = nouveau_bo_vma_find(nvbo, cli->vm); + if (vma) { + if (--vma->refcount == 0) { + ret = pm_runtime_get_sync(dev); + if (!WARN_ON(ret < 0 && ret != -EACCES)) { + nouveau_gem_object_unmap(nvbo, vma); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + } + } + } + ttm_bo_unreserve(&nvbo->bo); +} + +int +nouveau_gem_new(struct drm_device *dev, int size, int align, uint32_t domain, + uint32_t tile_mode, uint32_t tile_flags, + struct nouveau_bo **pnvbo) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_bo *nvbo; + u32 flags = 0; + int ret; + + if (domain & NOUVEAU_GEM_DOMAIN_VRAM) + flags |= TTM_PL_FLAG_VRAM; + if (domain & NOUVEAU_GEM_DOMAIN_GART) + flags |= TTM_PL_FLAG_TT; + if (!flags || domain & NOUVEAU_GEM_DOMAIN_CPU) + flags |= TTM_PL_FLAG_SYSTEM; + + if (domain & NOUVEAU_GEM_DOMAIN_COHERENT) + flags |= TTM_PL_FLAG_UNCACHED; + + ret = nouveau_bo_new(dev, size, align, flags, tile_mode, + tile_flags, NULL, NULL, pnvbo); + if (ret) + return ret; + nvbo = *pnvbo; + + /* we restrict allowed domains on nv50+ to only the types + * that were requested at creation time. not possibly on + * earlier chips without busting the ABI. + */ + nvbo->valid_domains = NOUVEAU_GEM_DOMAIN_VRAM | + NOUVEAU_GEM_DOMAIN_GART; + if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA) + nvbo->valid_domains &= domain; + + /* Initialize the embedded gem-object. We return a single gem-reference + * to the caller, instead of a normal nouveau_bo ttm reference. */ + ret = drm_gem_object_init(dev, &nvbo->gem, nvbo->bo.mem.size); + if (ret) { + nouveau_bo_ref(NULL, pnvbo); + return -ENOMEM; + } + + nvbo->bo.persistent_swap_storage = nvbo->gem.filp; + return 0; +} + +static int +nouveau_gem_info(struct drm_file *file_priv, struct drm_gem_object *gem, + struct drm_nouveau_gem_info *rep) +{ + struct nouveau_cli *cli = nouveau_cli(file_priv); + struct nouveau_bo *nvbo = nouveau_gem_object(gem); + struct nvkm_vma *vma; + + if (nvbo->bo.mem.mem_type == TTM_PL_TT) + rep->domain = NOUVEAU_GEM_DOMAIN_GART; + else + rep->domain = NOUVEAU_GEM_DOMAIN_VRAM; + + rep->offset = nvbo->bo.offset; + if (cli->vm) { + vma = nouveau_bo_vma_find(nvbo, cli->vm); + if (!vma) + return -EINVAL; + + rep->offset = vma->offset; + } + + rep->size = nvbo->bo.mem.num_pages << PAGE_SHIFT; + rep->map_handle = drm_vma_node_offset_addr(&nvbo->bo.vma_node); + rep->tile_mode = nvbo->tile_mode; + rep->tile_flags = nvbo->tile_flags; + return 0; +} + +int +nouveau_gem_ioctl_new(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_cli *cli = nouveau_cli(file_priv); + struct nvkm_fb *pfb = nvxx_fb(&drm->device); + struct drm_nouveau_gem_new *req = data; + struct nouveau_bo *nvbo = NULL; + int ret = 0; + + if (!pfb->memtype_valid(pfb, req->info.tile_flags)) { + NV_PRINTK(error, cli, "bad page flags: 0x%08x\n", req->info.tile_flags); + return -EINVAL; + } + + ret = nouveau_gem_new(dev, req->info.size, req->align, + req->info.domain, req->info.tile_mode, + req->info.tile_flags, &nvbo); + if (ret) + return ret; + + ret = drm_gem_handle_create(file_priv, &nvbo->gem, &req->info.handle); + if (ret == 0) { + ret = nouveau_gem_info(file_priv, &nvbo->gem, &req->info); + if (ret) + drm_gem_handle_delete(file_priv, req->info.handle); + } + + /* drop reference from allocate - handle holds it now */ + drm_gem_object_unreference_unlocked(&nvbo->gem); + return ret; +} + +static int +nouveau_gem_set_domain(struct drm_gem_object *gem, uint32_t read_domains, + uint32_t write_domains, uint32_t valid_domains) +{ + struct nouveau_bo *nvbo = nouveau_gem_object(gem); + struct ttm_buffer_object *bo = &nvbo->bo; + uint32_t domains = valid_domains & nvbo->valid_domains & + (write_domains ? write_domains : read_domains); + uint32_t pref_flags = 0, valid_flags = 0; + + if (!domains) + return -EINVAL; + + if (valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) + valid_flags |= TTM_PL_FLAG_VRAM; + + if (valid_domains & NOUVEAU_GEM_DOMAIN_GART) + valid_flags |= TTM_PL_FLAG_TT; + + if ((domains & NOUVEAU_GEM_DOMAIN_VRAM) && + bo->mem.mem_type == TTM_PL_VRAM) + pref_flags |= TTM_PL_FLAG_VRAM; + + else if ((domains & NOUVEAU_GEM_DOMAIN_GART) && + bo->mem.mem_type == TTM_PL_TT) + pref_flags |= TTM_PL_FLAG_TT; + + else if (domains & NOUVEAU_GEM_DOMAIN_VRAM) + pref_flags |= TTM_PL_FLAG_VRAM; + + else + pref_flags |= TTM_PL_FLAG_TT; + + nouveau_bo_placement_set(nvbo, pref_flags, valid_flags); + + return 0; +} + +struct validate_op { + struct list_head list; + struct ww_acquire_ctx ticket; +}; + +static void +validate_fini_no_ticket(struct validate_op *op, struct nouveau_fence *fence, + struct drm_nouveau_gem_pushbuf_bo *pbbo) +{ + struct nouveau_bo *nvbo; + struct drm_nouveau_gem_pushbuf_bo *b; + + while (!list_empty(&op->list)) { + nvbo = list_entry(op->list.next, struct nouveau_bo, entry); + b = &pbbo[nvbo->pbbo_index]; + + if (likely(fence)) + nouveau_bo_fence(nvbo, fence, !!b->write_domains); + + if (unlikely(nvbo->validate_mapped)) { + ttm_bo_kunmap(&nvbo->kmap); + nvbo->validate_mapped = false; + } + + list_del(&nvbo->entry); + nvbo->reserved_by = NULL; + ttm_bo_unreserve_ticket(&nvbo->bo, &op->ticket); + drm_gem_object_unreference_unlocked(&nvbo->gem); + } +} + +static void +validate_fini(struct validate_op *op, struct nouveau_fence *fence, + struct drm_nouveau_gem_pushbuf_bo *pbbo) +{ + validate_fini_no_ticket(op, fence, pbbo); + ww_acquire_fini(&op->ticket); +} + +static int +validate_init(struct nouveau_channel *chan, struct drm_file *file_priv, + struct drm_nouveau_gem_pushbuf_bo *pbbo, + int nr_buffers, struct validate_op *op) +{ + struct nouveau_cli *cli = nouveau_cli(file_priv); + struct drm_device *dev = chan->drm->dev; + int trycnt = 0; + int ret, i; + struct nouveau_bo *res_bo = NULL; + LIST_HEAD(gart_list); + LIST_HEAD(vram_list); + LIST_HEAD(both_list); + + ww_acquire_init(&op->ticket, &reservation_ww_class); +retry: + if (++trycnt > 100000) { + NV_PRINTK(error, cli, "%s failed and gave up.\n", __func__); + return -EINVAL; + } + + for (i = 0; i < nr_buffers; i++) { + struct drm_nouveau_gem_pushbuf_bo *b = &pbbo[i]; + struct drm_gem_object *gem; + struct nouveau_bo *nvbo; + + gem = drm_gem_object_lookup(dev, file_priv, b->handle); + if (!gem) { + NV_PRINTK(error, cli, "Unknown handle 0x%08x\n", b->handle); + ret = -ENOENT; + break; + } + nvbo = nouveau_gem_object(gem); + if (nvbo == res_bo) { + res_bo = NULL; + drm_gem_object_unreference_unlocked(gem); + continue; + } + + if (nvbo->reserved_by && nvbo->reserved_by == file_priv) { + NV_PRINTK(error, cli, "multiple instances of buffer %d on " + "validation list\n", b->handle); + drm_gem_object_unreference_unlocked(gem); + ret = -EINVAL; + break; + } + + ret = ttm_bo_reserve(&nvbo->bo, true, false, true, &op->ticket); + if (ret) { + list_splice_tail_init(&vram_list, &op->list); + list_splice_tail_init(&gart_list, &op->list); + list_splice_tail_init(&both_list, &op->list); + validate_fini_no_ticket(op, NULL, NULL); + if (unlikely(ret == -EDEADLK)) { + ret = ttm_bo_reserve_slowpath(&nvbo->bo, true, + &op->ticket); + if (!ret) + res_bo = nvbo; + } + if (unlikely(ret)) { + if (ret != -ERESTARTSYS) + NV_PRINTK(error, cli, "fail reserve\n"); + break; + } + } + + b->user_priv = (uint64_t)(unsigned long)nvbo; + nvbo->reserved_by = file_priv; + nvbo->pbbo_index = i; + if ((b->valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) && + (b->valid_domains & NOUVEAU_GEM_DOMAIN_GART)) + list_add_tail(&nvbo->entry, &both_list); + else + if (b->valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) + list_add_tail(&nvbo->entry, &vram_list); + else + if (b->valid_domains & NOUVEAU_GEM_DOMAIN_GART) + list_add_tail(&nvbo->entry, &gart_list); + else { + NV_PRINTK(error, cli, "invalid valid domains: 0x%08x\n", + b->valid_domains); + list_add_tail(&nvbo->entry, &both_list); + ret = -EINVAL; + break; + } + if (nvbo == res_bo) + goto retry; + } + + ww_acquire_done(&op->ticket); + list_splice_tail(&vram_list, &op->list); + list_splice_tail(&gart_list, &op->list); + list_splice_tail(&both_list, &op->list); + if (ret) + validate_fini(op, NULL, NULL); + return ret; + +} + +static int +validate_list(struct nouveau_channel *chan, struct nouveau_cli *cli, + struct list_head *list, struct drm_nouveau_gem_pushbuf_bo *pbbo, + uint64_t user_pbbo_ptr) +{ + struct nouveau_drm *drm = chan->drm; + struct drm_nouveau_gem_pushbuf_bo __user *upbbo = + (void __force __user *)(uintptr_t)user_pbbo_ptr; + struct nouveau_bo *nvbo; + int ret, relocs = 0; + + list_for_each_entry(nvbo, list, entry) { + struct drm_nouveau_gem_pushbuf_bo *b = &pbbo[nvbo->pbbo_index]; + + ret = nouveau_gem_set_domain(&nvbo->gem, b->read_domains, + b->write_domains, + b->valid_domains); + if (unlikely(ret)) { + NV_PRINTK(error, cli, "fail set_domain\n"); + return ret; + } + + ret = nouveau_bo_validate(nvbo, true, false); + if (unlikely(ret)) { + if (ret != -ERESTARTSYS) + NV_PRINTK(error, cli, "fail ttm_validate\n"); + return ret; + } + + ret = nouveau_fence_sync(nvbo, chan, !!b->write_domains, true); + if (unlikely(ret)) { + if (ret != -ERESTARTSYS) + NV_PRINTK(error, cli, "fail post-validate sync\n"); + return ret; + } + + if (drm->device.info.family < NV_DEVICE_INFO_V0_TESLA) { + if (nvbo->bo.offset == b->presumed.offset && + ((nvbo->bo.mem.mem_type == TTM_PL_VRAM && + b->presumed.domain & NOUVEAU_GEM_DOMAIN_VRAM) || + (nvbo->bo.mem.mem_type == TTM_PL_TT && + b->presumed.domain & NOUVEAU_GEM_DOMAIN_GART))) + continue; + + if (nvbo->bo.mem.mem_type == TTM_PL_TT) + b->presumed.domain = NOUVEAU_GEM_DOMAIN_GART; + else + b->presumed.domain = NOUVEAU_GEM_DOMAIN_VRAM; + b->presumed.offset = nvbo->bo.offset; + b->presumed.valid = 0; + relocs++; + + if (copy_to_user(&upbbo[nvbo->pbbo_index].presumed, + &b->presumed, sizeof(b->presumed))) + return -EFAULT; + } + } + + return relocs; +} + +static int +nouveau_gem_pushbuf_validate(struct nouveau_channel *chan, + struct drm_file *file_priv, + struct drm_nouveau_gem_pushbuf_bo *pbbo, + uint64_t user_buffers, int nr_buffers, + struct validate_op *op, int *apply_relocs) +{ + struct nouveau_cli *cli = nouveau_cli(file_priv); + int ret; + + INIT_LIST_HEAD(&op->list); + + if (nr_buffers == 0) + return 0; + + ret = validate_init(chan, file_priv, pbbo, nr_buffers, op); + if (unlikely(ret)) { + if (ret != -ERESTARTSYS) + NV_PRINTK(error, cli, "validate_init\n"); + return ret; + } + + ret = validate_list(chan, cli, &op->list, pbbo, user_buffers); + if (unlikely(ret < 0)) { + if (ret != -ERESTARTSYS) + NV_PRINTK(error, cli, "validating bo list\n"); + validate_fini(op, NULL, NULL); + return ret; + } + *apply_relocs = ret; + return 0; +} + +static inline void +u_free(void *addr) +{ + if (!is_vmalloc_addr(addr)) + kfree(addr); + else + vfree(addr); +} + +static inline void * +u_memcpya(uint64_t user, unsigned nmemb, unsigned size) +{ + void *mem; + void __user *userptr = (void __force __user *)(uintptr_t)user; + + size *= nmemb; + + mem = kmalloc(size, GFP_KERNEL | __GFP_NOWARN); + if (!mem) + mem = vmalloc(size); + if (!mem) + return ERR_PTR(-ENOMEM); + + if (copy_from_user(mem, userptr, size)) { + u_free(mem); + return ERR_PTR(-EFAULT); + } + + return mem; +} + +static int +nouveau_gem_pushbuf_reloc_apply(struct nouveau_cli *cli, + struct drm_nouveau_gem_pushbuf *req, + struct drm_nouveau_gem_pushbuf_bo *bo) +{ + struct drm_nouveau_gem_pushbuf_reloc *reloc = NULL; + int ret = 0; + unsigned i; + + reloc = u_memcpya(req->relocs, req->nr_relocs, sizeof(*reloc)); + if (IS_ERR(reloc)) + return PTR_ERR(reloc); + + for (i = 0; i < req->nr_relocs; i++) { + struct drm_nouveau_gem_pushbuf_reloc *r = &reloc[i]; + struct drm_nouveau_gem_pushbuf_bo *b; + struct nouveau_bo *nvbo; + uint32_t data; + + if (unlikely(r->bo_index > req->nr_buffers)) { + NV_PRINTK(error, cli, "reloc bo index invalid\n"); + ret = -EINVAL; + break; + } + + b = &bo[r->bo_index]; + if (b->presumed.valid) + continue; + + if (unlikely(r->reloc_bo_index > req->nr_buffers)) { + NV_PRINTK(error, cli, "reloc container bo index invalid\n"); + ret = -EINVAL; + break; + } + nvbo = (void *)(unsigned long)bo[r->reloc_bo_index].user_priv; + + if (unlikely(r->reloc_bo_offset + 4 > + nvbo->bo.mem.num_pages << PAGE_SHIFT)) { + NV_PRINTK(error, cli, "reloc outside of bo\n"); + ret = -EINVAL; + break; + } + + if (!nvbo->kmap.virtual) { + ret = ttm_bo_kmap(&nvbo->bo, 0, nvbo->bo.mem.num_pages, + &nvbo->kmap); + if (ret) { + NV_PRINTK(error, cli, "failed kmap for reloc\n"); + break; + } + nvbo->validate_mapped = true; + } + + if (r->flags & NOUVEAU_GEM_RELOC_LOW) + data = b->presumed.offset + r->data; + else + if (r->flags & NOUVEAU_GEM_RELOC_HIGH) + data = (b->presumed.offset + r->data) >> 32; + else + data = r->data; + + if (r->flags & NOUVEAU_GEM_RELOC_OR) { + if (b->presumed.domain == NOUVEAU_GEM_DOMAIN_GART) + data |= r->tor; + else + data |= r->vor; + } + + ret = ttm_bo_wait(&nvbo->bo, true, false, false); + if (ret) { + NV_PRINTK(error, cli, "reloc wait_idle failed: %d\n", ret); + break; + } + + nouveau_bo_wr32(nvbo, r->reloc_bo_offset >> 2, data); + } + + u_free(reloc); + return ret; +} + +int +nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv, dev); + struct nouveau_cli *cli = nouveau_cli(file_priv); + struct nouveau_abi16_chan *temp; + struct nouveau_drm *drm = nouveau_drm(dev); + struct drm_nouveau_gem_pushbuf *req = data; + struct drm_nouveau_gem_pushbuf_push *push; + struct drm_nouveau_gem_pushbuf_bo *bo; + struct nouveau_channel *chan = NULL; + struct validate_op op; + struct nouveau_fence *fence = NULL; + int i, j, ret = 0, do_reloc = 0; + + if (unlikely(!abi16)) + return -ENOMEM; + + list_for_each_entry(temp, &abi16->channels, head) { + if (temp->chan->object->handle == (NVDRM_CHAN | req->channel)) { + chan = temp->chan; + break; + } + } + + if (!chan) + return nouveau_abi16_put(abi16, -ENOENT); + + req->vram_available = drm->gem.vram_available; + req->gart_available = drm->gem.gart_available; + if (unlikely(req->nr_push == 0)) + goto out_next; + + if (unlikely(req->nr_push > NOUVEAU_GEM_MAX_PUSH)) { + NV_PRINTK(error, cli, "pushbuf push count exceeds limit: %d max %d\n", + req->nr_push, NOUVEAU_GEM_MAX_PUSH); + return nouveau_abi16_put(abi16, -EINVAL); + } + + if (unlikely(req->nr_buffers > NOUVEAU_GEM_MAX_BUFFERS)) { + NV_PRINTK(error, cli, "pushbuf bo count exceeds limit: %d max %d\n", + req->nr_buffers, NOUVEAU_GEM_MAX_BUFFERS); + return nouveau_abi16_put(abi16, -EINVAL); + } + + if (unlikely(req->nr_relocs > NOUVEAU_GEM_MAX_RELOCS)) { + NV_PRINTK(error, cli, "pushbuf reloc count exceeds limit: %d max %d\n", + req->nr_relocs, NOUVEAU_GEM_MAX_RELOCS); + return nouveau_abi16_put(abi16, -EINVAL); + } + + push = u_memcpya(req->push, req->nr_push, sizeof(*push)); + if (IS_ERR(push)) + return nouveau_abi16_put(abi16, PTR_ERR(push)); + + bo = u_memcpya(req->buffers, req->nr_buffers, sizeof(*bo)); + if (IS_ERR(bo)) { + u_free(push); + return nouveau_abi16_put(abi16, PTR_ERR(bo)); + } + + /* Ensure all push buffers are on validate list */ + for (i = 0; i < req->nr_push; i++) { + if (push[i].bo_index >= req->nr_buffers) { + NV_PRINTK(error, cli, "push %d buffer not in list\n", i); + ret = -EINVAL; + goto out_prevalid; + } + } + + /* Validate buffer list */ + ret = nouveau_gem_pushbuf_validate(chan, file_priv, bo, req->buffers, + req->nr_buffers, &op, &do_reloc); + if (ret) { + if (ret != -ERESTARTSYS) + NV_PRINTK(error, cli, "validate: %d\n", ret); + goto out_prevalid; + } + + /* Apply any relocations that are required */ + if (do_reloc) { + ret = nouveau_gem_pushbuf_reloc_apply(cli, req, bo); + if (ret) { + NV_PRINTK(error, cli, "reloc apply: %d\n", ret); + goto out; + } + } + + if (chan->dma.ib_max) { + ret = nouveau_dma_wait(chan, req->nr_push + 1, 16); + if (ret) { + NV_PRINTK(error, cli, "nv50cal_space: %d\n", ret); + goto out; + } + + for (i = 0; i < req->nr_push; i++) { + struct nouveau_bo *nvbo = (void *)(unsigned long) + bo[push[i].bo_index].user_priv; + + nv50_dma_push(chan, nvbo, push[i].offset, + push[i].length); + } + } else + if (drm->device.info.chipset >= 0x25) { + ret = RING_SPACE(chan, req->nr_push * 2); + if (ret) { + NV_PRINTK(error, cli, "cal_space: %d\n", ret); + goto out; + } + + for (i = 0; i < req->nr_push; i++) { + struct nouveau_bo *nvbo = (void *)(unsigned long) + bo[push[i].bo_index].user_priv; + + OUT_RING(chan, (nvbo->bo.offset + push[i].offset) | 2); + OUT_RING(chan, 0); + } + } else { + ret = RING_SPACE(chan, req->nr_push * (2 + NOUVEAU_DMA_SKIPS)); + if (ret) { + NV_PRINTK(error, cli, "jmp_space: %d\n", ret); + goto out; + } + + for (i = 0; i < req->nr_push; i++) { + struct nouveau_bo *nvbo = (void *)(unsigned long) + bo[push[i].bo_index].user_priv; + uint32_t cmd; + + cmd = chan->push.vma.offset + ((chan->dma.cur + 2) << 2); + cmd |= 0x20000000; + if (unlikely(cmd != req->suffix0)) { + if (!nvbo->kmap.virtual) { + ret = ttm_bo_kmap(&nvbo->bo, 0, + nvbo->bo.mem. + num_pages, + &nvbo->kmap); + if (ret) { + WIND_RING(chan); + goto out; + } + nvbo->validate_mapped = true; + } + + nouveau_bo_wr32(nvbo, (push[i].offset + + push[i].length - 8) / 4, cmd); + } + + OUT_RING(chan, 0x20000000 | + (nvbo->bo.offset + push[i].offset)); + OUT_RING(chan, 0); + for (j = 0; j < NOUVEAU_DMA_SKIPS; j++) + OUT_RING(chan, 0); + } + } + + ret = nouveau_fence_new(chan, false, &fence); + if (ret) { + NV_PRINTK(error, cli, "error fencing pushbuf: %d\n", ret); + WIND_RING(chan); + goto out; + } + +out: + validate_fini(&op, fence, bo); + nouveau_fence_unref(&fence); + +out_prevalid: + u_free(bo); + u_free(push); + +out_next: + if (chan->dma.ib_max) { + req->suffix0 = 0x00000000; + req->suffix1 = 0x00000000; + } else + if (drm->device.info.chipset >= 0x25) { + req->suffix0 = 0x00020000; + req->suffix1 = 0x00000000; + } else { + req->suffix0 = 0x20000000 | + (chan->push.vma.offset + ((chan->dma.cur + 2) << 2)); + req->suffix1 = 0x00000000; + } + + return nouveau_abi16_put(abi16, ret); +} + +int +nouveau_gem_ioctl_cpu_prep(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_nouveau_gem_cpu_prep *req = data; + struct drm_gem_object *gem; + struct nouveau_bo *nvbo; + bool no_wait = !!(req->flags & NOUVEAU_GEM_CPU_PREP_NOWAIT); + bool write = !!(req->flags & NOUVEAU_GEM_CPU_PREP_WRITE); + int ret; + + gem = drm_gem_object_lookup(dev, file_priv, req->handle); + if (!gem) + return -ENOENT; + nvbo = nouveau_gem_object(gem); + + if (no_wait) + ret = reservation_object_test_signaled_rcu(nvbo->bo.resv, write) ? 0 : -EBUSY; + else { + long lret; + + lret = reservation_object_wait_timeout_rcu(nvbo->bo.resv, write, true, 30 * HZ); + if (!lret) + ret = -EBUSY; + else if (lret > 0) + ret = 0; + else + ret = lret; + } + nouveau_bo_sync_for_cpu(nvbo); + drm_gem_object_unreference_unlocked(gem); + + return ret; +} + +int +nouveau_gem_ioctl_cpu_fini(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_nouveau_gem_cpu_fini *req = data; + struct drm_gem_object *gem; + struct nouveau_bo *nvbo; + + gem = drm_gem_object_lookup(dev, file_priv, req->handle); + if (!gem) + return -ENOENT; + nvbo = nouveau_gem_object(gem); + + nouveau_bo_sync_for_device(nvbo); + drm_gem_object_unreference_unlocked(gem); + return 0; +} + +int +nouveau_gem_ioctl_info(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_nouveau_gem_info *req = data; + struct drm_gem_object *gem; + int ret; + + gem = drm_gem_object_lookup(dev, file_priv, req->handle); + if (!gem) + return -ENOENT; + + ret = nouveau_gem_info(file_priv, gem, req); + drm_gem_object_unreference_unlocked(gem); + return ret; +} + diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_gem.h b/kernel/drivers/gpu/drm/nouveau/nouveau_gem.h new file mode 100644 index 000000000..e4049faca --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_gem.h @@ -0,0 +1,46 @@ +#ifndef __NOUVEAU_GEM_H__ +#define __NOUVEAU_GEM_H__ + +#include + +#include "nouveau_drm.h" +#include "nouveau_bo.h" + +#define nouveau_bo_tile_layout(nvbo) \ + ((nvbo)->tile_flags & NOUVEAU_GEM_TILE_LAYOUT_MASK) + +static inline struct nouveau_bo * +nouveau_gem_object(struct drm_gem_object *gem) +{ + return gem ? container_of(gem, struct nouveau_bo, gem) : NULL; +} + +/* nouveau_gem.c */ +extern int nouveau_gem_new(struct drm_device *, int size, int align, + uint32_t domain, uint32_t tile_mode, + uint32_t tile_flags, struct nouveau_bo **); +extern void nouveau_gem_object_del(struct drm_gem_object *); +extern int nouveau_gem_object_open(struct drm_gem_object *, struct drm_file *); +extern void nouveau_gem_object_close(struct drm_gem_object *, + struct drm_file *); +extern int nouveau_gem_ioctl_new(struct drm_device *, void *, + struct drm_file *); +extern int nouveau_gem_ioctl_pushbuf(struct drm_device *, void *, + struct drm_file *); +extern int nouveau_gem_ioctl_cpu_prep(struct drm_device *, void *, + struct drm_file *); +extern int nouveau_gem_ioctl_cpu_fini(struct drm_device *, void *, + struct drm_file *); +extern int nouveau_gem_ioctl_info(struct drm_device *, void *, + struct drm_file *); + +extern int nouveau_gem_prime_pin(struct drm_gem_object *); +struct reservation_object *nouveau_gem_prime_res_obj(struct drm_gem_object *); +extern void nouveau_gem_prime_unpin(struct drm_gem_object *); +extern struct sg_table *nouveau_gem_prime_get_sg_table(struct drm_gem_object *); +extern struct drm_gem_object *nouveau_gem_prime_import_sg_table( + struct drm_device *, struct dma_buf_attachment *, struct sg_table *); +extern void *nouveau_gem_prime_vmap(struct drm_gem_object *); +extern void nouveau_gem_prime_vunmap(struct drm_gem_object *, void *); + +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_hwmon.c b/kernel/drivers/gpu/drm/nouveau/nouveau_hwmon.c new file mode 100644 index 000000000..0dbe0060f --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_hwmon.c @@ -0,0 +1,648 @@ +/* + * 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 + */ + +#ifdef CONFIG_ACPI +#include +#endif +#include +#include +#include + +#include + +#include "nouveau_drm.h" +#include "nouveau_hwmon.h" + +#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE)) +static ssize_t +nouveau_hwmon_show_temp(struct device *d, struct device_attribute *a, char *buf) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_therm *therm = nvxx_therm(&drm->device); + int temp = therm->temp_get(therm); + + if (temp < 0) + return temp; + + return snprintf(buf, PAGE_SIZE, "%d\n", temp * 1000); +} +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, nouveau_hwmon_show_temp, + NULL, 0); + +static ssize_t +nouveau_hwmon_show_temp1_auto_point1_pwm(struct device *d, + struct device_attribute *a, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", 100); +} +static SENSOR_DEVICE_ATTR(temp1_auto_point1_pwm, S_IRUGO, + nouveau_hwmon_show_temp1_auto_point1_pwm, NULL, 0); + +static ssize_t +nouveau_hwmon_temp1_auto_point1_temp(struct device *d, + struct device_attribute *a, char *buf) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_therm *therm = nvxx_therm(&drm->device); + + return snprintf(buf, PAGE_SIZE, "%d\n", + therm->attr_get(therm, NVKM_THERM_ATTR_THRS_FAN_BOOST) * 1000); +} +static ssize_t +nouveau_hwmon_set_temp1_auto_point1_temp(struct device *d, + struct device_attribute *a, + const char *buf, size_t count) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_therm *therm = nvxx_therm(&drm->device); + long value; + + if (kstrtol(buf, 10, &value) == -EINVAL) + return count; + + therm->attr_set(therm, NVKM_THERM_ATTR_THRS_FAN_BOOST, + value / 1000); + + return count; +} +static SENSOR_DEVICE_ATTR(temp1_auto_point1_temp, S_IRUGO | S_IWUSR, + nouveau_hwmon_temp1_auto_point1_temp, + nouveau_hwmon_set_temp1_auto_point1_temp, 0); + +static ssize_t +nouveau_hwmon_temp1_auto_point1_temp_hyst(struct device *d, + struct device_attribute *a, char *buf) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_therm *therm = nvxx_therm(&drm->device); + + return snprintf(buf, PAGE_SIZE, "%d\n", + therm->attr_get(therm, NVKM_THERM_ATTR_THRS_FAN_BOOST_HYST) * 1000); +} +static ssize_t +nouveau_hwmon_set_temp1_auto_point1_temp_hyst(struct device *d, + struct device_attribute *a, + const char *buf, size_t count) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_therm *therm = nvxx_therm(&drm->device); + long value; + + if (kstrtol(buf, 10, &value) == -EINVAL) + return count; + + therm->attr_set(therm, NVKM_THERM_ATTR_THRS_FAN_BOOST_HYST, + value / 1000); + + return count; +} +static SENSOR_DEVICE_ATTR(temp1_auto_point1_temp_hyst, S_IRUGO | S_IWUSR, + nouveau_hwmon_temp1_auto_point1_temp_hyst, + nouveau_hwmon_set_temp1_auto_point1_temp_hyst, 0); + +static ssize_t +nouveau_hwmon_max_temp(struct device *d, struct device_attribute *a, char *buf) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_therm *therm = nvxx_therm(&drm->device); + + return snprintf(buf, PAGE_SIZE, "%d\n", + therm->attr_get(therm, NVKM_THERM_ATTR_THRS_DOWN_CLK) * 1000); +} +static ssize_t +nouveau_hwmon_set_max_temp(struct device *d, struct device_attribute *a, + const char *buf, size_t count) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_therm *therm = nvxx_therm(&drm->device); + long value; + + if (kstrtol(buf, 10, &value) == -EINVAL) + return count; + + therm->attr_set(therm, NVKM_THERM_ATTR_THRS_DOWN_CLK, value / 1000); + + return count; +} +static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, nouveau_hwmon_max_temp, + nouveau_hwmon_set_max_temp, + 0); + +static ssize_t +nouveau_hwmon_max_temp_hyst(struct device *d, struct device_attribute *a, + char *buf) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_therm *therm = nvxx_therm(&drm->device); + + return snprintf(buf, PAGE_SIZE, "%d\n", + therm->attr_get(therm, NVKM_THERM_ATTR_THRS_DOWN_CLK_HYST) * 1000); +} +static ssize_t +nouveau_hwmon_set_max_temp_hyst(struct device *d, struct device_attribute *a, + const char *buf, size_t count) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_therm *therm = nvxx_therm(&drm->device); + long value; + + if (kstrtol(buf, 10, &value) == -EINVAL) + return count; + + therm->attr_set(therm, NVKM_THERM_ATTR_THRS_DOWN_CLK_HYST, + value / 1000); + + return count; +} +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR, + nouveau_hwmon_max_temp_hyst, + nouveau_hwmon_set_max_temp_hyst, 0); + +static ssize_t +nouveau_hwmon_critical_temp(struct device *d, struct device_attribute *a, + char *buf) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_therm *therm = nvxx_therm(&drm->device); + + return snprintf(buf, PAGE_SIZE, "%d\n", + therm->attr_get(therm, NVKM_THERM_ATTR_THRS_CRITICAL) * 1000); +} +static ssize_t +nouveau_hwmon_set_critical_temp(struct device *d, struct device_attribute *a, + const char *buf, + size_t count) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_therm *therm = nvxx_therm(&drm->device); + long value; + + if (kstrtol(buf, 10, &value) == -EINVAL) + return count; + + therm->attr_set(therm, NVKM_THERM_ATTR_THRS_CRITICAL, value / 1000); + + return count; +} +static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO | S_IWUSR, + nouveau_hwmon_critical_temp, + nouveau_hwmon_set_critical_temp, + 0); + +static ssize_t +nouveau_hwmon_critical_temp_hyst(struct device *d, struct device_attribute *a, + char *buf) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_therm *therm = nvxx_therm(&drm->device); + + return snprintf(buf, PAGE_SIZE, "%d\n", + therm->attr_get(therm, NVKM_THERM_ATTR_THRS_CRITICAL_HYST) * 1000); +} +static ssize_t +nouveau_hwmon_set_critical_temp_hyst(struct device *d, + struct device_attribute *a, + const char *buf, + size_t count) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_therm *therm = nvxx_therm(&drm->device); + long value; + + if (kstrtol(buf, 10, &value) == -EINVAL) + return count; + + therm->attr_set(therm, NVKM_THERM_ATTR_THRS_CRITICAL_HYST, + value / 1000); + + return count; +} +static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO | S_IWUSR, + nouveau_hwmon_critical_temp_hyst, + nouveau_hwmon_set_critical_temp_hyst, 0); +static ssize_t +nouveau_hwmon_emergency_temp(struct device *d, struct device_attribute *a, + char *buf) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_therm *therm = nvxx_therm(&drm->device); + + return snprintf(buf, PAGE_SIZE, "%d\n", + therm->attr_get(therm, NVKM_THERM_ATTR_THRS_SHUTDOWN) * 1000); +} +static ssize_t +nouveau_hwmon_set_emergency_temp(struct device *d, struct device_attribute *a, + const char *buf, + size_t count) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_therm *therm = nvxx_therm(&drm->device); + long value; + + if (kstrtol(buf, 10, &value) == -EINVAL) + return count; + + therm->attr_set(therm, NVKM_THERM_ATTR_THRS_SHUTDOWN, value / 1000); + + return count; +} +static SENSOR_DEVICE_ATTR(temp1_emergency, S_IRUGO | S_IWUSR, + nouveau_hwmon_emergency_temp, + nouveau_hwmon_set_emergency_temp, + 0); + +static ssize_t +nouveau_hwmon_emergency_temp_hyst(struct device *d, struct device_attribute *a, + char *buf) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_therm *therm = nvxx_therm(&drm->device); + + return snprintf(buf, PAGE_SIZE, "%d\n", + therm->attr_get(therm, NVKM_THERM_ATTR_THRS_SHUTDOWN_HYST) * 1000); +} +static ssize_t +nouveau_hwmon_set_emergency_temp_hyst(struct device *d, + struct device_attribute *a, + const char *buf, + size_t count) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_therm *therm = nvxx_therm(&drm->device); + long value; + + if (kstrtol(buf, 10, &value) == -EINVAL) + return count; + + therm->attr_set(therm, NVKM_THERM_ATTR_THRS_SHUTDOWN_HYST, + value / 1000); + + return count; +} +static SENSOR_DEVICE_ATTR(temp1_emergency_hyst, S_IRUGO | S_IWUSR, + nouveau_hwmon_emergency_temp_hyst, + nouveau_hwmon_set_emergency_temp_hyst, + 0); + +static ssize_t nouveau_hwmon_show_name(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "nouveau\n"); +} +static SENSOR_DEVICE_ATTR(name, S_IRUGO, nouveau_hwmon_show_name, NULL, 0); + +static ssize_t nouveau_hwmon_show_update_rate(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "1000\n"); +} +static SENSOR_DEVICE_ATTR(update_rate, S_IRUGO, + nouveau_hwmon_show_update_rate, + NULL, 0); + +static ssize_t +nouveau_hwmon_show_fan1_input(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_therm *therm = nvxx_therm(&drm->device); + + return snprintf(buf, PAGE_SIZE, "%d\n", therm->fan_sense(therm)); +} +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, nouveau_hwmon_show_fan1_input, + NULL, 0); + + static ssize_t +nouveau_hwmon_get_pwm1_enable(struct device *d, + struct device_attribute *a, char *buf) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_therm *therm = nvxx_therm(&drm->device); + int ret; + + ret = therm->attr_get(therm, NVKM_THERM_ATTR_FAN_MODE); + if (ret < 0) + return ret; + + return sprintf(buf, "%i\n", ret); +} + +static ssize_t +nouveau_hwmon_set_pwm1_enable(struct device *d, struct device_attribute *a, + const char *buf, size_t count) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_therm *therm = nvxx_therm(&drm->device); + long value; + int ret; + + ret = kstrtol(buf, 10, &value); + if (ret) + return ret; + + ret = therm->attr_set(therm, NVKM_THERM_ATTR_FAN_MODE, value); + if (ret) + return ret; + else + return count; +} +static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, + nouveau_hwmon_get_pwm1_enable, + nouveau_hwmon_set_pwm1_enable, 0); + +static ssize_t +nouveau_hwmon_get_pwm1(struct device *d, struct device_attribute *a, char *buf) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_therm *therm = nvxx_therm(&drm->device); + int ret; + + ret = therm->fan_get(therm); + if (ret < 0) + return ret; + + return sprintf(buf, "%i\n", ret); +} + +static ssize_t +nouveau_hwmon_set_pwm1(struct device *d, struct device_attribute *a, + const char *buf, size_t count) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_therm *therm = nvxx_therm(&drm->device); + int ret = -ENODEV; + long value; + + if (kstrtol(buf, 10, &value) == -EINVAL) + return -EINVAL; + + ret = therm->fan_set(therm, value); + if (ret) + return ret; + + return count; +} + +static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, + nouveau_hwmon_get_pwm1, + nouveau_hwmon_set_pwm1, 0); + +static ssize_t +nouveau_hwmon_get_pwm1_min(struct device *d, + struct device_attribute *a, char *buf) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_therm *therm = nvxx_therm(&drm->device); + int ret; + + ret = therm->attr_get(therm, NVKM_THERM_ATTR_FAN_MIN_DUTY); + if (ret < 0) + return ret; + + return sprintf(buf, "%i\n", ret); +} + +static ssize_t +nouveau_hwmon_set_pwm1_min(struct device *d, struct device_attribute *a, + const char *buf, size_t count) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_therm *therm = nvxx_therm(&drm->device); + long value; + int ret; + + if (kstrtol(buf, 10, &value) == -EINVAL) + return -EINVAL; + + ret = therm->attr_set(therm, NVKM_THERM_ATTR_FAN_MIN_DUTY, value); + if (ret < 0) + return ret; + + return count; +} + +static SENSOR_DEVICE_ATTR(pwm1_min, S_IRUGO | S_IWUSR, + nouveau_hwmon_get_pwm1_min, + nouveau_hwmon_set_pwm1_min, 0); + +static ssize_t +nouveau_hwmon_get_pwm1_max(struct device *d, + struct device_attribute *a, char *buf) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_therm *therm = nvxx_therm(&drm->device); + int ret; + + ret = therm->attr_get(therm, NVKM_THERM_ATTR_FAN_MAX_DUTY); + if (ret < 0) + return ret; + + return sprintf(buf, "%i\n", ret); +} + +static ssize_t +nouveau_hwmon_set_pwm1_max(struct device *d, struct device_attribute *a, + const char *buf, size_t count) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_therm *therm = nvxx_therm(&drm->device); + long value; + int ret; + + if (kstrtol(buf, 10, &value) == -EINVAL) + return -EINVAL; + + ret = therm->attr_set(therm, NVKM_THERM_ATTR_FAN_MAX_DUTY, value); + if (ret < 0) + return ret; + + return count; +} + +static SENSOR_DEVICE_ATTR(pwm1_max, S_IRUGO | S_IWUSR, + nouveau_hwmon_get_pwm1_max, + nouveau_hwmon_set_pwm1_max, 0); + +static struct attribute *hwmon_default_attributes[] = { + &sensor_dev_attr_name.dev_attr.attr, + &sensor_dev_attr_update_rate.dev_attr.attr, + NULL +}; +static struct attribute *hwmon_temp_attributes[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_temp1_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_temp1_auto_point1_temp_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_emergency.dev_attr.attr, + &sensor_dev_attr_temp1_emergency_hyst.dev_attr.attr, + NULL +}; +static struct attribute *hwmon_fan_rpm_attributes[] = { + &sensor_dev_attr_fan1_input.dev_attr.attr, + NULL +}; +static struct attribute *hwmon_pwm_fan_attributes[] = { + &sensor_dev_attr_pwm1_enable.dev_attr.attr, + &sensor_dev_attr_pwm1.dev_attr.attr, + &sensor_dev_attr_pwm1_min.dev_attr.attr, + &sensor_dev_attr_pwm1_max.dev_attr.attr, + NULL +}; + +static const struct attribute_group hwmon_default_attrgroup = { + .attrs = hwmon_default_attributes, +}; +static const struct attribute_group hwmon_temp_attrgroup = { + .attrs = hwmon_temp_attributes, +}; +static const struct attribute_group hwmon_fan_rpm_attrgroup = { + .attrs = hwmon_fan_rpm_attributes, +}; +static const struct attribute_group hwmon_pwm_fan_attrgroup = { + .attrs = hwmon_pwm_fan_attributes, +}; +#endif + +int +nouveau_hwmon_init(struct drm_device *dev) +{ +#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE)) + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_therm *therm = nvxx_therm(&drm->device); + struct nouveau_hwmon *hwmon; + struct device *hwmon_dev; + int ret = 0; + + hwmon = drm->hwmon = kzalloc(sizeof(*hwmon), GFP_KERNEL); + if (!hwmon) + return -ENOMEM; + hwmon->dev = dev; + + if (!therm || !therm->temp_get || !therm->attr_get || !therm->attr_set) + return -ENODEV; + + hwmon_dev = hwmon_device_register(&dev->pdev->dev); + if (IS_ERR(hwmon_dev)) { + ret = PTR_ERR(hwmon_dev); + NV_ERROR(drm, "Unable to register hwmon device: %d\n", ret); + return ret; + } + dev_set_drvdata(hwmon_dev, dev); + + /* set the default attributes */ + ret = sysfs_create_group(&hwmon_dev->kobj, &hwmon_default_attrgroup); + if (ret) + goto error; + + /* if the card has a working thermal sensor */ + if (therm->temp_get(therm) >= 0) { + ret = sysfs_create_group(&hwmon_dev->kobj, &hwmon_temp_attrgroup); + if (ret) + goto error; + } + + /* if the card has a pwm fan */ + /*XXX: incorrect, need better detection for this, some boards have + * the gpio entries for pwm fan control even when there's no + * actual fan connected to it... therm table? */ + if (therm->fan_get && therm->fan_get(therm) >= 0) { + ret = sysfs_create_group(&hwmon_dev->kobj, + &hwmon_pwm_fan_attrgroup); + if (ret) + goto error; + } + + /* if the card can read the fan rpm */ + if (therm->fan_sense(therm) >= 0) { + ret = sysfs_create_group(&hwmon_dev->kobj, + &hwmon_fan_rpm_attrgroup); + if (ret) + goto error; + } + + hwmon->hwmon = hwmon_dev; + + return 0; + +error: + NV_ERROR(drm, "Unable to create some hwmon sysfs files: %d\n", ret); + hwmon_device_unregister(hwmon_dev); + hwmon->hwmon = NULL; + return ret; +#else + return 0; +#endif +} + +void +nouveau_hwmon_fini(struct drm_device *dev) +{ +#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE)) + struct nouveau_hwmon *hwmon = nouveau_hwmon(dev); + + if (hwmon->hwmon) { + sysfs_remove_group(&hwmon->hwmon->kobj, &hwmon_default_attrgroup); + sysfs_remove_group(&hwmon->hwmon->kobj, &hwmon_temp_attrgroup); + sysfs_remove_group(&hwmon->hwmon->kobj, &hwmon_pwm_fan_attrgroup); + sysfs_remove_group(&hwmon->hwmon->kobj, &hwmon_fan_rpm_attrgroup); + + hwmon_device_unregister(hwmon->hwmon); + } + + nouveau_drm(dev)->hwmon = NULL; + kfree(hwmon); +#endif +} diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_hwmon.h b/kernel/drivers/gpu/drm/nouveau/nouveau_hwmon.h new file mode 100644 index 000000000..62ccbb398 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_hwmon.h @@ -0,0 +1,43 @@ +/* + * Copyright 2010 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +#ifndef __NOUVEAU_PM_H__ +#define __NOUVEAU_PM_H__ + +struct nouveau_hwmon { + struct drm_device *dev; + struct device *hwmon; +}; + +static inline struct nouveau_hwmon * +nouveau_hwmon(struct drm_device *dev) +{ + return nouveau_drm(dev)->hwmon; +} + +/* nouveau_hwmon.c */ +int nouveau_hwmon_init(struct drm_device *dev); +void nouveau_hwmon_fini(struct drm_device *dev); + +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_ioc32.c b/kernel/drivers/gpu/drm/nouveau/nouveau_ioc32.c new file mode 100644 index 000000000..462679a8f --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_ioc32.c @@ -0,0 +1,69 @@ +/** + * \file mga_ioc32.c + * + * 32-bit ioctl compatibility routines for the MGA DRM. + * + * \author Dave Airlie with code from patches by Egbert Eich + * + * + * Copyright (C) Paul Mackerras 2005 + * Copyright (C) Egbert Eich 2003,2004 + * Copyright (C) Dave Airlie 2005 + * 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 AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION 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 "nouveau_ioctl.h" + +/** + * Called whenever a 32-bit process running under a 64-bit kernel + * performs an ioctl on /dev/dri/card. + * + * \param filp file pointer. + * \param cmd command. + * \param arg user argument. + * \return zero on success or negative number on failure. + */ +long nouveau_compat_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + unsigned int nr = DRM_IOCTL_NR(cmd); + drm_ioctl_compat_t *fn = NULL; + int ret; + + if (nr < DRM_COMMAND_BASE) + return drm_compat_ioctl(filp, cmd, arg); + +#if 0 + if (nr < DRM_COMMAND_BASE + ARRAY_SIZE(mga_compat_ioctls)) + fn = nouveau_compat_ioctls[nr - DRM_COMMAND_BASE]; +#endif + if (fn != NULL) + ret = (*fn)(filp, cmd, arg); + else + ret = nouveau_drm_ioctl(filp, cmd, arg); + + return ret; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_ioctl.h b/kernel/drivers/gpu/drm/nouveau/nouveau_ioctl.h new file mode 100644 index 000000000..3b9f2e546 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_ioctl.h @@ -0,0 +1,7 @@ +#ifndef __NOUVEAU_IOCTL_H__ +#define __NOUVEAU_IOCTL_H__ + +long nouveau_compat_ioctl(struct file *, unsigned int cmd, unsigned long arg); +long nouveau_drm_ioctl(struct file *, unsigned int cmd, unsigned long arg); + +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_nvif.c b/kernel/drivers/gpu/drm/nouveau/nouveau_nvif.c new file mode 100644 index 000000000..ca0ad9d15 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_nvif.c @@ -0,0 +1,136 @@ +/* + * 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 + */ + +/******************************************************************************* + * NVIF client driver - NVKM directly linked + ******************************************************************************/ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "nouveau_drm.h" +#include "nouveau_usif.h" + +static void +nvkm_client_unmap(void *priv, void __iomem *ptr, u32 size) +{ + iounmap(ptr); +} + +static void __iomem * +nvkm_client_map(void *priv, u64 handle, u32 size) +{ + return ioremap(handle, size); +} + +static int +nvkm_client_ioctl(void *priv, bool super, void *data, u32 size, void **hack) +{ + return nvkm_ioctl(priv, super, data, size, hack); +} + +static int +nvkm_client_resume(void *priv) +{ + return nvkm_client_init(priv); +} + +static int +nvkm_client_suspend(void *priv) +{ + return nvkm_client_fini(priv, true); +} + +static void +nvkm_client_driver_fini(void *priv) +{ + struct nvkm_object *client = priv; + nvkm_client_fini(nv_client(client), false); + atomic_set(&client->refcount, 1); + nvkm_object_ref(NULL, &client); +} + +static int +nvkm_client_ntfy(const void *header, u32 length, const void *data, u32 size) +{ + const union { + struct nvif_notify_req_v0 v0; + } *args = header; + u8 route; + + if (length == sizeof(args->v0) && args->v0.version == 0) { + route = args->v0.route; + } else { + WARN_ON(1); + return NVKM_NOTIFY_DROP; + } + + switch (route) { + case NVDRM_NOTIFY_NVIF: + return nvif_notify(header, length, data, size); + case NVDRM_NOTIFY_USIF: + return usif_notify(header, length, data, size); + default: + WARN_ON(1); + break; + } + + return NVKM_NOTIFY_DROP; +} + +static int +nvkm_client_driver_init(const char *name, u64 device, const char *cfg, + const char *dbg, void **ppriv) +{ + struct nvkm_client *client; + int ret; + + ret = nvkm_client_create(name, device, cfg, dbg, &client); + *ppriv = client; + if (ret) + return ret; + + client->ntfy = nvkm_client_ntfy; + return 0; +} + +const struct nvif_driver +nvif_driver_nvkm = { + .name = "nvkm", + .init = nvkm_client_driver_init, + .fini = nvkm_client_driver_fini, + .suspend = nvkm_client_suspend, + .resume = nvkm_client_resume, + .ioctl = nvkm_client_ioctl, + .map = nvkm_client_map, + .unmap = nvkm_client_unmap, + .keep = false, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_platform.c b/kernel/drivers/gpu/drm/nouveau/nouveau_platform.c new file mode 100644 index 000000000..775277f1e --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_platform.c @@ -0,0 +1,252 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nouveau_drm.h" +#include "nouveau_platform.h" + +static int nouveau_platform_power_up(struct nouveau_platform_gpu *gpu) +{ + int err; + + err = regulator_enable(gpu->vdd); + if (err) + goto err_power; + + err = clk_prepare_enable(gpu->clk); + if (err) + goto err_clk; + err = clk_prepare_enable(gpu->clk_pwr); + if (err) + goto err_clk_pwr; + clk_set_rate(gpu->clk_pwr, 204000000); + udelay(10); + + reset_control_assert(gpu->rst); + udelay(10); + + err = tegra_powergate_remove_clamping(TEGRA_POWERGATE_3D); + if (err) + goto err_clamp; + udelay(10); + + reset_control_deassert(gpu->rst); + udelay(10); + + return 0; + +err_clamp: + clk_disable_unprepare(gpu->clk_pwr); +err_clk_pwr: + clk_disable_unprepare(gpu->clk); +err_clk: + regulator_disable(gpu->vdd); +err_power: + return err; +} + +static int nouveau_platform_power_down(struct nouveau_platform_gpu *gpu) +{ + int err; + + reset_control_assert(gpu->rst); + udelay(10); + + clk_disable_unprepare(gpu->clk_pwr); + clk_disable_unprepare(gpu->clk); + udelay(10); + + err = regulator_disable(gpu->vdd); + if (err) + return err; + + return 0; +} + +static void nouveau_platform_probe_iommu(struct device *dev, + struct nouveau_platform_gpu *gpu) +{ + int err; + unsigned long pgsize_bitmap; + + mutex_init(&gpu->iommu.mutex); + + if (iommu_present(&platform_bus_type)) { + gpu->iommu.domain = iommu_domain_alloc(&platform_bus_type); + if (IS_ERR(gpu->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 = gpu->iommu.domain->ops->pgsize_bitmap; + if (pgsize_bitmap & PAGE_SIZE) { + gpu->iommu.pgshift = PAGE_SHIFT; + } else { + gpu->iommu.pgshift = fls(pgsize_bitmap & ~PAGE_MASK); + if (gpu->iommu.pgshift == 0) { + dev_warn(dev, "unsupported IOMMU page size\n"); + goto free_domain; + } + gpu->iommu.pgshift -= 1; + } + + err = iommu_attach_device(gpu->iommu.domain, dev); + if (err) + goto free_domain; + + err = nvkm_mm_init(&gpu->iommu._mm, 0, + (1ULL << 40) >> gpu->iommu.pgshift, 1); + if (err) + goto detach_device; + + gpu->iommu.mm = &gpu->iommu._mm; + } + + return; + +detach_device: + iommu_detach_device(gpu->iommu.domain, dev); + +free_domain: + iommu_domain_free(gpu->iommu.domain); + +error: + gpu->iommu.domain = NULL; + gpu->iommu.pgshift = 0; + dev_err(dev, "cannot initialize IOMMU MM\n"); +} + +static void nouveau_platform_remove_iommu(struct device *dev, + struct nouveau_platform_gpu *gpu) +{ + if (gpu->iommu.domain) { + nvkm_mm_fini(&gpu->iommu._mm); + iommu_detach_device(gpu->iommu.domain, dev); + iommu_domain_free(gpu->iommu.domain); + } +} + +static int nouveau_platform_probe(struct platform_device *pdev) +{ + struct nouveau_platform_gpu *gpu; + struct nouveau_platform_device *device; + struct drm_device *drm; + int err; + + gpu = devm_kzalloc(&pdev->dev, sizeof(*gpu), GFP_KERNEL); + if (!gpu) + return -ENOMEM; + + gpu->vdd = devm_regulator_get(&pdev->dev, "vdd"); + if (IS_ERR(gpu->vdd)) + return PTR_ERR(gpu->vdd); + + gpu->rst = devm_reset_control_get(&pdev->dev, "gpu"); + if (IS_ERR(gpu->rst)) + return PTR_ERR(gpu->rst); + + gpu->clk = devm_clk_get(&pdev->dev, "gpu"); + if (IS_ERR(gpu->clk)) + return PTR_ERR(gpu->clk); + + gpu->clk_pwr = devm_clk_get(&pdev->dev, "pwr"); + if (IS_ERR(gpu->clk_pwr)) + return PTR_ERR(gpu->clk_pwr); + + nouveau_platform_probe_iommu(&pdev->dev, gpu); + + err = nouveau_platform_power_up(gpu); + if (err) + return err; + + drm = nouveau_platform_device_create(pdev, &device); + if (IS_ERR(drm)) { + err = PTR_ERR(drm); + goto power_down; + } + + device->gpu = gpu; + device->gpu_speedo = tegra_sku_info.gpu_speedo_value; + + err = drm_dev_register(drm, 0); + if (err < 0) + goto err_unref; + + return 0; + +err_unref: + drm_dev_unref(drm); + +power_down: + nouveau_platform_power_down(gpu); + nouveau_platform_remove_iommu(&pdev->dev, gpu); + + return err; +} + +static int nouveau_platform_remove(struct platform_device *pdev) +{ + struct drm_device *drm_dev = platform_get_drvdata(pdev); + struct nouveau_drm *drm = nouveau_drm(drm_dev); + struct nvkm_device *device = nvxx_device(&drm->device); + struct nouveau_platform_gpu *gpu = nv_device_to_platform(device)->gpu; + int err; + + nouveau_drm_device_remove(drm_dev); + + err = nouveau_platform_power_down(gpu); + + nouveau_platform_remove_iommu(&pdev->dev, gpu); + + return err; +} + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id nouveau_platform_match[] = { + { .compatible = "nvidia,gk20a" }, + { } +}; + +MODULE_DEVICE_TABLE(of, nouveau_platform_match); +#endif + +struct platform_driver nouveau_platform_driver = { + .driver = { + .name = "nouveau", + .of_match_table = of_match_ptr(nouveau_platform_match), + }, + .probe = nouveau_platform_probe, + .remove = nouveau_platform_remove, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_platform.h b/kernel/drivers/gpu/drm/nouveau/nouveau_platform.h new file mode 100644 index 000000000..392874cf4 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_platform.h @@ -0,0 +1,72 @@ +/* + * 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. + */ + +#ifndef __NOUVEAU_PLATFORM_H__ +#define __NOUVEAU_PLATFORM_H__ + +#include "core/device.h" +#include "core/mm.h" + +struct reset_control; +struct clk; +struct regulator; +struct iommu_domain; +struct platform_driver; + +struct nouveau_platform_gpu { + struct reset_control *rst; + struct clk *clk; + struct clk *clk_pwr; + + struct regulator *vdd; + + struct { + /* + * Protects accesses to mm from subsystems + */ + struct mutex mutex; + + struct nvkm_mm _mm; + /* + * Just points to _mm. We need this to avoid embedding + * struct nvkm_mm in os.h + */ + struct nvkm_mm *mm; + struct iommu_domain *domain; + unsigned long pgshift; + } iommu; +}; + +struct nouveau_platform_device { + struct nvkm_device device; + + struct nouveau_platform_gpu *gpu; + + int gpu_speedo; +}; + +#define nv_device_to_platform(d) \ + container_of(d, struct nouveau_platform_device, device) + +extern struct platform_driver nouveau_platform_driver; + +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_prime.c b/kernel/drivers/gpu/drm/nouveau/nouveau_prime.c new file mode 100644 index 000000000..dd32ad6db --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_prime.c @@ -0,0 +1,115 @@ +/* + * 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: Dave Airlie + */ + +#include +#include + +#include "nouveau_drm.h" +#include "nouveau_gem.h" + +struct sg_table *nouveau_gem_prime_get_sg_table(struct drm_gem_object *obj) +{ + struct nouveau_bo *nvbo = nouveau_gem_object(obj); + int npages = nvbo->bo.num_pages; + + return drm_prime_pages_to_sg(nvbo->bo.ttm->pages, npages); +} + +void *nouveau_gem_prime_vmap(struct drm_gem_object *obj) +{ + struct nouveau_bo *nvbo = nouveau_gem_object(obj); + int ret; + + ret = ttm_bo_kmap(&nvbo->bo, 0, nvbo->bo.num_pages, + &nvbo->dma_buf_vmap); + if (ret) + return ERR_PTR(ret); + + return nvbo->dma_buf_vmap.virtual; +} + +void nouveau_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr) +{ + struct nouveau_bo *nvbo = nouveau_gem_object(obj); + + ttm_bo_kunmap(&nvbo->dma_buf_vmap); +} + +struct drm_gem_object *nouveau_gem_prime_import_sg_table(struct drm_device *dev, + struct dma_buf_attachment *attach, + struct sg_table *sg) +{ + struct nouveau_bo *nvbo; + struct reservation_object *robj = attach->dmabuf->resv; + u32 flags = 0; + int ret; + + flags = TTM_PL_FLAG_TT; + + ww_mutex_lock(&robj->lock, NULL); + ret = nouveau_bo_new(dev, attach->dmabuf->size, 0, flags, 0, 0, + sg, robj, &nvbo); + ww_mutex_unlock(&robj->lock); + if (ret) + return ERR_PTR(ret); + + nvbo->valid_domains = NOUVEAU_GEM_DOMAIN_GART; + + /* Initialize the embedded gem-object. We return a single gem-reference + * to the caller, instead of a normal nouveau_bo ttm reference. */ + ret = drm_gem_object_init(dev, &nvbo->gem, nvbo->bo.mem.size); + if (ret) { + nouveau_bo_ref(NULL, &nvbo); + return ERR_PTR(-ENOMEM); + } + + return &nvbo->gem; +} + +int nouveau_gem_prime_pin(struct drm_gem_object *obj) +{ + struct nouveau_bo *nvbo = nouveau_gem_object(obj); + int ret; + + /* pin buffer into GTT */ + ret = nouveau_bo_pin(nvbo, TTM_PL_FLAG_TT, false); + if (ret) + return -EINVAL; + + return 0; +} + +void nouveau_gem_prime_unpin(struct drm_gem_object *obj) +{ + struct nouveau_bo *nvbo = nouveau_gem_object(obj); + + nouveau_bo_unpin(nvbo); +} + +struct reservation_object *nouveau_gem_prime_res_obj(struct drm_gem_object *obj) +{ + struct nouveau_bo *nvbo = nouveau_gem_object(obj); + + return nvbo->bo.resv; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_reg.h b/kernel/drivers/gpu/drm/nouveau/nouveau_reg.h new file mode 100644 index 000000000..7226f1f60 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_reg.h @@ -0,0 +1,858 @@ + +#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_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_FIFO_DATA 0x0010020c +# define NV10_PFB_FIFO_DATA_RAM_AMOUNT_MB_MASK 0xfff00000 +# define NV10_PFB_FIFO_DATA_RAM_AMOUNT_MB_SHIFT 20 +#define NV10_PFB_REFCTRL 0x00100210 +# define NV10_PFB_REFCTRL_VALID_1 (1 << 31) +#define NV04_PFB_PAD 0x0010021c +# define NV04_PFB_PAD_CKE_NORMAL (1 << 0) +#define NV10_PFB_TILE(i) (0x00100240 + (i*16)) +#define NV10_PFB_TILE__SIZE 8 +#define NV10_PFB_TLIMIT(i) (0x00100244 + (i*16)) +#define NV10_PFB_TSIZE(i) (0x00100248 + (i*16)) +#define NV10_PFB_TSTATUS(i) (0x0010024c + (i*16)) +#define NV04_PFB_REF 0x001002d0 +# define NV04_PFB_REF_CMD_REFRESH (1 << 0) +#define NV04_PFB_PRE 0x001002d4 +# define NV04_PFB_PRE_CMD_PRECHARGE (1 << 0) +#define NV20_PFB_ZCOMP(i) (0x00100300 + 4*(i)) +# define NV20_PFB_ZCOMP_MODE_32 (4 << 24) +# define NV20_PFB_ZCOMP_EN (1 << 31) +# define NV25_PFB_ZCOMP_MODE_16 (1 << 20) +# define NV25_PFB_ZCOMP_MODE_32 (2 << 20) +#define NV10_PFB_CLOSE_PAGE2 0x0010033c +#define NV04_PFB_SCRAMBLE(i) (0x00100400 + 4 * (i)) +#define NV40_PFB_TILE(i) (0x00100600 + (i*16)) +#define NV40_PFB_TILE__SIZE_0 12 +#define NV40_PFB_TILE__SIZE_1 15 +#define NV40_PFB_TLIMIT(i) (0x00100604 + (i*16)) +#define NV40_PFB_TSIZE(i) (0x00100608 + (i*16)) +#define NV40_PFB_TSTATUS(i) (0x0010060c + (i*16)) +#define NV40_PFB_UNK_800 0x00100800 + +#define NV_PEXTDEV_BOOT_0 0x00101000 +#define NV_PEXTDEV_BOOT_0_RAMCFG 0x0000003c +# define NV_PEXTDEV_BOOT_0_STRAP_FP_IFACE_12BIT (8 << 12) +#define NV_PEXTDEV_BOOT_3 0x0010100c + +#define NV_RAMIN 0x00700000 + +#define NV_RAMHT_HANDLE_OFFSET 0 +#define NV_RAMHT_CONTEXT_OFFSET 4 +# define NV_RAMHT_CONTEXT_VALID (1<<31) +# define NV_RAMHT_CONTEXT_CHANNEL_SHIFT 24 +# define NV_RAMHT_CONTEXT_ENGINE_SHIFT 16 +# define NV_RAMHT_CONTEXT_ENGINE_SW 0 +# define NV_RAMHT_CONTEXT_ENGINE_GRAPHICS 1 +# define NV_RAMHT_CONTEXT_INSTANCE_SHIFT 0 +# define NV40_RAMHT_CONTEXT_CHANNEL_SHIFT 23 +# define NV40_RAMHT_CONTEXT_ENGINE_SHIFT 20 +# define NV40_RAMHT_CONTEXT_INSTANCE_SHIFT 0 + +/* Some object classes we care about in the drm */ +#define NV_CLASS_DMA_FROM_MEMORY 0x00000002 +#define NV_CLASS_DMA_TO_MEMORY 0x00000003 +#define NV_CLASS_NULL 0x00000030 +#define NV_CLASS_DMA_IN_MEMORY 0x0000003D + +#define NV03_USER(i) (0x00800000+(i*NV03_USER_SIZE)) +#define NV03_USER__SIZE 16 +#define NV10_USER__SIZE 32 +#define NV03_USER_SIZE 0x00010000 +#define NV03_USER_DMA_PUT(i) (0x00800040+(i*NV03_USER_SIZE)) +#define NV03_USER_DMA_PUT__SIZE 16 +#define NV10_USER_DMA_PUT__SIZE 32 +#define NV03_USER_DMA_GET(i) (0x00800044+(i*NV03_USER_SIZE)) +#define NV03_USER_DMA_GET__SIZE 16 +#define NV10_USER_DMA_GET__SIZE 32 +#define NV03_USER_REF_CNT(i) (0x00800048+(i*NV03_USER_SIZE)) +#define NV03_USER_REF_CNT__SIZE 16 +#define NV10_USER_REF_CNT__SIZE 32 + +#define NV40_USER(i) (0x00c00000+(i*NV40_USER_SIZE)) +#define NV40_USER_SIZE 0x00001000 +#define NV40_USER_DMA_PUT(i) (0x00c00040+(i*NV40_USER_SIZE)) +#define NV40_USER_DMA_PUT__SIZE 32 +#define NV40_USER_DMA_GET(i) (0x00c00044+(i*NV40_USER_SIZE)) +#define NV40_USER_DMA_GET__SIZE 32 +#define NV40_USER_REF_CNT(i) (0x00c00048+(i*NV40_USER_SIZE)) +#define NV40_USER_REF_CNT__SIZE 32 + +#define NV50_USER(i) (0x00c00000+(i*NV50_USER_SIZE)) +#define NV50_USER_SIZE 0x00002000 +#define NV50_USER_DMA_PUT(i) (0x00c00040+(i*NV50_USER_SIZE)) +#define NV50_USER_DMA_PUT__SIZE 128 +#define NV50_USER_DMA_GET(i) (0x00c00044+(i*NV50_USER_SIZE)) +#define NV50_USER_DMA_GET__SIZE 128 +#define NV50_USER_REF_CNT(i) (0x00c00048+(i*NV50_USER_SIZE)) +#define NV50_USER_REF_CNT__SIZE 128 + +#define NV03_FIFO_SIZE 0x8000UL + +#define NV03_PMC_BOOT_0 0x00000000 +#define NV03_PMC_BOOT_1 0x00000004 +#define NV03_PMC_INTR_0 0x00000100 +# define NV_PMC_INTR_0_PFIFO_PENDING (1<<8) +# define NV_PMC_INTR_0_PGRAPH_PENDING (1<<12) +# define NV_PMC_INTR_0_NV50_I2C_PENDING (1<<21) +# define NV_PMC_INTR_0_CRTC0_PENDING (1<<24) +# define NV_PMC_INTR_0_CRTC1_PENDING (1<<25) +# define NV_PMC_INTR_0_NV50_DISPLAY_PENDING (1<<26) +# define NV_PMC_INTR_0_CRTCn_PENDING (3<<24) +#define NV03_PMC_INTR_EN_0 0x00000140 +# define NV_PMC_INTR_EN_0_MASTER_ENABLE (1<<0) +#define NV03_PMC_ENABLE 0x00000200 +# define NV_PMC_ENABLE_PFIFO (1<<8) +# define NV_PMC_ENABLE_PGRAPH (1<<12) +/* Disabling the below bit breaks newer (G7X only?) mobile chipsets, + * the card will hang early on in the X init process. + */ +# define NV_PMC_ENABLE_UNK13 (1<<13) +#define NV40_PMC_GRAPH_UNITS 0x00001540 +#define NV40_PMC_BACKLIGHT 0x000015f0 +# define NV40_PMC_BACKLIGHT_MASK 0x001f0000 +#define NV40_PMC_1700 0x00001700 +#define NV40_PMC_1704 0x00001704 +#define NV40_PMC_1708 0x00001708 +#define NV40_PMC_170C 0x0000170C + +/* probably PMC ? */ +#define NV50_PUNK_BAR0_PRAMIN 0x00001700 +#define NV50_PUNK_BAR_CFG_BASE 0x00001704 +#define NV50_PUNK_BAR_CFG_BASE_VALID (1<<30) +#define NV50_PUNK_BAR1_CTXDMA 0x00001708 +#define NV50_PUNK_BAR1_CTXDMA_VALID (1<<31) +#define NV50_PUNK_BAR3_CTXDMA 0x0000170C +#define NV50_PUNK_BAR3_CTXDMA_VALID (1<<31) +#define NV50_PUNK_UNK1710 0x00001710 + +#define NV04_PBUS_PCI_NV_1 0x00001804 +#define NV04_PBUS_PCI_NV_19 0x0000184C +#define NV04_PBUS_PCI_NV_20 0x00001850 +# define NV04_PBUS_PCI_NV_20_ROM_SHADOW_DISABLED (0 << 0) +# define NV04_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED (1 << 0) + +#define NV04_PTIMER_INTR_0 0x00009100 +#define NV04_PTIMER_INTR_EN_0 0x00009140 +#define NV04_PTIMER_NUMERATOR 0x00009200 +#define NV04_PTIMER_DENOMINATOR 0x00009210 +#define NV04_PTIMER_TIME_0 0x00009400 +#define NV04_PTIMER_TIME_1 0x00009410 +#define NV04_PTIMER_ALARM_0 0x00009420 + +#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 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 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 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)) + + +/* It's a guess that this works on NV03. Confirmed on NV04, though */ +#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)) + +#define NV_CRTC0_INTSTAT 0x00600100 +#define NV_CRTC0_INTEN 0x00600140 +#define NV_CRTC1_INTSTAT 0x00602100 +#define NV_CRTC1_INTEN 0x00602140 +# define NV_CRTC_INTR_VBLANK (1<<0) + +#define NV04_PRAMIN 0x00700000 + +/* Fifo commands. These are not regs, neither masks */ +#define NV03_FIFO_CMD_JUMP 0x20000000 +#define NV03_FIFO_CMD_JUMP_OFFSET_MASK 0x1ffffffc +#define NV03_FIFO_CMD_REWIND (NV03_FIFO_CMD_JUMP | (0 & NV03_FIFO_CMD_JUMP_OFFSET_MASK)) + +/* This is a partial import from rules-ng, a few things may be duplicated. + * Eventually we should completely import everything from rules-ng. + * For the moment check rules-ng for docs. + */ + +#define NV50_PMC 0x00000000 +#define NV50_PMC__LEN 0x1 +#define NV50_PMC__ESIZE 0x2000 +# define NV50_PMC_BOOT_0 0x00000000 +# define NV50_PMC_BOOT_0_REVISION 0x000000ff +# define NV50_PMC_BOOT_0_REVISION__SHIFT 0 +# define NV50_PMC_BOOT_0_ARCH 0x0ff00000 +# define NV50_PMC_BOOT_0_ARCH__SHIFT 20 +# define NV50_PMC_INTR_0 0x00000100 +# define NV50_PMC_INTR_0_PFIFO (1<<8) +# define NV50_PMC_INTR_0_PGRAPH (1<<12) +# define NV50_PMC_INTR_0_PTIMER (1<<20) +# define NV50_PMC_INTR_0_HOTPLUG (1<<21) +# define NV50_PMC_INTR_0_DISPLAY (1<<26) +# define NV50_PMC_INTR_EN_0 0x00000140 +# define NV50_PMC_INTR_EN_0_MASTER (1<<0) +# define NV50_PMC_INTR_EN_0_MASTER_DISABLED (0<<0) +# define NV50_PMC_INTR_EN_0_MASTER_ENABLED (1<<0) +# define NV50_PMC_ENABLE 0x00000200 +# define NV50_PMC_ENABLE_PFIFO (1<<8) +# define NV50_PMC_ENABLE_PGRAPH (1<<12) + +#define NV50_PCONNECTOR 0x0000e000 +#define NV50_PCONNECTOR__LEN 0x1 +#define NV50_PCONNECTOR__ESIZE 0x1000 +# define NV50_PCONNECTOR_HOTPLUG_INTR 0x0000e050 +# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C0 (1<<0) +# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C1 (1<<1) +# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C2 (1<<2) +# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C3 (1<<3) +# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C0 (1<<16) +# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C1 (1<<17) +# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C2 (1<<18) +# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C3 (1<<19) +# define NV50_PCONNECTOR_HOTPLUG_CTRL 0x0000e054 +# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C0 (1<<0) +# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C1 (1<<1) +# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C2 (1<<2) +# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C3 (1<<3) +# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C0 (1<<16) +# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C1 (1<<17) +# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C2 (1<<18) +# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C3 (1<<19) +# define NV50_PCONNECTOR_HOTPLUG_STATE 0x0000e104 +# define NV50_PCONNECTOR_HOTPLUG_STATE_PIN_CONNECTED_I2C0 (1<<2) +# define NV50_PCONNECTOR_HOTPLUG_STATE_PIN_CONNECTED_I2C1 (1<<6) +# define NV50_PCONNECTOR_HOTPLUG_STATE_PIN_CONNECTED_I2C2 (1<<10) +# define NV50_PCONNECTOR_HOTPLUG_STATE_PIN_CONNECTED_I2C3 (1<<14) +# define NV50_PCONNECTOR_I2C_PORT_0 0x0000e138 +# define NV50_PCONNECTOR_I2C_PORT_1 0x0000e150 +# define NV50_PCONNECTOR_I2C_PORT_2 0x0000e168 +# define NV50_PCONNECTOR_I2C_PORT_3 0x0000e180 +# define NV50_PCONNECTOR_I2C_PORT_4 0x0000e240 +# define NV50_PCONNECTOR_I2C_PORT_5 0x0000e258 + +#define NV50_AUXCH_DATA_OUT(i, n) ((n) * 4 + (i) * 0x50 + 0x0000e4c0) +#define NV50_AUXCH_DATA_OUT__SIZE 4 +#define NV50_AUXCH_DATA_IN(i, n) ((n) * 4 + (i) * 0x50 + 0x0000e4d0) +#define NV50_AUXCH_DATA_IN__SIZE 4 +#define NV50_AUXCH_ADDR(i) ((i) * 0x50 + 0x0000e4e0) +#define NV50_AUXCH_CTRL(i) ((i) * 0x50 + 0x0000e4e4) +#define NV50_AUXCH_CTRL_LINKSTAT 0x01000000 +#define NV50_AUXCH_CTRL_LINKSTAT_NOT_READY 0x00000000 +#define NV50_AUXCH_CTRL_LINKSTAT_READY 0x01000000 +#define NV50_AUXCH_CTRL_LINKEN 0x00100000 +#define NV50_AUXCH_CTRL_LINKEN_DISABLED 0x00000000 +#define NV50_AUXCH_CTRL_LINKEN_ENABLED 0x00100000 +#define NV50_AUXCH_CTRL_EXEC 0x00010000 +#define NV50_AUXCH_CTRL_EXEC_COMPLETE 0x00000000 +#define NV50_AUXCH_CTRL_EXEC_IN_PROCESS 0x00010000 +#define NV50_AUXCH_CTRL_CMD 0x0000f000 +#define NV50_AUXCH_CTRL_CMD_SHIFT 12 +#define NV50_AUXCH_CTRL_LEN 0x0000000f +#define NV50_AUXCH_CTRL_LEN_SHIFT 0 +#define NV50_AUXCH_STAT(i) ((i) * 0x50 + 0x0000e4e8) +#define NV50_AUXCH_STAT_STATE 0x10000000 +#define NV50_AUXCH_STAT_STATE_NOT_READY 0x00000000 +#define NV50_AUXCH_STAT_STATE_READY 0x10000000 +#define NV50_AUXCH_STAT_REPLY 0x000f0000 +#define NV50_AUXCH_STAT_REPLY_AUX 0x00030000 +#define NV50_AUXCH_STAT_REPLY_AUX_ACK 0x00000000 +#define NV50_AUXCH_STAT_REPLY_AUX_NACK 0x00010000 +#define NV50_AUXCH_STAT_REPLY_AUX_DEFER 0x00020000 +#define NV50_AUXCH_STAT_REPLY_I2C 0x000c0000 +#define NV50_AUXCH_STAT_REPLY_I2C_ACK 0x00000000 +#define NV50_AUXCH_STAT_REPLY_I2C_NACK 0x00040000 +#define NV50_AUXCH_STAT_REPLY_I2C_DEFER 0x00080000 +#define NV50_AUXCH_STAT_COUNT 0x0000001f + +#define NV50_PBUS 0x00088000 +#define NV50_PBUS__LEN 0x1 +#define NV50_PBUS__ESIZE 0x1000 +# define NV50_PBUS_PCI_ID 0x00088000 +# define NV50_PBUS_PCI_ID_VENDOR_ID 0x0000ffff +# define NV50_PBUS_PCI_ID_VENDOR_ID__SHIFT 0 +# define NV50_PBUS_PCI_ID_DEVICE_ID 0xffff0000 +# define NV50_PBUS_PCI_ID_DEVICE_ID__SHIFT 16 + +#define NV50_PFB 0x00100000 +#define NV50_PFB__LEN 0x1 +#define NV50_PFB__ESIZE 0x1000 + +#define NV50_PEXTDEV 0x00101000 +#define NV50_PEXTDEV__LEN 0x1 +#define NV50_PEXTDEV__ESIZE 0x1000 + +#define NV50_PROM 0x00300000 +#define NV50_PROM__LEN 0x1 +#define NV50_PROM__ESIZE 0x10000 + +#define NV50_PGRAPH 0x00400000 +#define NV50_PGRAPH__LEN 0x1 +#define NV50_PGRAPH__ESIZE 0x10000 + +#define NV50_PDISPLAY 0x00610000 +#define NV50_PDISPLAY_OBJECTS 0x00610010 +#define NV50_PDISPLAY_INTR_0 0x00610020 +#define NV50_PDISPLAY_INTR_1 0x00610024 +#define NV50_PDISPLAY_INTR_1_VBLANK_CRTC 0x0000000c +#define NV50_PDISPLAY_INTR_1_VBLANK_CRTC_SHIFT 2 +#define NV50_PDISPLAY_INTR_1_VBLANK_CRTC_(n) (1 << ((n) + 2)) +#define NV50_PDISPLAY_INTR_1_VBLANK_CRTC_0 0x00000004 +#define NV50_PDISPLAY_INTR_1_VBLANK_CRTC_1 0x00000008 +#define NV50_PDISPLAY_INTR_1_CLK_UNK10 0x00000010 +#define NV50_PDISPLAY_INTR_1_CLK_UNK20 0x00000020 +#define NV50_PDISPLAY_INTR_1_CLK_UNK40 0x00000040 +#define NV50_PDISPLAY_INTR_EN_0 0x00610028 +#define NV50_PDISPLAY_INTR_EN_1 0x0061002c +#define NV50_PDISPLAY_INTR_EN_1_VBLANK_CRTC 0x0000000c +#define NV50_PDISPLAY_INTR_EN_1_VBLANK_CRTC_(n) (1 << ((n) + 2)) +#define NV50_PDISPLAY_INTR_EN_1_VBLANK_CRTC_0 0x00000004 +#define NV50_PDISPLAY_INTR_EN_1_VBLANK_CRTC_1 0x00000008 +#define NV50_PDISPLAY_INTR_EN_1_CLK_UNK10 0x00000010 +#define NV50_PDISPLAY_INTR_EN_1_CLK_UNK20 0x00000020 +#define NV50_PDISPLAY_INTR_EN_1_CLK_UNK40 0x00000040 +#define NV50_PDISPLAY_UNK30_CTRL 0x00610030 +#define NV50_PDISPLAY_UNK30_CTRL_UPDATE_VCLK0 0x00000200 +#define NV50_PDISPLAY_UNK30_CTRL_UPDATE_VCLK1 0x00000400 +#define NV50_PDISPLAY_UNK30_CTRL_PENDING 0x80000000 +#define NV50_PDISPLAY_TRAPPED_ADDR(i) ((i) * 0x08 + 0x00610080) +#define NV50_PDISPLAY_TRAPPED_DATA(i) ((i) * 0x08 + 0x00610084) +#define NV50_PDISPLAY_EVO_CTRL(i) ((i) * 0x10 + 0x00610200) +#define NV50_PDISPLAY_EVO_CTRL_DMA 0x00000010 +#define NV50_PDISPLAY_EVO_CTRL_DMA_DISABLED 0x00000000 +#define NV50_PDISPLAY_EVO_CTRL_DMA_ENABLED 0x00000010 +#define NV50_PDISPLAY_EVO_DMA_CB(i) ((i) * 0x10 + 0x00610204) +#define NV50_PDISPLAY_EVO_DMA_CB_LOCATION 0x00000002 +#define NV50_PDISPLAY_EVO_DMA_CB_LOCATION_VRAM 0x00000000 +#define NV50_PDISPLAY_EVO_DMA_CB_LOCATION_SYSTEM 0x00000002 +#define NV50_PDISPLAY_EVO_DMA_CB_VALID 0x00000001 +#define NV50_PDISPLAY_EVO_UNK2(i) ((i) * 0x10 + 0x00610208) +#define NV50_PDISPLAY_EVO_HASH_TAG(i) ((i) * 0x10 + 0x0061020c) + +#define NV50_PDISPLAY_CURSOR 0x00610270 +#define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i) ((i) * 0x10 + 0x00610270) +#define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_ON 0x00000001 +#define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS 0x00030000 +#define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_ACTIVE 0x00010000 + +#define NV50_PDISPLAY_PIO_CTRL 0x00610300 +#define NV50_PDISPLAY_PIO_CTRL_PENDING 0x80000000 +#define NV50_PDISPLAY_PIO_CTRL_MTHD 0x00001ffc +#define NV50_PDISPLAY_PIO_CTRL_ENABLED 0x00000001 +#define NV50_PDISPLAY_PIO_DATA 0x00610304 + +#define NV50_PDISPLAY_CRTC_P(i, r) ((i) * 0x540 + NV50_PDISPLAY_CRTC_##r) +#define NV50_PDISPLAY_CRTC_C(i, r) (4 + (i) * 0x540 + NV50_PDISPLAY_CRTC_##r) +#define NV50_PDISPLAY_CRTC_UNK_0A18 /* mthd 0x0900 */ 0x00610a18 +#define NV50_PDISPLAY_CRTC_CLUT_MODE 0x00610a24 +#define NV50_PDISPLAY_CRTC_INTERLACE 0x00610a48 +#define NV50_PDISPLAY_CRTC_SCALE_CTRL 0x00610a50 +#define NV50_PDISPLAY_CRTC_CURSOR_CTRL 0x00610a58 +#define NV50_PDISPLAY_CRTC_UNK0A78 /* mthd 0x0904 */ 0x00610a78 +#define NV50_PDISPLAY_CRTC_UNK0AB8 0x00610ab8 +#define NV50_PDISPLAY_CRTC_DEPTH 0x00610ac8 +#define NV50_PDISPLAY_CRTC_CLOCK 0x00610ad0 +#define NV50_PDISPLAY_CRTC_COLOR_CTRL 0x00610ae0 +#define NV50_PDISPLAY_CRTC_SYNC_START_TO_BLANK_END 0x00610ae8 +#define NV50_PDISPLAY_CRTC_MODE_UNK1 0x00610af0 +#define NV50_PDISPLAY_CRTC_DISPLAY_TOTAL 0x00610af8 +#define NV50_PDISPLAY_CRTC_SYNC_DURATION 0x00610b00 +#define NV50_PDISPLAY_CRTC_MODE_UNK2 0x00610b08 +#define NV50_PDISPLAY_CRTC_UNK_0B10 /* mthd 0x0828 */ 0x00610b10 +#define NV50_PDISPLAY_CRTC_FB_SIZE 0x00610b18 +#define NV50_PDISPLAY_CRTC_FB_PITCH 0x00610b20 +#define NV50_PDISPLAY_CRTC_FB_PITCH_LINEAR 0x00100000 +#define NV50_PDISPLAY_CRTC_FB_POS 0x00610b28 +#define NV50_PDISPLAY_CRTC_SCALE_CENTER_OFFSET 0x00610b38 +#define NV50_PDISPLAY_CRTC_REAL_RES 0x00610b40 +#define NV50_PDISPLAY_CRTC_SCALE_RES1 0x00610b48 +#define NV50_PDISPLAY_CRTC_SCALE_RES2 0x00610b50 + +#define NV50_PDISPLAY_DAC_MODE_CTRL_P(i) (0x00610b58 + (i) * 0x8) +#define NV50_PDISPLAY_DAC_MODE_CTRL_C(i) (0x00610b5c + (i) * 0x8) +#define NV50_PDISPLAY_SOR_MODE_CTRL_P(i) (0x00610b70 + (i) * 0x8) +#define NV50_PDISPLAY_SOR_MODE_CTRL_C(i) (0x00610b74 + (i) * 0x8) +#define NV50_PDISPLAY_EXT_MODE_CTRL_P(i) (0x00610b80 + (i) * 0x8) +#define NV50_PDISPLAY_EXT_MODE_CTRL_C(i) (0x00610b84 + (i) * 0x8) +#define NV50_PDISPLAY_DAC_MODE_CTRL2_P(i) (0x00610bdc + (i) * 0x8) +#define NV50_PDISPLAY_DAC_MODE_CTRL2_C(i) (0x00610be0 + (i) * 0x8) +#define NV90_PDISPLAY_SOR_MODE_CTRL_P(i) (0x00610794 + (i) * 0x8) +#define NV90_PDISPLAY_SOR_MODE_CTRL_C(i) (0x00610798 + (i) * 0x8) + +#define NV50_PDISPLAY_CRTC_CLK 0x00614000 +#define NV50_PDISPLAY_CRTC_CLK_CTRL1(i) ((i) * 0x800 + 0x614100) +#define NV50_PDISPLAY_CRTC_CLK_CTRL1_CONNECTED 0x00000600 +#define NV50_PDISPLAY_CRTC_CLK_VPLL_A(i) ((i) * 0x800 + 0x614104) +#define NV50_PDISPLAY_CRTC_CLK_VPLL_B(i) ((i) * 0x800 + 0x614108) +#define NV50_PDISPLAY_CRTC_CLK_CTRL2(i) ((i) * 0x800 + 0x614200) + +#define NV50_PDISPLAY_DAC_CLK 0x00614000 +#define NV50_PDISPLAY_DAC_CLK_CTRL2(i) ((i) * 0x800 + 0x614280) + +#define NV50_PDISPLAY_SOR_CLK 0x00614000 +#define NV50_PDISPLAY_SOR_CLK_CTRL2(i) ((i) * 0x800 + 0x614300) + +#define NV50_PDISPLAY_VGACRTC(r) ((r) + 0x619400) + +#define NV50_PDISPLAY_DAC 0x0061a000 +#define NV50_PDISPLAY_DAC_DPMS_CTRL(i) (0x0061a004 + (i) * 0x800) +#define NV50_PDISPLAY_DAC_DPMS_CTRL_HSYNC_OFF 0x00000001 +#define NV50_PDISPLAY_DAC_DPMS_CTRL_VSYNC_OFF 0x00000004 +#define NV50_PDISPLAY_DAC_DPMS_CTRL_BLANKED 0x00000010 +#define NV50_PDISPLAY_DAC_DPMS_CTRL_OFF 0x00000040 +#define NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING 0x80000000 +#define NV50_PDISPLAY_DAC_LOAD_CTRL(i) (0x0061a00c + (i) * 0x800) +#define NV50_PDISPLAY_DAC_LOAD_CTRL_ACTIVE 0x00100000 +#define NV50_PDISPLAY_DAC_LOAD_CTRL_PRESENT 0x38000000 +#define NV50_PDISPLAY_DAC_LOAD_CTRL_DONE 0x80000000 +#define NV50_PDISPLAY_DAC_CLK_CTRL1(i) (0x0061a010 + (i) * 0x800) +#define NV50_PDISPLAY_DAC_CLK_CTRL1_CONNECTED 0x00000600 + +#define NV50_PDISPLAY_SOR 0x0061c000 +#define NV50_PDISPLAY_SOR_DPMS_CTRL(i) (0x0061c004 + (i) * 0x800) +#define NV50_PDISPLAY_SOR_DPMS_CTRL_PENDING 0x80000000 +#define NV50_PDISPLAY_SOR_DPMS_CTRL_ON 0x00000001 +#define NV50_PDISPLAY_SOR_CLK_CTRL1(i) (0x0061c008 + (i) * 0x800) +#define NV50_PDISPLAY_SOR_CLK_CTRL1_CONNECTED 0x00000600 +#define NV50_PDISPLAY_SOR_DPMS_STATE(i) (0x0061c030 + (i) * 0x800) +#define NV50_PDISPLAY_SOR_DPMS_STATE_ACTIVE 0x00030000 +#define NV50_PDISPLAY_SOR_DPMS_STATE_BLANKED 0x00080000 +#define NV50_PDISPLAY_SOR_DPMS_STATE_WAIT 0x10000000 +#define NV50_PDISP_SOR_PWM_DIV(i) (0x0061c080 + (i) * 0x800) +#define NV50_PDISP_SOR_PWM_CTL(i) (0x0061c084 + (i) * 0x800) +#define NV50_PDISP_SOR_PWM_CTL_NEW 0x80000000 +#define NVA3_PDISP_SOR_PWM_CTL_UNK 0x40000000 +#define NV50_PDISP_SOR_PWM_CTL_VAL 0x000007ff +#define NVA3_PDISP_SOR_PWM_CTL_VAL 0x00ffffff +#define NV50_SOR_DP_CTRL(i, l) (0x0061c10c + (i) * 0x800 + (l) * 0x80) +#define NV50_SOR_DP_CTRL_ENABLED 0x00000001 +#define NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED 0x00004000 +#define NV50_SOR_DP_CTRL_LANE_MASK 0x001f0000 +#define NV50_SOR_DP_CTRL_LANE_0_ENABLED 0x00010000 +#define NV50_SOR_DP_CTRL_LANE_1_ENABLED 0x00020000 +#define NV50_SOR_DP_CTRL_LANE_2_ENABLED 0x00040000 +#define NV50_SOR_DP_CTRL_LANE_3_ENABLED 0x00080000 +#define NV50_SOR_DP_CTRL_TRAINING_PATTERN 0x0f000000 +#define NV50_SOR_DP_CTRL_TRAINING_PATTERN_DISABLED 0x00000000 +#define NV50_SOR_DP_CTRL_TRAINING_PATTERN_1 0x01000000 +#define NV50_SOR_DP_CTRL_TRAINING_PATTERN_2 0x02000000 +#define NV50_SOR_DP_UNK118(i, l) (0x0061c118 + (i) * 0x800 + (l) * 0x80) +#define NV50_SOR_DP_UNK120(i, l) (0x0061c120 + (i) * 0x800 + (l) * 0x80) +#define NV50_SOR_DP_SCFG(i, l) (0x0061c128 + (i) * 0x800 + (l) * 0x80) +#define NV50_SOR_DP_UNK130(i, l) (0x0061c130 + (i) * 0x800 + (l) * 0x80) + +#define NV50_PDISPLAY_USER(i) ((i) * 0x1000 + 0x00640000) +#define NV50_PDISPLAY_USER_PUT(i) ((i) * 0x1000 + 0x00640000) +#define NV50_PDISPLAY_USER_GET(i) ((i) * 0x1000 + 0x00640004) + +#define NV50_PDISPLAY_CURSOR_USER 0x00647000 +#define NV50_PDISPLAY_CURSOR_USER_POS_CTRL(i) ((i) * 0x1000 + 0x00647080) +#define NV50_PDISPLAY_CURSOR_USER_POS(i) ((i) * 0x1000 + 0x00647084) diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_sgdma.c b/kernel/drivers/gpu/drm/nouveau/nouveau_sgdma.c new file mode 100644 index 000000000..8c3053a17 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_sgdma.c @@ -0,0 +1,116 @@ +#include +#include + +#include "nouveau_drm.h" +#include "nouveau_ttm.h" + +struct nouveau_sgdma_be { + /* this has to be the first field so populate/unpopulated in + * nouve_bo.c works properly, otherwise have to move them here + */ + struct ttm_dma_tt ttm; + struct nvkm_mem *node; +}; + +static void +nouveau_sgdma_destroy(struct ttm_tt *ttm) +{ + struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)ttm; + + if (ttm) { + ttm_dma_tt_fini(&nvbe->ttm); + kfree(nvbe); + } +} + +static int +nv04_sgdma_bind(struct ttm_tt *ttm, struct ttm_mem_reg *mem) +{ + struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)ttm; + struct nvkm_mem *node = mem->mm_node; + + if (ttm->sg) { + node->sg = ttm->sg; + node->pages = NULL; + } else { + node->sg = NULL; + node->pages = nvbe->ttm.dma_address; + } + node->size = (mem->num_pages << PAGE_SHIFT) >> 12; + + nvkm_vm_map(&node->vma[0], node); + nvbe->node = node; + return 0; +} + +static int +nv04_sgdma_unbind(struct ttm_tt *ttm) +{ + struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)ttm; + nvkm_vm_unmap(&nvbe->node->vma[0]); + return 0; +} + +static struct ttm_backend_func nv04_sgdma_backend = { + .bind = nv04_sgdma_bind, + .unbind = nv04_sgdma_unbind, + .destroy = nouveau_sgdma_destroy +}; + +static int +nv50_sgdma_bind(struct ttm_tt *ttm, struct ttm_mem_reg *mem) +{ + struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)ttm; + struct nvkm_mem *node = mem->mm_node; + + /* noop: bound in move_notify() */ + if (ttm->sg) { + node->sg = ttm->sg; + node->pages = NULL; + } else { + node->sg = NULL; + node->pages = nvbe->ttm.dma_address; + } + node->size = (mem->num_pages << PAGE_SHIFT) >> 12; + return 0; +} + +static int +nv50_sgdma_unbind(struct ttm_tt *ttm) +{ + /* noop: unbound in move_notify() */ + return 0; +} + +static struct ttm_backend_func nv50_sgdma_backend = { + .bind = nv50_sgdma_bind, + .unbind = nv50_sgdma_unbind, + .destroy = nouveau_sgdma_destroy +}; + +struct ttm_tt * +nouveau_sgdma_create_ttm(struct ttm_bo_device *bdev, + unsigned long size, uint32_t page_flags, + struct page *dummy_read_page) +{ + struct nouveau_drm *drm = nouveau_bdev(bdev); + struct nouveau_sgdma_be *nvbe; + + nvbe = kzalloc(sizeof(*nvbe), GFP_KERNEL); + if (!nvbe) + return NULL; + + if (drm->device.info.family < NV_DEVICE_INFO_V0_TESLA) + nvbe->ttm.ttm.func = &nv04_sgdma_backend; + else + nvbe->ttm.ttm.func = &nv50_sgdma_backend; + + if (ttm_dma_tt_init(&nvbe->ttm, bdev, size, page_flags, dummy_read_page)) + /* + * A failing ttm_dma_tt_init() will call ttm_tt_destroy() + * and thus our nouveau_sgdma_destroy() hook, so we don't need + * to free nvbe here. + */ + return NULL; + return &nvbe->ttm.ttm; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_sysfs.c b/kernel/drivers/gpu/drm/nouveau/nouveau_sysfs.c new file mode 100644 index 000000000..1ec8f38ae --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_sysfs.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 +#include +#include + +#include "nouveau_sysfs.h" + +MODULE_PARM_DESC(pstate, "enable sysfs pstate file, which will be moved in the future"); +int nouveau_pstate; +module_param_named(pstate, nouveau_pstate, int, 0400); + +static inline struct drm_device * +drm_device(struct device *d) +{ + return dev_get_drvdata(d); +} + +#define snappendf(p,r,f,a...) do { \ + snprintf(p, r, f, ##a); \ + r -= strlen(p); \ + p += strlen(p); \ +} while(0) + +static ssize_t +nouveau_sysfs_pstate_get(struct device *d, struct device_attribute *a, char *b) +{ + struct nouveau_sysfs *sysfs = nouveau_sysfs(drm_device(d)); + struct nvif_control_pstate_info_v0 info = {}; + size_t cnt = PAGE_SIZE; + char *buf = b; + int ret, i; + + ret = nvif_mthd(&sysfs->ctrl, NVIF_CONTROL_PSTATE_INFO, + &info, sizeof(info)); + if (ret) + return ret; + + for (i = 0; i < info.count + 1; i++) { + const s32 state = i < info.count ? i : + NVIF_CONTROL_PSTATE_ATTR_V0_STATE_CURRENT; + struct nvif_control_pstate_attr_v0 attr = { + .state = state, + .index = 0, + }; + + ret = nvif_mthd(&sysfs->ctrl, NVIF_CONTROL_PSTATE_ATTR, + &attr, sizeof(attr)); + if (ret) + return ret; + + if (i < info.count) + snappendf(buf, cnt, "%02x:", attr.state); + else + snappendf(buf, cnt, "%s:", info.pwrsrc == 0 ? "DC" : + info.pwrsrc == 1 ? "AC" : + "--"); + + attr.index = 0; + do { + attr.state = state; + ret = nvif_mthd(&sysfs->ctrl, + NVIF_CONTROL_PSTATE_ATTR, + &attr, sizeof(attr)); + if (ret) + return ret; + + snappendf(buf, cnt, " %s %d", attr.name, attr.min); + if (attr.min != attr.max) + snappendf(buf, cnt, "-%d", attr.max); + snappendf(buf, cnt, " %s", attr.unit); + } while (attr.index); + + if (state >= 0) { + if (info.ustate_ac == state) + snappendf(buf, cnt, " AC"); + if (info.ustate_dc == state) + snappendf(buf, cnt, " DC"); + if (info.pstate == state) + snappendf(buf, cnt, " *"); + } else { + if (info.ustate_ac < -1) + snappendf(buf, cnt, " AC"); + if (info.ustate_dc < -1) + snappendf(buf, cnt, " DC"); + } + + snappendf(buf, cnt, "\n"); + } + + return strlen(b); +} + +static ssize_t +nouveau_sysfs_pstate_set(struct device *d, struct device_attribute *a, + const char *buf, size_t count) +{ + struct nouveau_sysfs *sysfs = nouveau_sysfs(drm_device(d)); + struct nvif_control_pstate_user_v0 args = { .pwrsrc = -EINVAL }; + long value, ret; + char *tmp; + + if ((tmp = strchr(buf, '\n'))) + *tmp = '\0'; + + if (!strncasecmp(buf, "dc:", 3)) { + args.pwrsrc = 0; + buf += 3; + } else + if (!strncasecmp(buf, "ac:", 3)) { + args.pwrsrc = 1; + buf += 3; + } + + if (!strcasecmp(buf, "none")) + args.ustate = NVIF_CONTROL_PSTATE_USER_V0_STATE_UNKNOWN; + else + if (!strcasecmp(buf, "auto")) + args.ustate = NVIF_CONTROL_PSTATE_USER_V0_STATE_PERFMON; + else { + ret = kstrtol(buf, 16, &value); + if (ret) + return ret; + args.ustate = value; + } + + ret = nvif_mthd(&sysfs->ctrl, NVIF_CONTROL_PSTATE_USER, + &args, sizeof(args)); + if (ret < 0) + return ret; + + return count; +} + +static DEVICE_ATTR(pstate, S_IRUGO | S_IWUSR, + nouveau_sysfs_pstate_get, nouveau_sysfs_pstate_set); + +void +nouveau_sysfs_fini(struct drm_device *dev) +{ + struct nouveau_sysfs *sysfs = nouveau_sysfs(dev); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvif_device *device = &drm->device; + + if (sysfs && sysfs->ctrl.priv) { + device_remove_file(nv_device_base(nvxx_device(device)), &dev_attr_pstate); + nvif_object_fini(&sysfs->ctrl); + } + + drm->sysfs = NULL; + kfree(sysfs); +} + +int +nouveau_sysfs_init(struct drm_device *dev) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvif_device *device = &drm->device; + struct nouveau_sysfs *sysfs; + int ret; + + if (!nouveau_pstate) + return 0; + + sysfs = drm->sysfs = kzalloc(sizeof(*sysfs), GFP_KERNEL); + if (!sysfs) + return -ENOMEM; + + ret = nvif_object_init(nvif_object(device), NULL, NVDRM_CONTROL, + NVIF_IOCTL_NEW_V0_CONTROL, NULL, 0, + &sysfs->ctrl); + if (ret == 0) + device_create_file(nv_device_base(nvxx_device(device)), &dev_attr_pstate); + + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_sysfs.h b/kernel/drivers/gpu/drm/nouveau/nouveau_sysfs.h new file mode 100644 index 000000000..4e5ea9241 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_sysfs.h @@ -0,0 +1,21 @@ +#ifndef __NOUVEAU_SYSFS_H__ +#define __NOUVEAU_SYSFS_H__ + +#include "nouveau_drm.h" + +struct nouveau_sysfs { + struct nvif_object ctrl; +}; + +static inline struct nouveau_sysfs * +nouveau_sysfs(struct drm_device *dev) +{ + return nouveau_drm(dev)->sysfs; +} + +int nouveau_sysfs_init(struct drm_device *); +void nouveau_sysfs_fini(struct drm_device *); + +extern int nouveau_pstate; + +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_ttm.c b/kernel/drivers/gpu/drm/nouveau/nouveau_ttm.c new file mode 100644 index 000000000..18f449715 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_ttm.c @@ -0,0 +1,438 @@ +/* + * Copyright (c) 2007-2008 Tungsten Graphics, Inc., Cedar Park, TX., USA, + * All Rights Reserved. + * Copyright (c) 2009 VMware, Inc., Palo Alto, CA., USA, + * 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, sub license, + * and/or sell copies of the Software, and to 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 NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS 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 "nouveau_drm.h" +#include "nouveau_ttm.h" +#include "nouveau_gem.h" + +#include "drm_legacy.h" +static int +nouveau_vram_manager_init(struct ttm_mem_type_manager *man, unsigned long psize) +{ + struct nouveau_drm *drm = nouveau_bdev(man->bdev); + struct nvkm_fb *pfb = nvxx_fb(&drm->device); + man->priv = pfb; + return 0; +} + +static int +nouveau_vram_manager_fini(struct ttm_mem_type_manager *man) +{ + man->priv = NULL; + return 0; +} + +static inline void +nvkm_mem_node_cleanup(struct nvkm_mem *node) +{ + if (node->vma[0].node) { + nvkm_vm_unmap(&node->vma[0]); + nvkm_vm_put(&node->vma[0]); + } + + if (node->vma[1].node) { + nvkm_vm_unmap(&node->vma[1]); + nvkm_vm_put(&node->vma[1]); + } +} + +static void +nouveau_vram_manager_del(struct ttm_mem_type_manager *man, + struct ttm_mem_reg *mem) +{ + struct nouveau_drm *drm = nouveau_bdev(man->bdev); + struct nvkm_fb *pfb = nvxx_fb(&drm->device); + nvkm_mem_node_cleanup(mem->mm_node); + pfb->ram->put(pfb, (struct nvkm_mem **)&mem->mm_node); +} + +static int +nouveau_vram_manager_new(struct ttm_mem_type_manager *man, + struct ttm_buffer_object *bo, + const struct ttm_place *place, + struct ttm_mem_reg *mem) +{ + struct nouveau_drm *drm = nouveau_bdev(man->bdev); + struct nvkm_fb *pfb = nvxx_fb(&drm->device); + struct nouveau_bo *nvbo = nouveau_bo(bo); + struct nvkm_mem *node; + u32 size_nc = 0; + int ret; + + if (drm->device.info.ram_size == 0) + return -ENOMEM; + + if (nvbo->tile_flags & NOUVEAU_GEM_TILE_NONCONTIG) + size_nc = 1 << nvbo->page_shift; + + ret = pfb->ram->get(pfb, mem->num_pages << PAGE_SHIFT, + mem->page_alignment << PAGE_SHIFT, size_nc, + (nvbo->tile_flags >> 8) & 0x3ff, &node); + if (ret) { + mem->mm_node = NULL; + return (ret == -ENOSPC) ? 0 : ret; + } + + node->page_shift = nvbo->page_shift; + + mem->mm_node = node; + mem->start = node->offset >> PAGE_SHIFT; + return 0; +} + +static void +nouveau_vram_manager_debug(struct ttm_mem_type_manager *man, const char *prefix) +{ + struct nvkm_fb *pfb = man->priv; + struct nvkm_mm *mm = &pfb->vram; + struct nvkm_mm_node *r; + u32 total = 0, free = 0; + + mutex_lock(&nv_subdev(pfb)->mutex); + list_for_each_entry(r, &mm->nodes, nl_entry) { + printk(KERN_DEBUG "%s %d: 0x%010llx 0x%010llx\n", + prefix, r->type, ((u64)r->offset << 12), + (((u64)r->offset + r->length) << 12)); + + total += r->length; + if (!r->type) + free += r->length; + } + mutex_unlock(&nv_subdev(pfb)->mutex); + + printk(KERN_DEBUG "%s total: 0x%010llx free: 0x%010llx\n", + prefix, (u64)total << 12, (u64)free << 12); + printk(KERN_DEBUG "%s block: 0x%08x\n", + prefix, mm->block_size << 12); +} + +const struct ttm_mem_type_manager_func nouveau_vram_manager = { + nouveau_vram_manager_init, + nouveau_vram_manager_fini, + nouveau_vram_manager_new, + nouveau_vram_manager_del, + nouveau_vram_manager_debug +}; + +static int +nouveau_gart_manager_init(struct ttm_mem_type_manager *man, unsigned long psize) +{ + return 0; +} + +static int +nouveau_gart_manager_fini(struct ttm_mem_type_manager *man) +{ + return 0; +} + +static void +nouveau_gart_manager_del(struct ttm_mem_type_manager *man, + struct ttm_mem_reg *mem) +{ + nvkm_mem_node_cleanup(mem->mm_node); + kfree(mem->mm_node); + mem->mm_node = NULL; +} + +static int +nouveau_gart_manager_new(struct ttm_mem_type_manager *man, + struct ttm_buffer_object *bo, + const struct ttm_place *place, + struct ttm_mem_reg *mem) +{ + struct nouveau_drm *drm = nouveau_bdev(bo->bdev); + struct nouveau_bo *nvbo = nouveau_bo(bo); + struct nvkm_mem *node; + + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) + return -ENOMEM; + + node->page_shift = 12; + + switch (drm->device.info.family) { + case NV_DEVICE_INFO_V0_TESLA: + if (drm->device.info.chipset != 0x50) + node->memtype = (nvbo->tile_flags & 0x7f00) >> 8; + break; + case NV_DEVICE_INFO_V0_FERMI: + case NV_DEVICE_INFO_V0_KEPLER: + node->memtype = (nvbo->tile_flags & 0xff00) >> 8; + break; + default: + break; + } + + mem->mm_node = node; + mem->start = 0; + return 0; +} + +static void +nouveau_gart_manager_debug(struct ttm_mem_type_manager *man, const char *prefix) +{ +} + +const struct ttm_mem_type_manager_func nouveau_gart_manager = { + nouveau_gart_manager_init, + nouveau_gart_manager_fini, + nouveau_gart_manager_new, + nouveau_gart_manager_del, + nouveau_gart_manager_debug +}; + +/*XXX*/ +#include +static int +nv04_gart_manager_init(struct ttm_mem_type_manager *man, unsigned long psize) +{ + struct nouveau_drm *drm = nouveau_bdev(man->bdev); + struct nvkm_mmu *mmu = nvxx_mmu(&drm->device); + struct nv04_mmu_priv *priv = (void *)mmu; + struct nvkm_vm *vm = NULL; + nvkm_vm_ref(priv->vm, &vm, NULL); + man->priv = vm; + return 0; +} + +static int +nv04_gart_manager_fini(struct ttm_mem_type_manager *man) +{ + struct nvkm_vm *vm = man->priv; + nvkm_vm_ref(NULL, &vm, NULL); + man->priv = NULL; + return 0; +} + +static void +nv04_gart_manager_del(struct ttm_mem_type_manager *man, struct ttm_mem_reg *mem) +{ + struct nvkm_mem *node = mem->mm_node; + if (node->vma[0].node) + nvkm_vm_put(&node->vma[0]); + kfree(mem->mm_node); + mem->mm_node = NULL; +} + +static int +nv04_gart_manager_new(struct ttm_mem_type_manager *man, + struct ttm_buffer_object *bo, + const struct ttm_place *place, + struct ttm_mem_reg *mem) +{ + struct nvkm_mem *node; + int ret; + + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) + return -ENOMEM; + + node->page_shift = 12; + + ret = nvkm_vm_get(man->priv, mem->num_pages << 12, node->page_shift, + NV_MEM_ACCESS_RW, &node->vma[0]); + if (ret) { + kfree(node); + return ret; + } + + mem->mm_node = node; + mem->start = node->vma[0].offset >> PAGE_SHIFT; + return 0; +} + +static void +nv04_gart_manager_debug(struct ttm_mem_type_manager *man, const char *prefix) +{ +} + +const struct ttm_mem_type_manager_func nv04_gart_manager = { + nv04_gart_manager_init, + nv04_gart_manager_fini, + nv04_gart_manager_new, + nv04_gart_manager_del, + nv04_gart_manager_debug +}; + +int +nouveau_ttm_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct drm_file *file_priv = filp->private_data; + struct nouveau_drm *drm = nouveau_drm(file_priv->minor->dev); + + if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET)) + return drm_legacy_mmap(filp, vma); + + return ttm_bo_mmap(filp, vma, &drm->ttm.bdev); +} + +static int +nouveau_ttm_mem_global_init(struct drm_global_reference *ref) +{ + return ttm_mem_global_init(ref->object); +} + +static void +nouveau_ttm_mem_global_release(struct drm_global_reference *ref) +{ + ttm_mem_global_release(ref->object); +} + +int +nouveau_ttm_global_init(struct nouveau_drm *drm) +{ + struct drm_global_reference *global_ref; + int ret; + + global_ref = &drm->ttm.mem_global_ref; + global_ref->global_type = DRM_GLOBAL_TTM_MEM; + global_ref->size = sizeof(struct ttm_mem_global); + global_ref->init = &nouveau_ttm_mem_global_init; + global_ref->release = &nouveau_ttm_mem_global_release; + + ret = drm_global_item_ref(global_ref); + if (unlikely(ret != 0)) { + DRM_ERROR("Failed setting up TTM memory accounting\n"); + drm->ttm.mem_global_ref.release = NULL; + return ret; + } + + drm->ttm.bo_global_ref.mem_glob = global_ref->object; + global_ref = &drm->ttm.bo_global_ref.ref; + global_ref->global_type = DRM_GLOBAL_TTM_BO; + global_ref->size = sizeof(struct ttm_bo_global); + global_ref->init = &ttm_bo_global_init; + global_ref->release = &ttm_bo_global_release; + + ret = drm_global_item_ref(global_ref); + if (unlikely(ret != 0)) { + DRM_ERROR("Failed setting up TTM BO subsystem\n"); + drm_global_item_unref(&drm->ttm.mem_global_ref); + drm->ttm.mem_global_ref.release = NULL; + return ret; + } + + return 0; +} + +void +nouveau_ttm_global_release(struct nouveau_drm *drm) +{ + if (drm->ttm.mem_global_ref.release == NULL) + return; + + drm_global_item_unref(&drm->ttm.bo_global_ref.ref); + drm_global_item_unref(&drm->ttm.mem_global_ref); + drm->ttm.mem_global_ref.release = NULL; +} + +int +nouveau_ttm_init(struct nouveau_drm *drm) +{ + struct drm_device *dev = drm->dev; + u32 bits; + int ret; + + bits = nvxx_mmu(&drm->device)->dma_bits; + if (nv_device_is_pci(nvxx_device(&drm->device))) { + if (drm->agp.stat == ENABLED || + !pci_dma_supported(dev->pdev, DMA_BIT_MASK(bits))) + bits = 32; + + ret = pci_set_dma_mask(dev->pdev, DMA_BIT_MASK(bits)); + if (ret) + return ret; + + ret = pci_set_consistent_dma_mask(dev->pdev, + DMA_BIT_MASK(bits)); + if (ret) + pci_set_consistent_dma_mask(dev->pdev, + DMA_BIT_MASK(32)); + } + + ret = nouveau_ttm_global_init(drm); + if (ret) + return ret; + + ret = ttm_bo_device_init(&drm->ttm.bdev, + drm->ttm.bo_global_ref.ref.object, + &nouveau_bo_driver, + dev->anon_inode->i_mapping, + DRM_FILE_PAGE_OFFSET, + bits <= 32 ? true : false); + if (ret) { + NV_ERROR(drm, "error initialising bo driver, %d\n", ret); + return ret; + } + + /* VRAM init */ + drm->gem.vram_available = drm->device.info.ram_user; + + ret = ttm_bo_init_mm(&drm->ttm.bdev, TTM_PL_VRAM, + drm->gem.vram_available >> PAGE_SHIFT); + if (ret) { + NV_ERROR(drm, "VRAM mm init failed, %d\n", ret); + return ret; + } + + drm->ttm.mtrr = arch_phys_wc_add(nv_device_resource_start(nvxx_device(&drm->device), 1), + nv_device_resource_len(nvxx_device(&drm->device), 1)); + + /* GART init */ + if (drm->agp.stat != ENABLED) { + drm->gem.gart_available = nvxx_mmu(&drm->device)->limit; + } else { + drm->gem.gart_available = drm->agp.size; + } + + ret = ttm_bo_init_mm(&drm->ttm.bdev, TTM_PL_TT, + drm->gem.gart_available >> PAGE_SHIFT); + if (ret) { + NV_ERROR(drm, "GART mm init failed, %d\n", ret); + return ret; + } + + NV_INFO(drm, "VRAM: %d MiB\n", (u32)(drm->gem.vram_available >> 20)); + NV_INFO(drm, "GART: %d MiB\n", (u32)(drm->gem.gart_available >> 20)); + return 0; +} + +void +nouveau_ttm_fini(struct nouveau_drm *drm) +{ + mutex_lock(&drm->dev->struct_mutex); + ttm_bo_clean_mm(&drm->ttm.bdev, TTM_PL_VRAM); + ttm_bo_clean_mm(&drm->ttm.bdev, TTM_PL_TT); + mutex_unlock(&drm->dev->struct_mutex); + + ttm_bo_device_release(&drm->ttm.bdev); + + nouveau_ttm_global_release(drm); + + arch_phys_wc_del(drm->ttm.mtrr); + drm->ttm.mtrr = 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_ttm.h b/kernel/drivers/gpu/drm/nouveau/nouveau_ttm.h new file mode 100644 index 000000000..25b0de413 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_ttm.h @@ -0,0 +1,25 @@ +#ifndef __NOUVEAU_TTM_H__ +#define __NOUVEAU_TTM_H__ + +static inline struct nouveau_drm * +nouveau_bdev(struct ttm_bo_device *bd) +{ + return container_of(bd, struct nouveau_drm, ttm.bdev); +} + +extern const struct ttm_mem_type_manager_func nouveau_vram_manager; +extern const struct ttm_mem_type_manager_func nouveau_gart_manager; +extern const struct ttm_mem_type_manager_func nv04_gart_manager; + +struct ttm_tt *nouveau_sgdma_create_ttm(struct ttm_bo_device *, + unsigned long size, u32 page_flags, + struct page *dummy_read_page); + +int nouveau_ttm_init(struct nouveau_drm *drm); +void nouveau_ttm_fini(struct nouveau_drm *drm); +int nouveau_ttm_mmap(struct file *, struct vm_area_struct *); + +int nouveau_ttm_global_init(struct nouveau_drm *); +void nouveau_ttm_global_release(struct nouveau_drm *); + +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_usif.c b/kernel/drivers/gpu/drm/nouveau/nouveau_usif.c new file mode 100644 index 000000000..cb1182d7e --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_usif.c @@ -0,0 +1,384 @@ +/* + * 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 "nouveau_drm.h" +#include "nouveau_usif.h" + +#include +#include +#include +#include +#include + +struct usif_notify_p { + struct drm_pending_event base; + struct { + struct drm_event base; + u8 data[]; + } e; +}; + +struct usif_notify { + struct list_head head; + atomic_t enabled; + u32 handle; + u16 reply; + u8 route; + u64 token; + struct usif_notify_p *p; +}; + +static inline struct usif_notify * +usif_notify_find(struct drm_file *filp, u32 handle) +{ + struct nouveau_cli *cli = nouveau_cli(filp); + struct usif_notify *ntfy; + list_for_each_entry(ntfy, &cli->notifys, head) { + if (ntfy->handle == handle) + return ntfy; + } + return NULL; +} + +static inline void +usif_notify_dtor(struct usif_notify *ntfy) +{ + list_del(&ntfy->head); + kfree(ntfy); +} + +int +usif_notify(const void *header, u32 length, const void *data, u32 size) +{ + struct usif_notify *ntfy = NULL; + const union { + struct nvif_notify_rep_v0 v0; + } *rep = header; + struct drm_device *dev; + struct drm_file *filp; + unsigned long flags; + + if (length == sizeof(rep->v0) && rep->v0.version == 0) { + if (WARN_ON(!(ntfy = (void *)(unsigned long)rep->v0.token))) + return NVIF_NOTIFY_DROP; + BUG_ON(rep->v0.route != NVDRM_NOTIFY_USIF); + } else + if (WARN_ON(1)) + return NVIF_NOTIFY_DROP; + + if (WARN_ON(!ntfy->p || ntfy->reply != (length + size))) + return NVIF_NOTIFY_DROP; + filp = ntfy->p->base.file_priv; + dev = filp->minor->dev; + + memcpy(&ntfy->p->e.data[0], header, length); + memcpy(&ntfy->p->e.data[length], data, size); + switch (rep->v0.version) { + case 0: { + struct nvif_notify_rep_v0 *rep = (void *)ntfy->p->e.data; + rep->route = ntfy->route; + rep->token = ntfy->token; + } + break; + default: + BUG_ON(1); + break; + } + + spin_lock_irqsave(&dev->event_lock, flags); + if (!WARN_ON(filp->event_space < ntfy->p->e.base.length)) { + list_add_tail(&ntfy->p->base.link, &filp->event_list); + filp->event_space -= ntfy->p->e.base.length; + } + wake_up_interruptible(&filp->event_wait); + spin_unlock_irqrestore(&dev->event_lock, flags); + atomic_set(&ntfy->enabled, 0); + return NVIF_NOTIFY_DROP; +} + +static int +usif_notify_new(struct drm_file *f, void *data, u32 size, void *argv, u32 argc) +{ + struct nouveau_cli *cli = nouveau_cli(f); + struct nvif_client *client = &cli->base; + union { + struct nvif_ioctl_ntfy_new_v0 v0; + } *args = data; + union { + struct nvif_notify_req_v0 v0; + } *req; + struct usif_notify *ntfy; + int ret; + + if (nvif_unpack(args->v0, 0, 0, true)) { + if (usif_notify_find(f, args->v0.index)) + return -EEXIST; + } else + return ret; + req = data; + + if (!(ntfy = kmalloc(sizeof(*ntfy), GFP_KERNEL))) + return -ENOMEM; + atomic_set(&ntfy->enabled, 0); + + if (nvif_unpack(req->v0, 0, 0, true)) { + ntfy->reply = sizeof(struct nvif_notify_rep_v0) + req->v0.reply; + ntfy->route = req->v0.route; + ntfy->token = req->v0.token; + req->v0.route = NVDRM_NOTIFY_USIF; + req->v0.token = (unsigned long)(void *)ntfy; + ret = nvif_client_ioctl(client, argv, argc); + req->v0.token = ntfy->token; + req->v0.route = ntfy->route; + ntfy->handle = args->v0.index; + } + + if (ret == 0) + list_add(&ntfy->head, &cli->notifys); + if (ret) + kfree(ntfy); + return ret; +} + +static int +usif_notify_del(struct drm_file *f, void *data, u32 size, void *argv, u32 argc) +{ + struct nouveau_cli *cli = nouveau_cli(f); + struct nvif_client *client = &cli->base; + union { + struct nvif_ioctl_ntfy_del_v0 v0; + } *args = data; + struct usif_notify *ntfy; + int ret; + + if (nvif_unpack(args->v0, 0, 0, true)) { + if (!(ntfy = usif_notify_find(f, args->v0.index))) + return -ENOENT; + } else + return ret; + + ret = nvif_client_ioctl(client, argv, argc); + if (ret == 0) + usif_notify_dtor(ntfy); + return ret; +} + +static int +usif_notify_get(struct drm_file *f, void *data, u32 size, void *argv, u32 argc) +{ + struct nouveau_cli *cli = nouveau_cli(f); + struct nvif_client *client = &cli->base; + union { + struct nvif_ioctl_ntfy_del_v0 v0; + } *args = data; + struct usif_notify *ntfy; + int ret; + + if (nvif_unpack(args->v0, 0, 0, true)) { + if (!(ntfy = usif_notify_find(f, args->v0.index))) + return -ENOENT; + } else + return ret; + + if (atomic_xchg(&ntfy->enabled, 1)) + return 0; + + ntfy->p = kmalloc(sizeof(*ntfy->p) + ntfy->reply, GFP_KERNEL); + if (ret = -ENOMEM, !ntfy->p) + goto done; + ntfy->p->base.event = &ntfy->p->e.base; + ntfy->p->base.file_priv = f; + ntfy->p->base.pid = current->pid; + ntfy->p->base.destroy =(void(*)(struct drm_pending_event *))kfree; + ntfy->p->e.base.type = DRM_NOUVEAU_EVENT_NVIF; + ntfy->p->e.base.length = sizeof(ntfy->p->e.base) + ntfy->reply; + + ret = nvif_client_ioctl(client, argv, argc); +done: + if (ret) { + atomic_set(&ntfy->enabled, 0); + kfree(ntfy->p); + } + return ret; +} + +static int +usif_notify_put(struct drm_file *f, void *data, u32 size, void *argv, u32 argc) +{ + struct nouveau_cli *cli = nouveau_cli(f); + struct nvif_client *client = &cli->base; + union { + struct nvif_ioctl_ntfy_put_v0 v0; + } *args = data; + struct usif_notify *ntfy; + int ret; + + if (nvif_unpack(args->v0, 0, 0, true)) { + if (!(ntfy = usif_notify_find(f, args->v0.index))) + return -ENOENT; + } else + return ret; + + ret = nvif_client_ioctl(client, argv, argc); + if (ret == 0 && atomic_xchg(&ntfy->enabled, 0)) + kfree(ntfy->p); + return ret; +} + +struct usif_object { + struct list_head head; + struct list_head ntfy; + u8 route; + u64 token; +}; + +static void +usif_object_dtor(struct usif_object *object) +{ + list_del(&object->head); + kfree(object); +} + +static int +usif_object_new(struct drm_file *f, void *data, u32 size, void *argv, u32 argc) +{ + struct nouveau_cli *cli = nouveau_cli(f); + struct nvif_client *client = &cli->base; + union { + struct nvif_ioctl_new_v0 v0; + } *args = data; + struct usif_object *object; + int ret; + + if (!(object = kmalloc(sizeof(*object), GFP_KERNEL))) + return -ENOMEM; + list_add(&object->head, &cli->objects); + + if (nvif_unpack(args->v0, 0, 0, true)) { + object->route = args->v0.route; + object->token = args->v0.token; + args->v0.route = NVDRM_OBJECT_USIF; + args->v0.token = (unsigned long)(void *)object; + ret = nvif_client_ioctl(client, argv, argc); + args->v0.token = object->token; + args->v0.route = object->route; + } + + if (ret) + usif_object_dtor(object); + return ret; +} + +int +usif_ioctl(struct drm_file *filp, void __user *user, u32 argc) +{ + struct nouveau_cli *cli = nouveau_cli(filp); + struct nvif_client *client = &cli->base; + void *data = kmalloc(argc, GFP_KERNEL); + u32 size = argc; + union { + struct nvif_ioctl_v0 v0; + } *argv = data; + struct usif_object *object; + u8 owner; + int ret; + + if (ret = -ENOMEM, !argv) + goto done; + if (ret = -EFAULT, copy_from_user(argv, user, size)) + goto done; + + if (nvif_unpack(argv->v0, 0, 0, true)) { + /* block access to objects not created via this interface */ + owner = argv->v0.owner; + argv->v0.owner = NVDRM_OBJECT_USIF; + } else + goto done; + + mutex_lock(&cli->mutex); + switch (argv->v0.type) { + case NVIF_IOCTL_V0_NEW: + /* ... except if we're creating children */ + argv->v0.owner = NVIF_IOCTL_V0_OWNER_ANY; + ret = usif_object_new(filp, data, size, argv, argc); + break; + case NVIF_IOCTL_V0_NTFY_NEW: + ret = usif_notify_new(filp, data, size, argv, argc); + break; + case NVIF_IOCTL_V0_NTFY_DEL: + ret = usif_notify_del(filp, data, size, argv, argc); + break; + case NVIF_IOCTL_V0_NTFY_GET: + ret = usif_notify_get(filp, data, size, argv, argc); + break; + case NVIF_IOCTL_V0_NTFY_PUT: + ret = usif_notify_put(filp, data, size, argv, argc); + break; + default: + ret = nvif_client_ioctl(client, argv, argc); + break; + } + if (argv->v0.route == NVDRM_OBJECT_USIF) { + object = (void *)(unsigned long)argv->v0.token; + argv->v0.route = object->route; + argv->v0.token = object->token; + if (ret == 0 && argv->v0.type == NVIF_IOCTL_V0_DEL) { + list_del(&object->head); + kfree(object); + } + } else { + argv->v0.route = NVIF_IOCTL_V0_ROUTE_HIDDEN; + argv->v0.token = 0; + } + argv->v0.owner = owner; + mutex_unlock(&cli->mutex); + + if (copy_to_user(user, argv, argc)) + ret = -EFAULT; +done: + kfree(argv); + return ret; +} + +void +usif_client_fini(struct nouveau_cli *cli) +{ + struct usif_object *object, *otemp; + struct usif_notify *notify, *ntemp; + + list_for_each_entry_safe(notify, ntemp, &cli->notifys, head) { + usif_notify_dtor(notify); + } + + list_for_each_entry_safe(object, otemp, &cli->objects, head) { + usif_object_dtor(object); + } +} + +void +usif_client_init(struct nouveau_cli *cli) +{ + INIT_LIST_HEAD(&cli->objects); + INIT_LIST_HEAD(&cli->notifys); +} diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_usif.h b/kernel/drivers/gpu/drm/nouveau/nouveau_usif.h new file mode 100644 index 000000000..c037e3ae8 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_usif.h @@ -0,0 +1,9 @@ +#ifndef __NOUVEAU_USIF_H__ +#define __NOUVEAU_USIF_H__ + +void usif_client_init(struct nouveau_cli *); +void usif_client_fini(struct nouveau_cli *); +int usif_ioctl(struct drm_file *, void __user *, u32); +int usif_notify(const void *, u32, const void *, u32); + +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_vga.c b/kernel/drivers/gpu/drm/nouveau/nouveau_vga.c new file mode 100644 index 000000000..c7592ec8e --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_vga.c @@ -0,0 +1,129 @@ +#include +#include + +#include +#include + +#include "nouveau_drm.h" +#include "nouveau_acpi.h" +#include "nouveau_fbcon.h" +#include "nouveau_vga.h" + +static unsigned int +nouveau_vga_set_decode(void *priv, bool state) +{ + struct nvif_device *device = &nouveau_drm(priv)->device; + + if (device->info.family == NV_DEVICE_INFO_V0_CURIE && + device->info.chipset >= 0x4c) + nvif_wr32(device, 0x088060, state); + else + if (device->info.chipset >= 0x40) + nvif_wr32(device, 0x088054, state); + else + nvif_wr32(device, 0x001854, state); + + if (state) + return VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM | + VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM; + else + return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM; +} + +static void +nouveau_switcheroo_set_state(struct pci_dev *pdev, + enum vga_switcheroo_state state) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + + if ((nouveau_is_optimus() || nouveau_is_v1_dsm()) && state == VGA_SWITCHEROO_OFF) + return; + + if (state == VGA_SWITCHEROO_ON) { + printk(KERN_ERR "VGA switcheroo: switched nouveau on\n"); + dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; + nouveau_pmops_resume(&pdev->dev); + drm_kms_helper_poll_enable(dev); + dev->switch_power_state = DRM_SWITCH_POWER_ON; + } else { + printk(KERN_ERR "VGA switcheroo: switched nouveau off\n"); + dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; + drm_kms_helper_poll_disable(dev); + nouveau_switcheroo_optimus_dsm(); + nouveau_pmops_suspend(&pdev->dev); + dev->switch_power_state = DRM_SWITCH_POWER_OFF; + } +} + +static void +nouveau_switcheroo_reprobe(struct pci_dev *pdev) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + nouveau_fbcon_output_poll_changed(dev); +} + +static bool +nouveau_switcheroo_can_switch(struct pci_dev *pdev) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + + /* + * FIXME: open_count is protected by drm_global_mutex but that would lead to + * locking inversion with the driver load path. And the access here is + * completely racy anyway. So don't bother with locking for now. + */ + return dev->open_count == 0; +} + +static const struct vga_switcheroo_client_ops +nouveau_switcheroo_ops = { + .set_gpu_state = nouveau_switcheroo_set_state, + .reprobe = nouveau_switcheroo_reprobe, + .can_switch = nouveau_switcheroo_can_switch, +}; + +void +nouveau_vga_init(struct nouveau_drm *drm) +{ + struct drm_device *dev = drm->dev; + bool runtime = false; + + /* only relevant for PCI devices */ + if (!dev->pdev) + return; + + vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode); + + if (nouveau_runtime_pm == 1) + runtime = true; + if ((nouveau_runtime_pm == -1) && (nouveau_is_optimus() || nouveau_is_v1_dsm())) + runtime = true; + vga_switcheroo_register_client(dev->pdev, &nouveau_switcheroo_ops, runtime); + + if (runtime && nouveau_is_v1_dsm() && !nouveau_is_optimus()) + vga_switcheroo_init_domain_pm_ops(drm->dev->dev, &drm->vga_pm_domain); +} + +void +nouveau_vga_fini(struct nouveau_drm *drm) +{ + struct drm_device *dev = drm->dev; + bool runtime = false; + + if (nouveau_runtime_pm == 1) + runtime = true; + if ((nouveau_runtime_pm == -1) && (nouveau_is_optimus() || nouveau_is_v1_dsm())) + runtime = true; + + vga_switcheroo_unregister_client(dev->pdev); + if (runtime && nouveau_is_v1_dsm() && !nouveau_is_optimus()) + vga_switcheroo_fini_domain_pm_ops(drm->dev->dev); + vga_client_register(dev->pdev, NULL, NULL, NULL); +} + + +void +nouveau_vga_lastclose(struct drm_device *dev) +{ + vga_switcheroo_process_delayed_switch(); +} diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_vga.h b/kernel/drivers/gpu/drm/nouveau/nouveau_vga.h new file mode 100644 index 000000000..ea3ad6974 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nouveau_vga.h @@ -0,0 +1,8 @@ +#ifndef __NOUVEAU_VGA_H__ +#define __NOUVEAU_VGA_H__ + +void nouveau_vga_init(struct nouveau_drm *); +void nouveau_vga_fini(struct nouveau_drm *); +void nouveau_vga_lastclose(struct drm_device *dev); + +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nv04_fbcon.c b/kernel/drivers/gpu/drm/nouveau/nv04_fbcon.c new file mode 100644 index 000000000..4ef602c54 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nv04_fbcon.c @@ -0,0 +1,280 @@ +/* + * Copyright 2009 Ben Skeggs + * Copyright 2008 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 (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 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 "nouveau_drm.h" +#include "nouveau_dma.h" +#include "nouveau_fbcon.h" + +int +nv04_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region) +{ + struct nouveau_fbdev *nfbdev = info->par; + struct nouveau_drm *drm = nouveau_drm(nfbdev->dev); + struct nouveau_channel *chan = drm->channel; + int ret; + + ret = RING_SPACE(chan, 4); + if (ret) + return ret; + + BEGIN_NV04(chan, NvSubImageBlit, 0x0300, 3); + OUT_RING(chan, (region->sy << 16) | region->sx); + OUT_RING(chan, (region->dy << 16) | region->dx); + OUT_RING(chan, (region->height << 16) | region->width); + FIRE_RING(chan); + return 0; +} + +int +nv04_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect) +{ + struct nouveau_fbdev *nfbdev = info->par; + struct nouveau_drm *drm = nouveau_drm(nfbdev->dev); + struct nouveau_channel *chan = drm->channel; + int ret; + + ret = RING_SPACE(chan, 7); + if (ret) + return ret; + + BEGIN_NV04(chan, NvSubGdiRect, 0x02fc, 1); + OUT_RING(chan, (rect->rop != ROP_COPY) ? 1 : 3); + BEGIN_NV04(chan, NvSubGdiRect, 0x03fc, 1); + if (info->fix.visual == FB_VISUAL_TRUECOLOR || + info->fix.visual == FB_VISUAL_DIRECTCOLOR) + OUT_RING(chan, ((uint32_t *)info->pseudo_palette)[rect->color]); + else + OUT_RING(chan, rect->color); + BEGIN_NV04(chan, NvSubGdiRect, 0x0400, 2); + OUT_RING(chan, (rect->dx << 16) | rect->dy); + OUT_RING(chan, (rect->width << 16) | rect->height); + FIRE_RING(chan); + return 0; +} + +int +nv04_fbcon_imageblit(struct fb_info *info, const struct fb_image *image) +{ + struct nouveau_fbdev *nfbdev = info->par; + struct nouveau_drm *drm = nouveau_drm(nfbdev->dev); + struct nouveau_channel *chan = drm->channel; + uint32_t fg; + uint32_t bg; + uint32_t dsize; + uint32_t width; + uint32_t *data = (uint32_t *)image->data; + int ret; + + if (image->depth != 1) + return -ENODEV; + + ret = RING_SPACE(chan, 8); + if (ret) + return ret; + + width = ALIGN(image->width, 8); + dsize = ALIGN(width * image->height, 32) >> 5; + + if (info->fix.visual == FB_VISUAL_TRUECOLOR || + info->fix.visual == FB_VISUAL_DIRECTCOLOR) { + fg = ((uint32_t *) info->pseudo_palette)[image->fg_color]; + bg = ((uint32_t *) info->pseudo_palette)[image->bg_color]; + } else { + fg = image->fg_color; + bg = image->bg_color; + } + + BEGIN_NV04(chan, NvSubGdiRect, 0x0be4, 7); + OUT_RING(chan, (image->dy << 16) | (image->dx & 0xffff)); + OUT_RING(chan, ((image->dy + image->height) << 16) | + ((image->dx + image->width) & 0xffff)); + OUT_RING(chan, bg); + OUT_RING(chan, fg); + OUT_RING(chan, (image->height << 16) | width); + OUT_RING(chan, (image->height << 16) | image->width); + OUT_RING(chan, (image->dy << 16) | (image->dx & 0xffff)); + + while (dsize) { + int iter_len = dsize > 128 ? 128 : dsize; + + ret = RING_SPACE(chan, iter_len + 1); + if (ret) + return ret; + + BEGIN_NV04(chan, NvSubGdiRect, 0x0c00, iter_len); + OUT_RINGp(chan, data, iter_len); + data += iter_len; + dsize -= iter_len; + } + + FIRE_RING(chan); + return 0; +} + +int +nv04_fbcon_accel_init(struct fb_info *info) +{ + struct nouveau_fbdev *nfbdev = info->par; + struct drm_device *dev = nfbdev->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_channel *chan = drm->channel; + struct nvif_device *device = &drm->device; + int surface_fmt, pattern_fmt, rect_fmt; + int ret; + + switch (info->var.bits_per_pixel) { + case 8: + surface_fmt = 1; + pattern_fmt = 3; + rect_fmt = 3; + break; + case 16: + surface_fmt = 4; + pattern_fmt = 1; + rect_fmt = 1; + break; + case 32: + switch (info->var.transp.length) { + case 0: /* depth 24 */ + case 8: /* depth 32 */ + break; + default: + return -EINVAL; + } + + surface_fmt = 6; + pattern_fmt = 3; + rect_fmt = 3; + break; + default: + return -EINVAL; + } + + ret = nvif_object_init(chan->object, NULL, 0x0062, + device->info.family >= NV_DEVICE_INFO_V0_CELSIUS ? + 0x0062 : 0x0042, NULL, 0, &nfbdev->surf2d); + if (ret) + return ret; + + ret = nvif_object_init(chan->object, NULL, 0x0019, 0x0019, NULL, 0, + &nfbdev->clip); + if (ret) + return ret; + + ret = nvif_object_init(chan->object, NULL, 0x0043, 0x0043, NULL, 0, + &nfbdev->rop); + if (ret) + return ret; + + ret = nvif_object_init(chan->object, NULL, 0x0044, 0x0044, NULL, 0, + &nfbdev->patt); + if (ret) + return ret; + + ret = nvif_object_init(chan->object, NULL, 0x004a, 0x004a, NULL, 0, + &nfbdev->gdi); + if (ret) + return ret; + + ret = nvif_object_init(chan->object, NULL, 0x005f, + device->info.chipset >= 0x11 ? 0x009f : 0x005f, + NULL, 0, &nfbdev->blit); + if (ret) + return ret; + + if (RING_SPACE(chan, 49)) { + nouveau_fbcon_gpu_lockup(info); + return 0; + } + + BEGIN_NV04(chan, NvSubCtxSurf2D, 0x0000, 1); + OUT_RING(chan, nfbdev->surf2d.handle); + BEGIN_NV04(chan, NvSubCtxSurf2D, 0x0184, 2); + OUT_RING(chan, chan->vram.handle); + OUT_RING(chan, chan->vram.handle); + BEGIN_NV04(chan, NvSubCtxSurf2D, 0x0300, 4); + OUT_RING(chan, surface_fmt); + OUT_RING(chan, info->fix.line_length | (info->fix.line_length << 16)); + OUT_RING(chan, info->fix.smem_start - dev->mode_config.fb_base); + OUT_RING(chan, info->fix.smem_start - dev->mode_config.fb_base); + + BEGIN_NV04(chan, NvSubCtxSurf2D, 0x0000, 1); + OUT_RING(chan, nfbdev->rop.handle); + BEGIN_NV04(chan, NvSubCtxSurf2D, 0x0300, 1); + OUT_RING(chan, 0x55); + + BEGIN_NV04(chan, NvSubCtxSurf2D, 0x0000, 1); + OUT_RING(chan, nfbdev->patt.handle); + BEGIN_NV04(chan, NvSubCtxSurf2D, 0x0300, 8); + OUT_RING(chan, pattern_fmt); +#ifdef __BIG_ENDIAN + OUT_RING(chan, 2); +#else + OUT_RING(chan, 1); +#endif + OUT_RING(chan, 0); + OUT_RING(chan, 1); + OUT_RING(chan, ~0); + OUT_RING(chan, ~0); + OUT_RING(chan, ~0); + OUT_RING(chan, ~0); + + BEGIN_NV04(chan, NvSubCtxSurf2D, 0x0000, 1); + OUT_RING(chan, nfbdev->clip.handle); + BEGIN_NV04(chan, NvSubCtxSurf2D, 0x0300, 2); + OUT_RING(chan, 0); + OUT_RING(chan, (info->var.yres_virtual << 16) | info->var.xres_virtual); + + BEGIN_NV04(chan, NvSubImageBlit, 0x0000, 1); + OUT_RING(chan, nfbdev->blit.handle); + BEGIN_NV04(chan, NvSubImageBlit, 0x019c, 1); + OUT_RING(chan, nfbdev->surf2d.handle); + BEGIN_NV04(chan, NvSubImageBlit, 0x02fc, 1); + OUT_RING(chan, 3); + if (device->info.chipset >= 0x11 /*XXX: oclass == 0x009f*/) { + BEGIN_NV04(chan, NvSubImageBlit, 0x0120, 3); + OUT_RING(chan, 0); + OUT_RING(chan, 1); + OUT_RING(chan, 2); + } + + BEGIN_NV04(chan, NvSubGdiRect, 0x0000, 1); + OUT_RING(chan, nfbdev->gdi.handle); + BEGIN_NV04(chan, NvSubGdiRect, 0x0198, 1); + OUT_RING(chan, nfbdev->surf2d.handle); + BEGIN_NV04(chan, NvSubGdiRect, 0x0188, 2); + OUT_RING(chan, nfbdev->patt.handle); + OUT_RING(chan, nfbdev->rop.handle); + BEGIN_NV04(chan, NvSubGdiRect, 0x0304, 1); + OUT_RING(chan, 1); + BEGIN_NV04(chan, NvSubGdiRect, 0x0300, 1); + OUT_RING(chan, rect_fmt); + BEGIN_NV04(chan, NvSubGdiRect, 0x02fc, 1); + OUT_RING(chan, 3); + + FIRE_RING(chan); + + return 0; +} + diff --git a/kernel/drivers/gpu/drm/nouveau/nv04_fence.c b/kernel/drivers/gpu/drm/nouveau/nv04_fence.c new file mode 100644 index 000000000..c2e05e64c --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nv04_fence.c @@ -0,0 +1,111 @@ +/* + * 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 "nouveau_drm.h" +#include "nouveau_dma.h" +#include "nouveau_fence.h" + +struct nv04_fence_chan { + struct nouveau_fence_chan base; +}; + +struct nv04_fence_priv { + struct nouveau_fence_priv base; +}; + +static int +nv04_fence_emit(struct nouveau_fence *fence) +{ + struct nouveau_channel *chan = fence->channel; + int ret = RING_SPACE(chan, 2); + if (ret == 0) { + BEGIN_NV04(chan, NvSubSw, 0x0150, 1); + OUT_RING (chan, fence->base.seqno); + FIRE_RING (chan); + } + return ret; +} + +static int +nv04_fence_sync(struct nouveau_fence *fence, + struct nouveau_channel *prev, struct nouveau_channel *chan) +{ + return -ENODEV; +} + +static u32 +nv04_fence_read(struct nouveau_channel *chan) +{ + struct nvkm_fifo_chan *fifo = nvxx_fifo_chan(chan);; + return atomic_read(&fifo->refcnt); +} + +static void +nv04_fence_context_del(struct nouveau_channel *chan) +{ + struct nv04_fence_chan *fctx = chan->fence; + nouveau_fence_context_del(&fctx->base); + chan->fence = NULL; + nouveau_fence_context_free(&fctx->base); +} + +static int +nv04_fence_context_new(struct nouveau_channel *chan) +{ + struct nv04_fence_chan *fctx = kzalloc(sizeof(*fctx), GFP_KERNEL); + if (fctx) { + nouveau_fence_context_new(chan, &fctx->base); + fctx->base.emit = nv04_fence_emit; + fctx->base.sync = nv04_fence_sync; + fctx->base.read = nv04_fence_read; + chan->fence = fctx; + return 0; + } + return -ENOMEM; +} + +static void +nv04_fence_destroy(struct nouveau_drm *drm) +{ + struct nv04_fence_priv *priv = drm->fence; + drm->fence = NULL; + kfree(priv); +} + +int +nv04_fence_create(struct nouveau_drm *drm) +{ + struct nv04_fence_priv *priv; + + priv = drm->fence = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base.dtor = nv04_fence_destroy; + priv->base.context_new = nv04_fence_context_new; + priv->base.context_del = nv04_fence_context_del; + priv->base.contexts = 15; + priv->base.context_base = fence_context_alloc(priv->base.contexts); + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nv10_fence.c b/kernel/drivers/gpu/drm/nouveau/nv10_fence.c new file mode 100644 index 000000000..5e1ea1cdc --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nv10_fence.c @@ -0,0 +1,113 @@ +/* + * 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 "nouveau_drm.h" +#include "nouveau_dma.h" +#include "nv10_fence.h" + +int +nv10_fence_emit(struct nouveau_fence *fence) +{ + struct nouveau_channel *chan = fence->channel; + int ret = RING_SPACE(chan, 2); + if (ret == 0) { + BEGIN_NV04(chan, 0, NV10_SUBCHAN_REF_CNT, 1); + OUT_RING (chan, fence->base.seqno); + FIRE_RING (chan); + } + return ret; +} + + +static int +nv10_fence_sync(struct nouveau_fence *fence, + struct nouveau_channel *prev, struct nouveau_channel *chan) +{ + return -ENODEV; +} + +u32 +nv10_fence_read(struct nouveau_channel *chan) +{ + return nvif_rd32(chan, 0x0048); +} + +void +nv10_fence_context_del(struct nouveau_channel *chan) +{ + struct nv10_fence_chan *fctx = chan->fence; + int i; + nouveau_fence_context_del(&fctx->base); + for (i = 0; i < ARRAY_SIZE(fctx->head); i++) + nvif_object_fini(&fctx->head[i]); + nvif_object_fini(&fctx->sema); + chan->fence = NULL; + nouveau_fence_context_free(&fctx->base); +} + +int +nv10_fence_context_new(struct nouveau_channel *chan) +{ + struct nv10_fence_chan *fctx; + + fctx = chan->fence = kzalloc(sizeof(*fctx), GFP_KERNEL); + if (!fctx) + return -ENOMEM; + + nouveau_fence_context_new(chan, &fctx->base); + fctx->base.emit = nv10_fence_emit; + fctx->base.read = nv10_fence_read; + fctx->base.sync = nv10_fence_sync; + return 0; +} + +void +nv10_fence_destroy(struct nouveau_drm *drm) +{ + struct nv10_fence_priv *priv = drm->fence; + nouveau_bo_unmap(priv->bo); + if (priv->bo) + nouveau_bo_unpin(priv->bo); + nouveau_bo_ref(NULL, &priv->bo); + drm->fence = NULL; + kfree(priv); +} + +int +nv10_fence_create(struct nouveau_drm *drm) +{ + struct nv10_fence_priv *priv; + + priv = drm->fence = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base.dtor = nv10_fence_destroy; + priv->base.context_new = nv10_fence_context_new; + priv->base.context_del = nv10_fence_context_del; + priv->base.contexts = 31; + priv->base.context_base = fence_context_alloc(priv->base.contexts); + spin_lock_init(&priv->lock); + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nv10_fence.h b/kernel/drivers/gpu/drm/nouveau/nv10_fence.h new file mode 100644 index 000000000..a87259f39 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nv10_fence.h @@ -0,0 +1,20 @@ +#ifndef __NV10_FENCE_H_ +#define __NV10_FENCE_H_ + +#include "nouveau_fence.h" +#include "nouveau_bo.h" + +struct nv10_fence_chan { + struct nouveau_fence_chan base; + struct nvif_object sema; + struct nvif_object head[4]; +}; + +struct nv10_fence_priv { + struct nouveau_fence_priv base; + struct nouveau_bo *bo; + spinlock_t lock; + u32 sequence; +}; + +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nv17_fence.c b/kernel/drivers/gpu/drm/nouveau/nv17_fence.c new file mode 100644 index 000000000..57860cfa1 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nv17_fence.c @@ -0,0 +1,151 @@ +/* + * 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 "nouveau_drm.h" +#include "nouveau_dma.h" +#include "nv10_fence.h" + +int +nv17_fence_sync(struct nouveau_fence *fence, + struct nouveau_channel *prev, struct nouveau_channel *chan) +{ + struct nouveau_cli *cli = (void *)nvif_client(&prev->device->base); + struct nv10_fence_priv *priv = chan->drm->fence; + struct nv10_fence_chan *fctx = chan->fence; + u32 value; + int ret; + + if (!mutex_trylock(&cli->mutex)) + return -EBUSY; + + spin_lock(&priv->lock); + value = priv->sequence; + priv->sequence += 2; + spin_unlock(&priv->lock); + + ret = RING_SPACE(prev, 5); + if (!ret) { + BEGIN_NV04(prev, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 4); + OUT_RING (prev, fctx->sema.handle); + OUT_RING (prev, 0); + OUT_RING (prev, value + 0); + OUT_RING (prev, value + 1); + FIRE_RING (prev); + } + + if (!ret && !(ret = RING_SPACE(chan, 5))) { + BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 4); + OUT_RING (chan, fctx->sema.handle); + OUT_RING (chan, 0); + OUT_RING (chan, value + 1); + OUT_RING (chan, value + 2); + FIRE_RING (chan); + } + + mutex_unlock(&cli->mutex); + return 0; +} + +static int +nv17_fence_context_new(struct nouveau_channel *chan) +{ + struct nv10_fence_priv *priv = chan->drm->fence; + struct nv10_fence_chan *fctx; + struct ttm_mem_reg *mem = &priv->bo->bo.mem; + u32 start = mem->start * PAGE_SIZE; + u32 limit = start + mem->size - 1; + int ret = 0; + + fctx = chan->fence = kzalloc(sizeof(*fctx), GFP_KERNEL); + if (!fctx) + return -ENOMEM; + + nouveau_fence_context_new(chan, &fctx->base); + fctx->base.emit = nv10_fence_emit; + fctx->base.read = nv10_fence_read; + fctx->base.sync = nv17_fence_sync; + + ret = nvif_object_init(chan->object, NULL, NvSema, NV_DMA_FROM_MEMORY, + &(struct nv_dma_v0) { + .target = NV_DMA_V0_TARGET_VRAM, + .access = NV_DMA_V0_ACCESS_RDWR, + .start = start, + .limit = limit, + }, sizeof(struct nv_dma_v0), + &fctx->sema); + if (ret) + nv10_fence_context_del(chan); + return ret; +} + +void +nv17_fence_resume(struct nouveau_drm *drm) +{ + struct nv10_fence_priv *priv = drm->fence; + + nouveau_bo_wr32(priv->bo, 0, priv->sequence); +} + +int +nv17_fence_create(struct nouveau_drm *drm) +{ + struct nv10_fence_priv *priv; + int ret = 0; + + priv = drm->fence = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base.dtor = nv10_fence_destroy; + priv->base.resume = nv17_fence_resume; + priv->base.context_new = nv17_fence_context_new; + priv->base.context_del = nv10_fence_context_del; + priv->base.contexts = 31; + priv->base.context_base = fence_context_alloc(priv->base.contexts); + spin_lock_init(&priv->lock); + + ret = nouveau_bo_new(drm->dev, 4096, 0x1000, TTM_PL_FLAG_VRAM, + 0, 0x0000, NULL, NULL, &priv->bo); + if (!ret) { + ret = nouveau_bo_pin(priv->bo, TTM_PL_FLAG_VRAM, false); + if (!ret) { + ret = nouveau_bo_map(priv->bo); + if (ret) + nouveau_bo_unpin(priv->bo); + } + if (ret) + nouveau_bo_ref(NULL, &priv->bo); + } + + if (ret) { + nv10_fence_destroy(drm); + return ret; + } + + nouveau_bo_wr32(priv->bo, 0x000, 0x00000000); + return ret; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nv50_display.c b/kernel/drivers/gpu/drm/nouveau/nv50_display.c new file mode 100644 index 000000000..7da795855 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nv50_display.c @@ -0,0 +1,2568 @@ +/* + * 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 + +#include +#include +#include +#include + +#include + +#include "nouveau_drm.h" +#include "nouveau_dma.h" +#include "nouveau_gem.h" +#include "nouveau_connector.h" +#include "nouveau_encoder.h" +#include "nouveau_crtc.h" +#include "nouveau_fence.h" +#include "nv50_display.h" + +#define EVO_DMA_NR 9 + +#define EVO_MASTER (0x00) +#define EVO_FLIP(c) (0x01 + (c)) +#define EVO_OVLY(c) (0x05 + (c)) +#define EVO_OIMM(c) (0x09 + (c)) +#define EVO_CURS(c) (0x0d + (c)) + +/* offsets in shared sync bo of various structures */ +#define EVO_SYNC(c, o) ((c) * 0x0100 + (o)) +#define EVO_MAST_NTFY EVO_SYNC( 0, 0x00) +#define EVO_FLIP_SEM0(c) EVO_SYNC((c) + 1, 0x00) +#define EVO_FLIP_SEM1(c) EVO_SYNC((c) + 1, 0x10) + +/****************************************************************************** + * EVO channel + *****************************************************************************/ + +struct nv50_chan { + struct nvif_object user; +}; + +static int +nv50_chan_create(struct nvif_object *disp, const u32 *oclass, u8 head, + void *data, u32 size, struct nv50_chan *chan) +{ + const u32 handle = (oclass[0] << 16) | head; + u32 sclass[8]; + int ret, i; + + ret = nvif_object_sclass(disp, sclass, ARRAY_SIZE(sclass)); + WARN_ON(ret > ARRAY_SIZE(sclass)); + if (ret < 0) + return ret; + + while (oclass[0]) { + for (i = 0; i < ARRAY_SIZE(sclass); i++) { + if (sclass[i] == oclass[0]) { + ret = nvif_object_init(disp, NULL, handle, + oclass[0], data, size, + &chan->user); + if (ret == 0) + nvif_object_map(&chan->user); + return ret; + } + } + oclass++; + } + + return -ENOSYS; +} + +static void +nv50_chan_destroy(struct nv50_chan *chan) +{ + nvif_object_fini(&chan->user); +} + +/****************************************************************************** + * PIO EVO channel + *****************************************************************************/ + +struct nv50_pioc { + struct nv50_chan base; +}; + +static void +nv50_pioc_destroy(struct nv50_pioc *pioc) +{ + nv50_chan_destroy(&pioc->base); +} + +static int +nv50_pioc_create(struct nvif_object *disp, const u32 *oclass, u8 head, + void *data, u32 size, struct nv50_pioc *pioc) +{ + return nv50_chan_create(disp, oclass, head, data, size, &pioc->base); +} + +/****************************************************************************** + * Cursor Immediate + *****************************************************************************/ + +struct nv50_curs { + struct nv50_pioc base; +}; + +static int +nv50_curs_create(struct nvif_object *disp, int head, struct nv50_curs *curs) +{ + struct nv50_disp_cursor_v0 args = { + .head = head, + }; + static const u32 oclass[] = { + GK104_DISP_CURSOR, + GF110_DISP_CURSOR, + GT214_DISP_CURSOR, + G82_DISP_CURSOR, + NV50_DISP_CURSOR, + 0 + }; + + return nv50_pioc_create(disp, oclass, head, &args, sizeof(args), + &curs->base); +} + +/****************************************************************************** + * Overlay Immediate + *****************************************************************************/ + +struct nv50_oimm { + struct nv50_pioc base; +}; + +static int +nv50_oimm_create(struct nvif_object *disp, int head, struct nv50_oimm *oimm) +{ + struct nv50_disp_cursor_v0 args = { + .head = head, + }; + static const u32 oclass[] = { + GK104_DISP_OVERLAY, + GF110_DISP_OVERLAY, + GT214_DISP_OVERLAY, + G82_DISP_OVERLAY, + NV50_DISP_OVERLAY, + 0 + }; + + return nv50_pioc_create(disp, oclass, head, &args, sizeof(args), + &oimm->base); +} + +/****************************************************************************** + * DMA EVO channel + *****************************************************************************/ + +struct nv50_dmac { + struct nv50_chan base; + dma_addr_t handle; + u32 *ptr; + + struct nvif_object sync; + struct nvif_object vram; + + /* Protects against concurrent pushbuf access to this channel, lock is + * grabbed by evo_wait (if the pushbuf reservation is successful) and + * dropped again by evo_kick. */ + struct mutex lock; +}; + +static void +nv50_dmac_destroy(struct nv50_dmac *dmac, struct nvif_object *disp) +{ + nvif_object_fini(&dmac->vram); + nvif_object_fini(&dmac->sync); + + nv50_chan_destroy(&dmac->base); + + if (dmac->ptr) { + struct pci_dev *pdev = nvxx_device(nvif_device(disp))->pdev; + pci_free_consistent(pdev, PAGE_SIZE, dmac->ptr, dmac->handle); + } +} + +static int +nv50_dmac_create(struct nvif_object *disp, const u32 *oclass, u8 head, + void *data, u32 size, u64 syncbuf, + struct nv50_dmac *dmac) +{ + struct nvif_device *device = nvif_device(disp); + struct nv50_disp_core_channel_dma_v0 *args = data; + struct nvif_object pushbuf; + int ret; + + mutex_init(&dmac->lock); + + dmac->ptr = pci_alloc_consistent(nvxx_device(device)->pdev, + PAGE_SIZE, &dmac->handle); + if (!dmac->ptr) + return -ENOMEM; + + ret = nvif_object_init(nvif_object(device), NULL, + args->pushbuf, NV_DMA_FROM_MEMORY, + &(struct nv_dma_v0) { + .target = NV_DMA_V0_TARGET_PCI_US, + .access = NV_DMA_V0_ACCESS_RD, + .start = dmac->handle + 0x0000, + .limit = dmac->handle + 0x0fff, + }, sizeof(struct nv_dma_v0), &pushbuf); + if (ret) + return ret; + + ret = nv50_chan_create(disp, oclass, head, data, size, &dmac->base); + nvif_object_fini(&pushbuf); + if (ret) + return ret; + + ret = nvif_object_init(&dmac->base.user, NULL, 0xf0000000, + NV_DMA_IN_MEMORY, + &(struct nv_dma_v0) { + .target = NV_DMA_V0_TARGET_VRAM, + .access = NV_DMA_V0_ACCESS_RDWR, + .start = syncbuf + 0x0000, + .limit = syncbuf + 0x0fff, + }, sizeof(struct nv_dma_v0), + &dmac->sync); + if (ret) + return ret; + + ret = nvif_object_init(&dmac->base.user, NULL, 0xf0000001, + NV_DMA_IN_MEMORY, + &(struct nv_dma_v0) { + .target = NV_DMA_V0_TARGET_VRAM, + .access = NV_DMA_V0_ACCESS_RDWR, + .start = 0, + .limit = device->info.ram_user - 1, + }, sizeof(struct nv_dma_v0), + &dmac->vram); + if (ret) + return ret; + + return ret; +} + +/****************************************************************************** + * Core + *****************************************************************************/ + +struct nv50_mast { + struct nv50_dmac base; +}; + +static int +nv50_core_create(struct nvif_object *disp, u64 syncbuf, struct nv50_mast *core) +{ + struct nv50_disp_core_channel_dma_v0 args = { + .pushbuf = 0xb0007d00, + }; + static const u32 oclass[] = { + GM204_DISP_CORE_CHANNEL_DMA, + GM107_DISP_CORE_CHANNEL_DMA, + GK110_DISP_CORE_CHANNEL_DMA, + GK104_DISP_CORE_CHANNEL_DMA, + GF110_DISP_CORE_CHANNEL_DMA, + GT214_DISP_CORE_CHANNEL_DMA, + GT206_DISP_CORE_CHANNEL_DMA, + GT200_DISP_CORE_CHANNEL_DMA, + G82_DISP_CORE_CHANNEL_DMA, + NV50_DISP_CORE_CHANNEL_DMA, + 0 + }; + + return nv50_dmac_create(disp, oclass, 0, &args, sizeof(args), syncbuf, + &core->base); +} + +/****************************************************************************** + * Base + *****************************************************************************/ + +struct nv50_sync { + struct nv50_dmac base; + u32 addr; + u32 data; +}; + +static int +nv50_base_create(struct nvif_object *disp, int head, u64 syncbuf, + struct nv50_sync *base) +{ + struct nv50_disp_base_channel_dma_v0 args = { + .pushbuf = 0xb0007c00 | head, + .head = head, + }; + static const u32 oclass[] = { + GK110_DISP_BASE_CHANNEL_DMA, + GK104_DISP_BASE_CHANNEL_DMA, + GF110_DISP_BASE_CHANNEL_DMA, + GT214_DISP_BASE_CHANNEL_DMA, + GT200_DISP_BASE_CHANNEL_DMA, + G82_DISP_BASE_CHANNEL_DMA, + NV50_DISP_BASE_CHANNEL_DMA, + 0 + }; + + return nv50_dmac_create(disp, oclass, head, &args, sizeof(args), + syncbuf, &base->base); +} + +/****************************************************************************** + * Overlay + *****************************************************************************/ + +struct nv50_ovly { + struct nv50_dmac base; +}; + +static int +nv50_ovly_create(struct nvif_object *disp, int head, u64 syncbuf, + struct nv50_ovly *ovly) +{ + struct nv50_disp_overlay_channel_dma_v0 args = { + .pushbuf = 0xb0007e00 | head, + .head = head, + }; + static const u32 oclass[] = { + GK104_DISP_OVERLAY_CONTROL_DMA, + GF110_DISP_OVERLAY_CONTROL_DMA, + GT214_DISP_OVERLAY_CHANNEL_DMA, + GT200_DISP_OVERLAY_CHANNEL_DMA, + G82_DISP_OVERLAY_CHANNEL_DMA, + NV50_DISP_OVERLAY_CHANNEL_DMA, + 0 + }; + + return nv50_dmac_create(disp, oclass, head, &args, sizeof(args), + syncbuf, &ovly->base); +} + +struct nv50_head { + struct nouveau_crtc base; + struct nouveau_bo *image; + struct nv50_curs curs; + struct nv50_sync sync; + struct nv50_ovly ovly; + struct nv50_oimm oimm; +}; + +#define nv50_head(c) ((struct nv50_head *)nouveau_crtc(c)) +#define nv50_curs(c) (&nv50_head(c)->curs) +#define nv50_sync(c) (&nv50_head(c)->sync) +#define nv50_ovly(c) (&nv50_head(c)->ovly) +#define nv50_oimm(c) (&nv50_head(c)->oimm) +#define nv50_chan(c) (&(c)->base.base) +#define nv50_vers(c) nv50_chan(c)->user.oclass + +struct nv50_fbdma { + struct list_head head; + struct nvif_object core; + struct nvif_object base[4]; +}; + +struct nv50_disp { + struct nvif_object *disp; + struct nv50_mast mast; + + struct list_head fbdma; + + struct nouveau_bo *sync; +}; + +static struct nv50_disp * +nv50_disp(struct drm_device *dev) +{ + return nouveau_display(dev)->priv; +} + +#define nv50_mast(d) (&nv50_disp(d)->mast) + +static struct drm_crtc * +nv50_display_crtc_get(struct drm_encoder *encoder) +{ + return nouveau_encoder(encoder)->crtc; +} + +/****************************************************************************** + * EVO channel helpers + *****************************************************************************/ +static u32 * +evo_wait(void *evoc, int nr) +{ + struct nv50_dmac *dmac = evoc; + u32 put = nvif_rd32(&dmac->base.user, 0x0000) / 4; + + mutex_lock(&dmac->lock); + if (put + nr >= (PAGE_SIZE / 4) - 8) { + dmac->ptr[put] = 0x20000000; + + nvif_wr32(&dmac->base.user, 0x0000, 0x00000000); + if (!nvxx_wait(&dmac->base.user, 0x0004, ~0, 0x00000000)) { + mutex_unlock(&dmac->lock); + nv_error(nvxx_object(&dmac->base.user), "channel stalled\n"); + return NULL; + } + + put = 0; + } + + return dmac->ptr + put; +} + +static void +evo_kick(u32 *push, void *evoc) +{ + struct nv50_dmac *dmac = evoc; + nvif_wr32(&dmac->base.user, 0x0000, (push - dmac->ptr) << 2); + mutex_unlock(&dmac->lock); +} + +#if 1 +#define evo_mthd(p,m,s) *((p)++) = (((s) << 18) | (m)) +#define evo_data(p,d) *((p)++) = (d) +#else +#define evo_mthd(p,m,s) do { \ + const u32 _m = (m), _s = (s); \ + printk(KERN_ERR "%04x %d %s\n", _m, _s, __func__); \ + *((p)++) = ((_s << 18) | _m); \ +} while(0) +#define evo_data(p,d) do { \ + const u32 _d = (d); \ + printk(KERN_ERR "\t%08x\n", _d); \ + *((p)++) = _d; \ +} while(0) +#endif + +static bool +evo_sync_wait(void *data) +{ + if (nouveau_bo_rd32(data, EVO_MAST_NTFY) != 0x00000000) + return true; + usleep_range(1, 2); + return false; +} + +static int +evo_sync(struct drm_device *dev) +{ + struct nvif_device *device = &nouveau_drm(dev)->device; + struct nv50_disp *disp = nv50_disp(dev); + struct nv50_mast *mast = nv50_mast(dev); + u32 *push = evo_wait(mast, 8); + if (push) { + nouveau_bo_wr32(disp->sync, EVO_MAST_NTFY, 0x00000000); + evo_mthd(push, 0x0084, 1); + evo_data(push, 0x80000000 | EVO_MAST_NTFY); + evo_mthd(push, 0x0080, 2); + evo_data(push, 0x00000000); + evo_data(push, 0x00000000); + evo_kick(push, mast); + if (nv_wait_cb(nvxx_device(device), evo_sync_wait, disp->sync)) + return 0; + } + + return -EBUSY; +} + +/****************************************************************************** + * Page flipping channel + *****************************************************************************/ +struct nouveau_bo * +nv50_display_crtc_sema(struct drm_device *dev, int crtc) +{ + return nv50_disp(dev)->sync; +} + +struct nv50_display_flip { + struct nv50_disp *disp; + struct nv50_sync *chan; +}; + +static bool +nv50_display_flip_wait(void *data) +{ + struct nv50_display_flip *flip = data; + if (nouveau_bo_rd32(flip->disp->sync, flip->chan->addr / 4) == + flip->chan->data) + return true; + usleep_range(1, 2); + return false; +} + +void +nv50_display_flip_stop(struct drm_crtc *crtc) +{ + struct nvif_device *device = &nouveau_drm(crtc->dev)->device; + struct nv50_display_flip flip = { + .disp = nv50_disp(crtc->dev), + .chan = nv50_sync(crtc), + }; + u32 *push; + + push = evo_wait(flip.chan, 8); + if (push) { + evo_mthd(push, 0x0084, 1); + evo_data(push, 0x00000000); + evo_mthd(push, 0x0094, 1); + evo_data(push, 0x00000000); + evo_mthd(push, 0x00c0, 1); + evo_data(push, 0x00000000); + evo_mthd(push, 0x0080, 1); + evo_data(push, 0x00000000); + evo_kick(push, flip.chan); + } + + nv_wait_cb(nvxx_device(device), nv50_display_flip_wait, &flip); +} + +int +nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb, + struct nouveau_channel *chan, u32 swap_interval) +{ + struct nouveau_framebuffer *nv_fb = nouveau_framebuffer(fb); + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct nv50_head *head = nv50_head(crtc); + struct nv50_sync *sync = nv50_sync(crtc); + u32 *push; + int ret; + + if (crtc->primary->fb->width != fb->width || + crtc->primary->fb->height != fb->height) + return -EINVAL; + + swap_interval <<= 4; + if (swap_interval == 0) + swap_interval |= 0x100; + if (chan == NULL) + evo_sync(crtc->dev); + + push = evo_wait(sync, 128); + if (unlikely(push == NULL)) + return -EBUSY; + + if (chan && chan->object->oclass < G82_CHANNEL_GPFIFO) { + ret = RING_SPACE(chan, 8); + if (ret) + return ret; + + BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 2); + OUT_RING (chan, NvEvoSema0 + nv_crtc->index); + OUT_RING (chan, sync->addr ^ 0x10); + BEGIN_NV04(chan, 0, NV11_SUBCHAN_SEMAPHORE_RELEASE, 1); + OUT_RING (chan, sync->data + 1); + BEGIN_NV04(chan, 0, NV11_SUBCHAN_SEMAPHORE_OFFSET, 2); + OUT_RING (chan, sync->addr); + OUT_RING (chan, sync->data); + } else + if (chan && chan->object->oclass < FERMI_CHANNEL_GPFIFO) { + u64 addr = nv84_fence_crtc(chan, nv_crtc->index) + sync->addr; + ret = RING_SPACE(chan, 12); + if (ret) + return ret; + + BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 1); + OUT_RING (chan, chan->vram.handle); + BEGIN_NV04(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); + OUT_RING (chan, upper_32_bits(addr ^ 0x10)); + OUT_RING (chan, lower_32_bits(addr ^ 0x10)); + OUT_RING (chan, sync->data + 1); + OUT_RING (chan, NV84_SUBCHAN_SEMAPHORE_TRIGGER_WRITE_LONG); + BEGIN_NV04(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); + OUT_RING (chan, upper_32_bits(addr)); + OUT_RING (chan, lower_32_bits(addr)); + OUT_RING (chan, sync->data); + OUT_RING (chan, NV84_SUBCHAN_SEMAPHORE_TRIGGER_ACQUIRE_EQUAL); + } else + if (chan) { + u64 addr = nv84_fence_crtc(chan, nv_crtc->index) + sync->addr; + ret = RING_SPACE(chan, 10); + if (ret) + return ret; + + BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); + OUT_RING (chan, upper_32_bits(addr ^ 0x10)); + OUT_RING (chan, lower_32_bits(addr ^ 0x10)); + OUT_RING (chan, sync->data + 1); + OUT_RING (chan, NV84_SUBCHAN_SEMAPHORE_TRIGGER_WRITE_LONG | + NVC0_SUBCHAN_SEMAPHORE_TRIGGER_YIELD); + BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); + OUT_RING (chan, upper_32_bits(addr)); + OUT_RING (chan, lower_32_bits(addr)); + OUT_RING (chan, sync->data); + OUT_RING (chan, NV84_SUBCHAN_SEMAPHORE_TRIGGER_ACQUIRE_EQUAL | + NVC0_SUBCHAN_SEMAPHORE_TRIGGER_YIELD); + } + + if (chan) { + sync->addr ^= 0x10; + sync->data++; + FIRE_RING (chan); + } + + /* queue the flip */ + evo_mthd(push, 0x0100, 1); + evo_data(push, 0xfffe0000); + evo_mthd(push, 0x0084, 1); + evo_data(push, swap_interval); + if (!(swap_interval & 0x00000100)) { + evo_mthd(push, 0x00e0, 1); + evo_data(push, 0x40000000); + } + evo_mthd(push, 0x0088, 4); + evo_data(push, sync->addr); + evo_data(push, sync->data++); + evo_data(push, sync->data); + evo_data(push, sync->base.sync.handle); + evo_mthd(push, 0x00a0, 2); + evo_data(push, 0x00000000); + evo_data(push, 0x00000000); + evo_mthd(push, 0x00c0, 1); + evo_data(push, nv_fb->r_handle); + evo_mthd(push, 0x0110, 2); + evo_data(push, 0x00000000); + evo_data(push, 0x00000000); + if (nv50_vers(sync) < GF110_DISP_BASE_CHANNEL_DMA) { + evo_mthd(push, 0x0800, 5); + evo_data(push, nv_fb->nvbo->bo.offset >> 8); + evo_data(push, 0); + evo_data(push, (fb->height << 16) | fb->width); + evo_data(push, nv_fb->r_pitch); + evo_data(push, nv_fb->r_format); + } else { + evo_mthd(push, 0x0400, 5); + evo_data(push, nv_fb->nvbo->bo.offset >> 8); + evo_data(push, 0); + evo_data(push, (fb->height << 16) | fb->width); + evo_data(push, nv_fb->r_pitch); + evo_data(push, nv_fb->r_format); + } + evo_mthd(push, 0x0080, 1); + evo_data(push, 0x00000000); + evo_kick(push, sync); + + nouveau_bo_ref(nv_fb->nvbo, &head->image); + return 0; +} + +/****************************************************************************** + * CRTC + *****************************************************************************/ +static int +nv50_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool update) +{ + struct nv50_mast *mast = nv50_mast(nv_crtc->base.dev); + struct nouveau_connector *nv_connector; + struct drm_connector *connector; + u32 *push, mode = 0x00; + + nv_connector = nouveau_crtc_connector_get(nv_crtc); + connector = &nv_connector->base; + if (nv_connector->dithering_mode == DITHERING_MODE_AUTO) { + if (nv_crtc->base.primary->fb->depth > connector->display_info.bpc * 3) + mode = DITHERING_MODE_DYNAMIC2X2; + } else { + mode = nv_connector->dithering_mode; + } + + if (nv_connector->dithering_depth == DITHERING_DEPTH_AUTO) { + if (connector->display_info.bpc >= 8) + mode |= DITHERING_DEPTH_8BPC; + } else { + mode |= nv_connector->dithering_depth; + } + + push = evo_wait(mast, 4); + if (push) { + if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) { + evo_mthd(push, 0x08a0 + (nv_crtc->index * 0x0400), 1); + evo_data(push, mode); + } else + if (nv50_vers(mast) < GK104_DISP_CORE_CHANNEL_DMA) { + evo_mthd(push, 0x0490 + (nv_crtc->index * 0x0300), 1); + evo_data(push, mode); + } else { + evo_mthd(push, 0x04a0 + (nv_crtc->index * 0x0300), 1); + evo_data(push, mode); + } + + if (update) { + evo_mthd(push, 0x0080, 1); + evo_data(push, 0x00000000); + } + evo_kick(push, mast); + } + + return 0; +} + +static int +nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, bool update) +{ + struct nv50_mast *mast = nv50_mast(nv_crtc->base.dev); + struct drm_display_mode *omode, *umode = &nv_crtc->base.mode; + struct drm_crtc *crtc = &nv_crtc->base; + struct nouveau_connector *nv_connector; + int mode = DRM_MODE_SCALE_NONE; + u32 oX, oY, *push; + + /* start off at the resolution we programmed the crtc for, this + * effectively handles NONE/FULL scaling + */ + nv_connector = nouveau_crtc_connector_get(nv_crtc); + if (nv_connector && nv_connector->native_mode) { + mode = nv_connector->scaling_mode; + if (nv_connector->scaling_full) /* non-EDID LVDS/eDP mode */ + mode = DRM_MODE_SCALE_FULLSCREEN; + } + + if (mode != DRM_MODE_SCALE_NONE) + omode = nv_connector->native_mode; + else + omode = umode; + + oX = omode->hdisplay; + oY = omode->vdisplay; + if (omode->flags & DRM_MODE_FLAG_DBLSCAN) + oY *= 2; + + /* add overscan compensation if necessary, will keep the aspect + * ratio the same as the backend mode unless overridden by the + * user setting both hborder and vborder properties. + */ + if (nv_connector && ( nv_connector->underscan == UNDERSCAN_ON || + (nv_connector->underscan == UNDERSCAN_AUTO && + nv_connector->edid && + drm_detect_hdmi_monitor(nv_connector->edid)))) { + u32 bX = nv_connector->underscan_hborder; + u32 bY = nv_connector->underscan_vborder; + u32 aspect = (oY << 19) / oX; + + if (bX) { + oX -= (bX * 2); + if (bY) oY -= (bY * 2); + else oY = ((oX * aspect) + (aspect / 2)) >> 19; + } else { + oX -= (oX >> 4) + 32; + if (bY) oY -= (bY * 2); + else oY = ((oX * aspect) + (aspect / 2)) >> 19; + } + } + + /* handle CENTER/ASPECT scaling, taking into account the areas + * removed already for overscan compensation + */ + switch (mode) { + case DRM_MODE_SCALE_CENTER: + oX = min((u32)umode->hdisplay, oX); + oY = min((u32)umode->vdisplay, oY); + /* fall-through */ + case DRM_MODE_SCALE_ASPECT: + if (oY < oX) { + u32 aspect = (umode->hdisplay << 19) / umode->vdisplay; + oX = ((oY * aspect) + (aspect / 2)) >> 19; + } else { + u32 aspect = (umode->vdisplay << 19) / umode->hdisplay; + oY = ((oX * aspect) + (aspect / 2)) >> 19; + } + break; + default: + break; + } + + push = evo_wait(mast, 8); + if (push) { + if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) { + /*XXX: SCALE_CTRL_ACTIVE??? */ + evo_mthd(push, 0x08d8 + (nv_crtc->index * 0x400), 2); + evo_data(push, (oY << 16) | oX); + evo_data(push, (oY << 16) | oX); + evo_mthd(push, 0x08a4 + (nv_crtc->index * 0x400), 1); + evo_data(push, 0x00000000); + evo_mthd(push, 0x08c8 + (nv_crtc->index * 0x400), 1); + evo_data(push, umode->vdisplay << 16 | umode->hdisplay); + } else { + evo_mthd(push, 0x04c0 + (nv_crtc->index * 0x300), 3); + evo_data(push, (oY << 16) | oX); + evo_data(push, (oY << 16) | oX); + evo_data(push, (oY << 16) | oX); + evo_mthd(push, 0x0494 + (nv_crtc->index * 0x300), 1); + evo_data(push, 0x00000000); + evo_mthd(push, 0x04b8 + (nv_crtc->index * 0x300), 1); + evo_data(push, umode->vdisplay << 16 | umode->hdisplay); + } + + evo_kick(push, mast); + + if (update) { + nv50_display_flip_stop(crtc); + nv50_display_flip_next(crtc, crtc->primary->fb, + NULL, 1); + } + } + + return 0; +} + +static int +nv50_crtc_set_raster_vblank_dmi(struct nouveau_crtc *nv_crtc, u32 usec) +{ + struct nv50_mast *mast = nv50_mast(nv_crtc->base.dev); + u32 *push; + + push = evo_wait(mast, 8); + if (!push) + return -ENOMEM; + + evo_mthd(push, 0x0828 + (nv_crtc->index * 0x400), 1); + evo_data(push, usec); + evo_kick(push, mast); + return 0; +} + +static int +nv50_crtc_set_color_vibrance(struct nouveau_crtc *nv_crtc, bool update) +{ + struct nv50_mast *mast = nv50_mast(nv_crtc->base.dev); + u32 *push, hue, vib; + int adj; + + adj = (nv_crtc->color_vibrance > 0) ? 50 : 0; + vib = ((nv_crtc->color_vibrance * 2047 + adj) / 100) & 0xfff; + hue = ((nv_crtc->vibrant_hue * 2047) / 100) & 0xfff; + + push = evo_wait(mast, 16); + if (push) { + if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) { + evo_mthd(push, 0x08a8 + (nv_crtc->index * 0x400), 1); + evo_data(push, (hue << 20) | (vib << 8)); + } else { + evo_mthd(push, 0x0498 + (nv_crtc->index * 0x300), 1); + evo_data(push, (hue << 20) | (vib << 8)); + } + + if (update) { + evo_mthd(push, 0x0080, 1); + evo_data(push, 0x00000000); + } + evo_kick(push, mast); + } + + return 0; +} + +static int +nv50_crtc_set_image(struct nouveau_crtc *nv_crtc, struct drm_framebuffer *fb, + int x, int y, bool update) +{ + struct nouveau_framebuffer *nvfb = nouveau_framebuffer(fb); + struct nv50_mast *mast = nv50_mast(nv_crtc->base.dev); + u32 *push; + + push = evo_wait(mast, 16); + if (push) { + if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) { + evo_mthd(push, 0x0860 + (nv_crtc->index * 0x400), 1); + evo_data(push, nvfb->nvbo->bo.offset >> 8); + evo_mthd(push, 0x0868 + (nv_crtc->index * 0x400), 3); + evo_data(push, (fb->height << 16) | fb->width); + evo_data(push, nvfb->r_pitch); + evo_data(push, nvfb->r_format); + evo_mthd(push, 0x08c0 + (nv_crtc->index * 0x400), 1); + evo_data(push, (y << 16) | x); + if (nv50_vers(mast) > NV50_DISP_CORE_CHANNEL_DMA) { + evo_mthd(push, 0x0874 + (nv_crtc->index * 0x400), 1); + evo_data(push, nvfb->r_handle); + } + } else { + evo_mthd(push, 0x0460 + (nv_crtc->index * 0x300), 1); + evo_data(push, nvfb->nvbo->bo.offset >> 8); + evo_mthd(push, 0x0468 + (nv_crtc->index * 0x300), 4); + evo_data(push, (fb->height << 16) | fb->width); + evo_data(push, nvfb->r_pitch); + evo_data(push, nvfb->r_format); + evo_data(push, nvfb->r_handle); + evo_mthd(push, 0x04b0 + (nv_crtc->index * 0x300), 1); + evo_data(push, (y << 16) | x); + } + + if (update) { + evo_mthd(push, 0x0080, 1); + evo_data(push, 0x00000000); + } + evo_kick(push, mast); + } + + nv_crtc->fb.handle = nvfb->r_handle; + return 0; +} + +static void +nv50_crtc_cursor_show(struct nouveau_crtc *nv_crtc) +{ + struct nv50_mast *mast = nv50_mast(nv_crtc->base.dev); + u32 *push = evo_wait(mast, 16); + if (push) { + if (nv50_vers(mast) < G82_DISP_CORE_CHANNEL_DMA) { + evo_mthd(push, 0x0880 + (nv_crtc->index * 0x400), 2); + evo_data(push, 0x85000000); + evo_data(push, nv_crtc->cursor.nvbo->bo.offset >> 8); + } else + if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) { + evo_mthd(push, 0x0880 + (nv_crtc->index * 0x400), 2); + evo_data(push, 0x85000000); + evo_data(push, nv_crtc->cursor.nvbo->bo.offset >> 8); + evo_mthd(push, 0x089c + (nv_crtc->index * 0x400), 1); + evo_data(push, mast->base.vram.handle); + } else { + evo_mthd(push, 0x0480 + (nv_crtc->index * 0x300), 2); + evo_data(push, 0x85000000); + evo_data(push, nv_crtc->cursor.nvbo->bo.offset >> 8); + evo_mthd(push, 0x048c + (nv_crtc->index * 0x300), 1); + evo_data(push, mast->base.vram.handle); + } + evo_kick(push, mast); + } + nv_crtc->cursor.visible = true; +} + +static void +nv50_crtc_cursor_hide(struct nouveau_crtc *nv_crtc) +{ + struct nv50_mast *mast = nv50_mast(nv_crtc->base.dev); + u32 *push = evo_wait(mast, 16); + if (push) { + if (nv50_vers(mast) < G82_DISP_CORE_CHANNEL_DMA) { + evo_mthd(push, 0x0880 + (nv_crtc->index * 0x400), 1); + evo_data(push, 0x05000000); + } else + if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) { + evo_mthd(push, 0x0880 + (nv_crtc->index * 0x400), 1); + evo_data(push, 0x05000000); + evo_mthd(push, 0x089c + (nv_crtc->index * 0x400), 1); + evo_data(push, 0x00000000); + } else { + evo_mthd(push, 0x0480 + (nv_crtc->index * 0x300), 1); + evo_data(push, 0x05000000); + evo_mthd(push, 0x048c + (nv_crtc->index * 0x300), 1); + evo_data(push, 0x00000000); + } + evo_kick(push, mast); + } + nv_crtc->cursor.visible = false; +} + +static void +nv50_crtc_cursor_show_hide(struct nouveau_crtc *nv_crtc, bool show, bool update) +{ + struct nv50_mast *mast = nv50_mast(nv_crtc->base.dev); + + if (show && nv_crtc->cursor.nvbo) + nv50_crtc_cursor_show(nv_crtc); + else + nv50_crtc_cursor_hide(nv_crtc); + + if (update) { + u32 *push = evo_wait(mast, 2); + if (push) { + evo_mthd(push, 0x0080, 1); + evo_data(push, 0x00000000); + evo_kick(push, mast); + } + } +} + +static void +nv50_crtc_dpms(struct drm_crtc *crtc, int mode) +{ +} + +static void +nv50_crtc_prepare(struct drm_crtc *crtc) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct nv50_mast *mast = nv50_mast(crtc->dev); + u32 *push; + + nv50_display_flip_stop(crtc); + + push = evo_wait(mast, 6); + if (push) { + if (nv50_vers(mast) < G82_DISP_CORE_CHANNEL_DMA) { + evo_mthd(push, 0x0874 + (nv_crtc->index * 0x400), 1); + evo_data(push, 0x00000000); + evo_mthd(push, 0x0840 + (nv_crtc->index * 0x400), 1); + evo_data(push, 0x40000000); + } else + if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) { + evo_mthd(push, 0x0874 + (nv_crtc->index * 0x400), 1); + evo_data(push, 0x00000000); + evo_mthd(push, 0x0840 + (nv_crtc->index * 0x400), 1); + evo_data(push, 0x40000000); + evo_mthd(push, 0x085c + (nv_crtc->index * 0x400), 1); + evo_data(push, 0x00000000); + } else { + evo_mthd(push, 0x0474 + (nv_crtc->index * 0x300), 1); + evo_data(push, 0x00000000); + evo_mthd(push, 0x0440 + (nv_crtc->index * 0x300), 1); + evo_data(push, 0x03000000); + evo_mthd(push, 0x045c + (nv_crtc->index * 0x300), 1); + evo_data(push, 0x00000000); + } + + evo_kick(push, mast); + } + + nv50_crtc_cursor_show_hide(nv_crtc, false, false); +} + +static void +nv50_crtc_commit(struct drm_crtc *crtc) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct nv50_mast *mast = nv50_mast(crtc->dev); + u32 *push; + + push = evo_wait(mast, 32); + if (push) { + if (nv50_vers(mast) < G82_DISP_CORE_CHANNEL_DMA) { + evo_mthd(push, 0x0874 + (nv_crtc->index * 0x400), 1); + evo_data(push, nv_crtc->fb.handle); + evo_mthd(push, 0x0840 + (nv_crtc->index * 0x400), 2); + evo_data(push, 0xc0000000); + evo_data(push, nv_crtc->lut.nvbo->bo.offset >> 8); + } else + if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) { + evo_mthd(push, 0x0874 + (nv_crtc->index * 0x400), 1); + evo_data(push, nv_crtc->fb.handle); + evo_mthd(push, 0x0840 + (nv_crtc->index * 0x400), 2); + evo_data(push, 0xc0000000); + evo_data(push, nv_crtc->lut.nvbo->bo.offset >> 8); + evo_mthd(push, 0x085c + (nv_crtc->index * 0x400), 1); + evo_data(push, mast->base.vram.handle); + } else { + evo_mthd(push, 0x0474 + (nv_crtc->index * 0x300), 1); + evo_data(push, nv_crtc->fb.handle); + evo_mthd(push, 0x0440 + (nv_crtc->index * 0x300), 4); + evo_data(push, 0x83000000); + evo_data(push, nv_crtc->lut.nvbo->bo.offset >> 8); + evo_data(push, 0x00000000); + evo_data(push, 0x00000000); + evo_mthd(push, 0x045c + (nv_crtc->index * 0x300), 1); + evo_data(push, mast->base.vram.handle); + evo_mthd(push, 0x0430 + (nv_crtc->index * 0x300), 1); + evo_data(push, 0xffffff00); + } + + evo_kick(push, mast); + } + + nv50_crtc_cursor_show_hide(nv_crtc, true, true); + nv50_display_flip_next(crtc, crtc->primary->fb, NULL, 1); +} + +static bool +nv50_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V); + return true; +} + +static int +nv50_crtc_swap_fbs(struct drm_crtc *crtc, struct drm_framebuffer *old_fb) +{ + struct nouveau_framebuffer *nvfb = nouveau_framebuffer(crtc->primary->fb); + struct nv50_head *head = nv50_head(crtc); + int ret; + + ret = nouveau_bo_pin(nvfb->nvbo, TTM_PL_FLAG_VRAM, true); + if (ret == 0) { + if (head->image) + nouveau_bo_unpin(head->image); + nouveau_bo_ref(nvfb->nvbo, &head->image); + } + + return ret; +} + +static int +nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode, + struct drm_display_mode *mode, int x, int y, + struct drm_framebuffer *old_fb) +{ + struct nv50_mast *mast = nv50_mast(crtc->dev); + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct nouveau_connector *nv_connector; + u32 ilace = (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 2 : 1; + u32 vscan = (mode->flags & DRM_MODE_FLAG_DBLSCAN) ? 2 : 1; + u32 hactive, hsynce, hbackp, hfrontp, hblanke, hblanks; + u32 vactive, vsynce, vbackp, vfrontp, vblanke, vblanks; + u32 vblan2e = 0, vblan2s = 1, vblankus = 0; + u32 *push; + int ret; + + hactive = mode->htotal; + hsynce = mode->hsync_end - mode->hsync_start - 1; + hbackp = mode->htotal - mode->hsync_end; + hblanke = hsynce + hbackp; + hfrontp = mode->hsync_start - mode->hdisplay; + hblanks = mode->htotal - hfrontp - 1; + + vactive = mode->vtotal * vscan / ilace; + vsynce = ((mode->vsync_end - mode->vsync_start) * vscan / ilace) - 1; + vbackp = (mode->vtotal - mode->vsync_end) * vscan / ilace; + vblanke = vsynce + vbackp; + vfrontp = (mode->vsync_start - mode->vdisplay) * vscan / ilace; + vblanks = vactive - vfrontp - 1; + /* XXX: Safe underestimate, even "0" works */ + vblankus = (vactive - mode->vdisplay - 2) * hactive; + vblankus *= 1000; + vblankus /= mode->clock; + + if (mode->flags & DRM_MODE_FLAG_INTERLACE) { + vblan2e = vactive + vsynce + vbackp; + vblan2s = vblan2e + (mode->vdisplay * vscan / ilace); + vactive = (vactive * 2) + 1; + } + + ret = nv50_crtc_swap_fbs(crtc, old_fb); + if (ret) + return ret; + + push = evo_wait(mast, 64); + if (push) { + if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) { + evo_mthd(push, 0x0804 + (nv_crtc->index * 0x400), 2); + evo_data(push, 0x00800000 | mode->clock); + evo_data(push, (ilace == 2) ? 2 : 0); + evo_mthd(push, 0x0810 + (nv_crtc->index * 0x400), 6); + evo_data(push, 0x00000000); + evo_data(push, (vactive << 16) | hactive); + evo_data(push, ( vsynce << 16) | hsynce); + evo_data(push, (vblanke << 16) | hblanke); + evo_data(push, (vblanks << 16) | hblanks); + evo_data(push, (vblan2e << 16) | vblan2s); + evo_mthd(push, 0x082c + (nv_crtc->index * 0x400), 1); + evo_data(push, 0x00000000); + evo_mthd(push, 0x0900 + (nv_crtc->index * 0x400), 2); + evo_data(push, 0x00000311); + evo_data(push, 0x00000100); + } else { + evo_mthd(push, 0x0410 + (nv_crtc->index * 0x300), 6); + evo_data(push, 0x00000000); + evo_data(push, (vactive << 16) | hactive); + evo_data(push, ( vsynce << 16) | hsynce); + evo_data(push, (vblanke << 16) | hblanke); + evo_data(push, (vblanks << 16) | hblanks); + evo_data(push, (vblan2e << 16) | vblan2s); + evo_mthd(push, 0x042c + (nv_crtc->index * 0x300), 1); + evo_data(push, 0x00000000); /* ??? */ + evo_mthd(push, 0x0450 + (nv_crtc->index * 0x300), 3); + evo_data(push, mode->clock * 1000); + evo_data(push, 0x00200000); /* ??? */ + evo_data(push, mode->clock * 1000); + evo_mthd(push, 0x04d0 + (nv_crtc->index * 0x300), 2); + evo_data(push, 0x00000311); + evo_data(push, 0x00000100); + } + + evo_kick(push, mast); + } + + nv_connector = nouveau_crtc_connector_get(nv_crtc); + nv50_crtc_set_dither(nv_crtc, false); + nv50_crtc_set_scale(nv_crtc, false); + + /* G94 only accepts this after setting scale */ + if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) + nv50_crtc_set_raster_vblank_dmi(nv_crtc, vblankus); + + nv50_crtc_set_color_vibrance(nv_crtc, false); + nv50_crtc_set_image(nv_crtc, crtc->primary->fb, x, y, false); + return 0; +} + +static int +nv50_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, + struct drm_framebuffer *old_fb) +{ + struct nouveau_drm *drm = nouveau_drm(crtc->dev); + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + int ret; + + if (!crtc->primary->fb) { + NV_DEBUG(drm, "No FB bound\n"); + return 0; + } + + ret = nv50_crtc_swap_fbs(crtc, old_fb); + if (ret) + return ret; + + nv50_display_flip_stop(crtc); + nv50_crtc_set_image(nv_crtc, crtc->primary->fb, x, y, true); + nv50_display_flip_next(crtc, crtc->primary->fb, NULL, 1); + return 0; +} + +static int +nv50_crtc_mode_set_base_atomic(struct drm_crtc *crtc, + struct drm_framebuffer *fb, int x, int y, + enum mode_set_atomic state) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + nv50_display_flip_stop(crtc); + nv50_crtc_set_image(nv_crtc, fb, x, y, true); + return 0; +} + +static void +nv50_crtc_lut_load(struct drm_crtc *crtc) +{ + struct nv50_disp *disp = nv50_disp(crtc->dev); + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + void __iomem *lut = nvbo_kmap_obj_iovirtual(nv_crtc->lut.nvbo); + int i; + + for (i = 0; i < 256; i++) { + u16 r = nv_crtc->lut.r[i] >> 2; + u16 g = nv_crtc->lut.g[i] >> 2; + u16 b = nv_crtc->lut.b[i] >> 2; + + if (disp->disp->oclass < GF110_DISP) { + writew(r + 0x0000, lut + (i * 0x08) + 0); + writew(g + 0x0000, lut + (i * 0x08) + 2); + writew(b + 0x0000, lut + (i * 0x08) + 4); + } else { + writew(r + 0x6000, lut + (i * 0x20) + 0); + writew(g + 0x6000, lut + (i * 0x20) + 2); + writew(b + 0x6000, lut + (i * 0x20) + 4); + } + } +} + +static void +nv50_crtc_disable(struct drm_crtc *crtc) +{ + struct nv50_head *head = nv50_head(crtc); + evo_sync(crtc->dev); + if (head->image) + nouveau_bo_unpin(head->image); + nouveau_bo_ref(NULL, &head->image); +} + +static int +nv50_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, + uint32_t handle, uint32_t width, uint32_t height) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct drm_gem_object *gem = NULL; + struct nouveau_bo *nvbo = NULL; + int ret = 0; + + if (handle) { + if (width != 64 || height != 64) + return -EINVAL; + + gem = drm_gem_object_lookup(dev, file_priv, handle); + if (unlikely(!gem)) + return -ENOENT; + nvbo = nouveau_gem_object(gem); + + ret = nouveau_bo_pin(nvbo, TTM_PL_FLAG_VRAM, true); + } + + if (ret == 0) { + if (nv_crtc->cursor.nvbo) + nouveau_bo_unpin(nv_crtc->cursor.nvbo); + nouveau_bo_ref(nvbo, &nv_crtc->cursor.nvbo); + } + drm_gem_object_unreference_unlocked(gem); + + nv50_crtc_cursor_show_hide(nv_crtc, true, true); + return ret; +} + +static int +nv50_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct nv50_curs *curs = nv50_curs(crtc); + struct nv50_chan *chan = nv50_chan(curs); + nvif_wr32(&chan->user, 0x0084, (y << 16) | (x & 0xffff)); + nvif_wr32(&chan->user, 0x0080, 0x00000000); + + nv_crtc->cursor_saved_x = x; + nv_crtc->cursor_saved_y = y; + return 0; +} + +static void +nv50_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, + uint32_t start, uint32_t size) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + u32 end = min_t(u32, start + size, 256); + u32 i; + + for (i = start; i < end; i++) { + nv_crtc->lut.r[i] = r[i]; + nv_crtc->lut.g[i] = g[i]; + nv_crtc->lut.b[i] = b[i]; + } + + nv50_crtc_lut_load(crtc); +} + +static void +nv50_crtc_cursor_restore(struct nouveau_crtc *nv_crtc, int x, int y) +{ + nv50_crtc_cursor_move(&nv_crtc->base, x, y); + + nv50_crtc_cursor_show_hide(nv_crtc, true, true); +} + +static void +nv50_crtc_destroy(struct drm_crtc *crtc) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct nv50_disp *disp = nv50_disp(crtc->dev); + struct nv50_head *head = nv50_head(crtc); + struct nv50_fbdma *fbdma; + + list_for_each_entry(fbdma, &disp->fbdma, head) { + nvif_object_fini(&fbdma->base[nv_crtc->index]); + } + + nv50_dmac_destroy(&head->ovly.base, disp->disp); + nv50_pioc_destroy(&head->oimm.base); + nv50_dmac_destroy(&head->sync.base, disp->disp); + nv50_pioc_destroy(&head->curs.base); + + /*XXX: this shouldn't be necessary, but the core doesn't call + * disconnect() during the cleanup paths + */ + if (head->image) + nouveau_bo_unpin(head->image); + nouveau_bo_ref(NULL, &head->image); + + /*XXX: ditto */ + if (nv_crtc->cursor.nvbo) + nouveau_bo_unpin(nv_crtc->cursor.nvbo); + nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo); + + nouveau_bo_unmap(nv_crtc->lut.nvbo); + if (nv_crtc->lut.nvbo) + nouveau_bo_unpin(nv_crtc->lut.nvbo); + nouveau_bo_ref(NULL, &nv_crtc->lut.nvbo); + + drm_crtc_cleanup(crtc); + kfree(crtc); +} + +static const struct drm_crtc_helper_funcs nv50_crtc_hfunc = { + .dpms = nv50_crtc_dpms, + .prepare = nv50_crtc_prepare, + .commit = nv50_crtc_commit, + .mode_fixup = nv50_crtc_mode_fixup, + .mode_set = nv50_crtc_mode_set, + .mode_set_base = nv50_crtc_mode_set_base, + .mode_set_base_atomic = nv50_crtc_mode_set_base_atomic, + .load_lut = nv50_crtc_lut_load, + .disable = nv50_crtc_disable, +}; + +static const struct drm_crtc_funcs nv50_crtc_func = { + .cursor_set = nv50_crtc_cursor_set, + .cursor_move = nv50_crtc_cursor_move, + .gamma_set = nv50_crtc_gamma_set, + .set_config = nouveau_crtc_set_config, + .destroy = nv50_crtc_destroy, + .page_flip = nouveau_crtc_page_flip, +}; + +static int +nv50_crtc_create(struct drm_device *dev, int index) +{ + struct nv50_disp *disp = nv50_disp(dev); + struct nv50_head *head; + struct drm_crtc *crtc; + int ret, i; + + head = kzalloc(sizeof(*head), GFP_KERNEL); + if (!head) + return -ENOMEM; + + head->base.index = index; + head->base.set_dither = nv50_crtc_set_dither; + head->base.set_scale = nv50_crtc_set_scale; + head->base.set_color_vibrance = nv50_crtc_set_color_vibrance; + head->base.color_vibrance = 50; + head->base.vibrant_hue = 0; + head->base.cursor.set_pos = nv50_crtc_cursor_restore; + for (i = 0; i < 256; i++) { + head->base.lut.r[i] = i << 8; + head->base.lut.g[i] = i << 8; + head->base.lut.b[i] = i << 8; + } + + crtc = &head->base.base; + drm_crtc_init(dev, crtc, &nv50_crtc_func); + drm_crtc_helper_add(crtc, &nv50_crtc_hfunc); + drm_mode_crtc_set_gamma_size(crtc, 256); + + ret = nouveau_bo_new(dev, 8192, 0x100, TTM_PL_FLAG_VRAM, + 0, 0x0000, NULL, NULL, &head->base.lut.nvbo); + if (!ret) { + ret = nouveau_bo_pin(head->base.lut.nvbo, TTM_PL_FLAG_VRAM, true); + if (!ret) { + ret = nouveau_bo_map(head->base.lut.nvbo); + if (ret) + nouveau_bo_unpin(head->base.lut.nvbo); + } + if (ret) + nouveau_bo_ref(NULL, &head->base.lut.nvbo); + } + + if (ret) + goto out; + + /* allocate cursor resources */ + ret = nv50_curs_create(disp->disp, index, &head->curs); + if (ret) + goto out; + + /* allocate page flip / sync resources */ + ret = nv50_base_create(disp->disp, index, disp->sync->bo.offset, + &head->sync); + if (ret) + goto out; + + head->sync.addr = EVO_FLIP_SEM0(index); + head->sync.data = 0x00000000; + + /* allocate overlay resources */ + ret = nv50_oimm_create(disp->disp, index, &head->oimm); + if (ret) + goto out; + + ret = nv50_ovly_create(disp->disp, index, disp->sync->bo.offset, + &head->ovly); + if (ret) + goto out; + +out: + if (ret) + nv50_crtc_destroy(crtc); + return ret; +} + +/****************************************************************************** + * Encoder helpers + *****************************************************************************/ +static bool +nv50_encoder_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_connector *nv_connector; + + nv_connector = nouveau_encoder_connector_get(nv_encoder); + if (nv_connector && nv_connector->native_mode) { + nv_connector->scaling_full = false; + if (nv_connector->scaling_mode == DRM_MODE_SCALE_NONE) { + switch (nv_connector->type) { + case DCB_CONNECTOR_LVDS: + case DCB_CONNECTOR_LVDS_SPWG: + case DCB_CONNECTOR_eDP: + /* force use of scaler for non-edid modes */ + if (adjusted_mode->type & DRM_MODE_TYPE_DRIVER) + return true; + nv_connector->scaling_full = true; + break; + default: + return true; + } + } + + drm_mode_copy(adjusted_mode, nv_connector->native_mode); + } + + return true; +} + +/****************************************************************************** + * DAC + *****************************************************************************/ +static void +nv50_dac_dpms(struct drm_encoder *encoder, int mode) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nv50_disp *disp = nv50_disp(encoder->dev); + struct { + struct nv50_disp_mthd_v1 base; + struct nv50_disp_dac_pwr_v0 pwr; + } args = { + .base.version = 1, + .base.method = NV50_DISP_MTHD_V1_DAC_PWR, + .base.hasht = nv_encoder->dcb->hasht, + .base.hashm = nv_encoder->dcb->hashm, + .pwr.state = 1, + .pwr.data = 1, + .pwr.vsync = (mode != DRM_MODE_DPMS_SUSPEND && + mode != DRM_MODE_DPMS_OFF), + .pwr.hsync = (mode != DRM_MODE_DPMS_STANDBY && + mode != DRM_MODE_DPMS_OFF), + }; + + nvif_mthd(disp->disp, 0, &args, sizeof(args)); +} + +static void +nv50_dac_commit(struct drm_encoder *encoder) +{ +} + +static void +nv50_dac_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct nv50_mast *mast = nv50_mast(encoder->dev); + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); + u32 *push; + + nv50_dac_dpms(encoder, DRM_MODE_DPMS_ON); + + push = evo_wait(mast, 8); + if (push) { + if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) { + u32 syncs = 0x00000000; + + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + syncs |= 0x00000001; + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + syncs |= 0x00000002; + + evo_mthd(push, 0x0400 + (nv_encoder->or * 0x080), 2); + evo_data(push, 1 << nv_crtc->index); + evo_data(push, syncs); + } else { + u32 magic = 0x31ec6000 | (nv_crtc->index << 25); + u32 syncs = 0x00000001; + + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + syncs |= 0x00000008; + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + syncs |= 0x00000010; + + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + magic |= 0x00000001; + + evo_mthd(push, 0x0404 + (nv_crtc->index * 0x300), 2); + evo_data(push, syncs); + evo_data(push, magic); + evo_mthd(push, 0x0180 + (nv_encoder->or * 0x020), 1); + evo_data(push, 1 << nv_crtc->index); + } + + evo_kick(push, mast); + } + + nv_encoder->crtc = encoder->crtc; +} + +static void +nv50_dac_disconnect(struct drm_encoder *encoder) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nv50_mast *mast = nv50_mast(encoder->dev); + const int or = nv_encoder->or; + u32 *push; + + if (nv_encoder->crtc) { + nv50_crtc_prepare(nv_encoder->crtc); + + push = evo_wait(mast, 4); + if (push) { + if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) { + evo_mthd(push, 0x0400 + (or * 0x080), 1); + evo_data(push, 0x00000000); + } else { + evo_mthd(push, 0x0180 + (or * 0x020), 1); + evo_data(push, 0x00000000); + } + evo_kick(push, mast); + } + } + + nv_encoder->crtc = NULL; +} + +static enum drm_connector_status +nv50_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nv50_disp *disp = nv50_disp(encoder->dev); + struct { + struct nv50_disp_mthd_v1 base; + struct nv50_disp_dac_load_v0 load; + } args = { + .base.version = 1, + .base.method = NV50_DISP_MTHD_V1_DAC_LOAD, + .base.hasht = nv_encoder->dcb->hasht, + .base.hashm = nv_encoder->dcb->hashm, + }; + int ret; + + args.load.data = nouveau_drm(encoder->dev)->vbios.dactestval; + if (args.load.data == 0) + args.load.data = 340; + + ret = nvif_mthd(disp->disp, 0, &args, sizeof(args)); + if (ret || !args.load.load) + return connector_status_disconnected; + + return connector_status_connected; +} + +static void +nv50_dac_destroy(struct drm_encoder *encoder) +{ + drm_encoder_cleanup(encoder); + kfree(encoder); +} + +static const struct drm_encoder_helper_funcs nv50_dac_hfunc = { + .dpms = nv50_dac_dpms, + .mode_fixup = nv50_encoder_mode_fixup, + .prepare = nv50_dac_disconnect, + .commit = nv50_dac_commit, + .mode_set = nv50_dac_mode_set, + .disable = nv50_dac_disconnect, + .get_crtc = nv50_display_crtc_get, + .detect = nv50_dac_detect +}; + +static const struct drm_encoder_funcs nv50_dac_func = { + .destroy = nv50_dac_destroy, +}; + +static int +nv50_dac_create(struct drm_connector *connector, struct dcb_output *dcbe) +{ + struct nouveau_drm *drm = nouveau_drm(connector->dev); + struct nvkm_i2c *i2c = nvxx_i2c(&drm->device); + struct nouveau_encoder *nv_encoder; + struct drm_encoder *encoder; + int type = DRM_MODE_ENCODER_DAC; + + nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); + if (!nv_encoder) + return -ENOMEM; + nv_encoder->dcb = dcbe; + nv_encoder->or = ffs(dcbe->or) - 1; + nv_encoder->i2c = i2c->find(i2c, dcbe->i2c_index); + + encoder = to_drm_encoder(nv_encoder); + encoder->possible_crtcs = dcbe->heads; + encoder->possible_clones = 0; + drm_encoder_init(connector->dev, encoder, &nv50_dac_func, type); + drm_encoder_helper_add(encoder, &nv50_dac_hfunc); + + drm_mode_connector_attach_encoder(connector, encoder); + return 0; +} + +/****************************************************************************** + * Audio + *****************************************************************************/ +static void +nv50_audio_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); + struct nouveau_connector *nv_connector; + struct nv50_disp *disp = nv50_disp(encoder->dev); + struct __packed { + struct { + struct nv50_disp_mthd_v1 mthd; + struct nv50_disp_sor_hda_eld_v0 eld; + } base; + u8 data[sizeof(nv_connector->base.eld)]; + } args = { + .base.mthd.version = 1, + .base.mthd.method = NV50_DISP_MTHD_V1_SOR_HDA_ELD, + .base.mthd.hasht = nv_encoder->dcb->hasht, + .base.mthd.hashm = (0xf0ff & nv_encoder->dcb->hashm) | + (0x0100 << nv_crtc->index), + }; + + nv_connector = nouveau_encoder_connector_get(nv_encoder); + if (!drm_detect_monitor_audio(nv_connector->edid)) + return; + + drm_edid_to_eld(&nv_connector->base, nv_connector->edid); + memcpy(args.data, nv_connector->base.eld, sizeof(args.data)); + + nvif_mthd(disp->disp, 0, &args, + sizeof(args.base) + drm_eld_size(args.data)); +} + +static void +nv50_audio_disconnect(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nv50_disp *disp = nv50_disp(encoder->dev); + struct { + struct nv50_disp_mthd_v1 base; + struct nv50_disp_sor_hda_eld_v0 eld; + } args = { + .base.version = 1, + .base.method = NV50_DISP_MTHD_V1_SOR_HDA_ELD, + .base.hasht = nv_encoder->dcb->hasht, + .base.hashm = (0xf0ff & nv_encoder->dcb->hashm) | + (0x0100 << nv_crtc->index), + }; + + nvif_mthd(disp->disp, 0, &args, sizeof(args)); +} + +/****************************************************************************** + * HDMI + *****************************************************************************/ +static void +nv50_hdmi_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); + struct nv50_disp *disp = nv50_disp(encoder->dev); + struct { + struct nv50_disp_mthd_v1 base; + struct nv50_disp_sor_hdmi_pwr_v0 pwr; + } args = { + .base.version = 1, + .base.method = NV50_DISP_MTHD_V1_SOR_HDMI_PWR, + .base.hasht = nv_encoder->dcb->hasht, + .base.hashm = (0xf0ff & nv_encoder->dcb->hashm) | + (0x0100 << nv_crtc->index), + .pwr.state = 1, + .pwr.rekey = 56, /* binary driver, and tegra, constant */ + }; + struct nouveau_connector *nv_connector; + u32 max_ac_packet; + + nv_connector = nouveau_encoder_connector_get(nv_encoder); + if (!drm_detect_hdmi_monitor(nv_connector->edid)) + return; + + max_ac_packet = mode->htotal - mode->hdisplay; + max_ac_packet -= args.pwr.rekey; + max_ac_packet -= 18; /* constant from tegra */ + args.pwr.max_ac_packet = max_ac_packet / 32; + + nvif_mthd(disp->disp, 0, &args, sizeof(args)); + nv50_audio_mode_set(encoder, mode); +} + +static void +nv50_hdmi_disconnect(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nv50_disp *disp = nv50_disp(encoder->dev); + struct { + struct nv50_disp_mthd_v1 base; + struct nv50_disp_sor_hdmi_pwr_v0 pwr; + } args = { + .base.version = 1, + .base.method = NV50_DISP_MTHD_V1_SOR_HDMI_PWR, + .base.hasht = nv_encoder->dcb->hasht, + .base.hashm = (0xf0ff & nv_encoder->dcb->hashm) | + (0x0100 << nv_crtc->index), + }; + + nvif_mthd(disp->disp, 0, &args, sizeof(args)); +} + +/****************************************************************************** + * SOR + *****************************************************************************/ +static void +nv50_sor_dpms(struct drm_encoder *encoder, int mode) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nv50_disp *disp = nv50_disp(encoder->dev); + struct { + struct nv50_disp_mthd_v1 base; + struct nv50_disp_sor_pwr_v0 pwr; + } args = { + .base.version = 1, + .base.method = NV50_DISP_MTHD_V1_SOR_PWR, + .base.hasht = nv_encoder->dcb->hasht, + .base.hashm = nv_encoder->dcb->hashm, + .pwr.state = mode == DRM_MODE_DPMS_ON, + }; + struct { + struct nv50_disp_mthd_v1 base; + struct nv50_disp_sor_dp_pwr_v0 pwr; + } link = { + .base.version = 1, + .base.method = NV50_DISP_MTHD_V1_SOR_DP_PWR, + .base.hasht = nv_encoder->dcb->hasht, + .base.hashm = nv_encoder->dcb->hashm, + .pwr.state = mode == DRM_MODE_DPMS_ON, + }; + struct drm_device *dev = encoder->dev; + struct drm_encoder *partner; + + nv_encoder->last_dpms = mode; + + list_for_each_entry(partner, &dev->mode_config.encoder_list, head) { + struct nouveau_encoder *nv_partner = nouveau_encoder(partner); + + if (partner->encoder_type != DRM_MODE_ENCODER_TMDS) + continue; + + if (nv_partner != nv_encoder && + nv_partner->dcb->or == nv_encoder->dcb->or) { + if (nv_partner->last_dpms == DRM_MODE_DPMS_ON) + return; + break; + } + } + + if (nv_encoder->dcb->type == DCB_OUTPUT_DP) { + args.pwr.state = 1; + nvif_mthd(disp->disp, 0, &args, sizeof(args)); + nvif_mthd(disp->disp, 0, &link, sizeof(link)); + } else { + nvif_mthd(disp->disp, 0, &args, sizeof(args)); + } +} + +static void +nv50_sor_ctrl(struct nouveau_encoder *nv_encoder, u32 mask, u32 data) +{ + struct nv50_mast *mast = nv50_mast(nv_encoder->base.base.dev); + u32 temp = (nv_encoder->ctrl & ~mask) | (data & mask), *push; + if (temp != nv_encoder->ctrl && (push = evo_wait(mast, 2))) { + if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) { + evo_mthd(push, 0x0600 + (nv_encoder->or * 0x40), 1); + evo_data(push, (nv_encoder->ctrl = temp)); + } else { + evo_mthd(push, 0x0200 + (nv_encoder->or * 0x20), 1); + evo_data(push, (nv_encoder->ctrl = temp)); + } + evo_kick(push, mast); + } +} + +static void +nv50_sor_disconnect(struct drm_encoder *encoder) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_crtc *nv_crtc = nouveau_crtc(nv_encoder->crtc); + + nv_encoder->last_dpms = DRM_MODE_DPMS_OFF; + nv_encoder->crtc = NULL; + + if (nv_crtc) { + nv50_crtc_prepare(&nv_crtc->base); + nv50_sor_ctrl(nv_encoder, 1 << nv_crtc->index, 0); + nv50_audio_disconnect(encoder, nv_crtc); + nv50_hdmi_disconnect(&nv_encoder->base.base, nv_crtc); + } +} + +static void +nv50_sor_commit(struct drm_encoder *encoder) +{ +} + +static void +nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode, + struct drm_display_mode *mode) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); + struct { + struct nv50_disp_mthd_v1 base; + struct nv50_disp_sor_lvds_script_v0 lvds; + } lvds = { + .base.version = 1, + .base.method = NV50_DISP_MTHD_V1_SOR_LVDS_SCRIPT, + .base.hasht = nv_encoder->dcb->hasht, + .base.hashm = nv_encoder->dcb->hashm, + }; + struct nv50_disp *disp = nv50_disp(encoder->dev); + struct nv50_mast *mast = nv50_mast(encoder->dev); + struct drm_device *dev = encoder->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_connector *nv_connector; + struct nvbios *bios = &drm->vbios; + u32 mask, ctrl; + u8 owner = 1 << nv_crtc->index; + u8 proto = 0xf; + u8 depth = 0x0; + + nv_connector = nouveau_encoder_connector_get(nv_encoder); + nv_encoder->crtc = encoder->crtc; + + switch (nv_encoder->dcb->type) { + case DCB_OUTPUT_TMDS: + if (nv_encoder->dcb->sorconf.link & 1) { + if (mode->clock < 165000) + proto = 0x1; + else + proto = 0x5; + } else { + proto = 0x2; + } + + nv50_hdmi_mode_set(&nv_encoder->base.base, mode); + break; + case DCB_OUTPUT_LVDS: + proto = 0x0; + + if (bios->fp_no_ddc) { + if (bios->fp.dual_link) + lvds.lvds.script |= 0x0100; + if (bios->fp.if_is_24bit) + lvds.lvds.script |= 0x0200; + } else { + if (nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) { + if (((u8 *)nv_connector->edid)[121] == 2) + lvds.lvds.script |= 0x0100; + } else + if (mode->clock >= bios->fp.duallink_transition_clk) { + lvds.lvds.script |= 0x0100; + } + + if (lvds.lvds.script & 0x0100) { + if (bios->fp.strapless_is_24bit & 2) + lvds.lvds.script |= 0x0200; + } else { + if (bios->fp.strapless_is_24bit & 1) + lvds.lvds.script |= 0x0200; + } + + if (nv_connector->base.display_info.bpc == 8) + lvds.lvds.script |= 0x0200; + } + + nvif_mthd(disp->disp, 0, &lvds, sizeof(lvds)); + break; + case DCB_OUTPUT_DP: + if (nv_connector->base.display_info.bpc == 6) { + nv_encoder->dp.datarate = mode->clock * 18 / 8; + depth = 0x2; + } else + if (nv_connector->base.display_info.bpc == 8) { + nv_encoder->dp.datarate = mode->clock * 24 / 8; + depth = 0x5; + } else { + nv_encoder->dp.datarate = mode->clock * 30 / 8; + depth = 0x6; + } + + if (nv_encoder->dcb->sorconf.link & 1) + proto = 0x8; + else + proto = 0x9; + nv50_audio_mode_set(encoder, mode); + break; + default: + BUG_ON(1); + break; + } + + nv50_sor_dpms(&nv_encoder->base.base, DRM_MODE_DPMS_ON); + + if (nv50_vers(mast) >= GF110_DISP) { + u32 *push = evo_wait(mast, 3); + if (push) { + u32 magic = 0x31ec6000 | (nv_crtc->index << 25); + u32 syncs = 0x00000001; + + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + syncs |= 0x00000008; + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + syncs |= 0x00000010; + + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + magic |= 0x00000001; + + evo_mthd(push, 0x0404 + (nv_crtc->index * 0x300), 2); + evo_data(push, syncs | (depth << 6)); + evo_data(push, magic); + evo_kick(push, mast); + } + + ctrl = proto << 8; + mask = 0x00000f00; + } else { + ctrl = (depth << 16) | (proto << 8); + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + ctrl |= 0x00001000; + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + ctrl |= 0x00002000; + mask = 0x000f3f00; + } + + nv50_sor_ctrl(nv_encoder, mask | owner, ctrl | owner); +} + +static void +nv50_sor_destroy(struct drm_encoder *encoder) +{ + drm_encoder_cleanup(encoder); + kfree(encoder); +} + +static const struct drm_encoder_helper_funcs nv50_sor_hfunc = { + .dpms = nv50_sor_dpms, + .mode_fixup = nv50_encoder_mode_fixup, + .prepare = nv50_sor_disconnect, + .commit = nv50_sor_commit, + .mode_set = nv50_sor_mode_set, + .disable = nv50_sor_disconnect, + .get_crtc = nv50_display_crtc_get, +}; + +static const struct drm_encoder_funcs nv50_sor_func = { + .destroy = nv50_sor_destroy, +}; + +static int +nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe) +{ + struct nouveau_drm *drm = nouveau_drm(connector->dev); + struct nvkm_i2c *i2c = nvxx_i2c(&drm->device); + struct nouveau_encoder *nv_encoder; + struct drm_encoder *encoder; + int type; + + switch (dcbe->type) { + case DCB_OUTPUT_LVDS: type = DRM_MODE_ENCODER_LVDS; break; + case DCB_OUTPUT_TMDS: + case DCB_OUTPUT_DP: + default: + type = DRM_MODE_ENCODER_TMDS; + break; + } + + nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); + if (!nv_encoder) + return -ENOMEM; + nv_encoder->dcb = dcbe; + nv_encoder->or = ffs(dcbe->or) - 1; + nv_encoder->i2c = i2c->find(i2c, dcbe->i2c_index); + nv_encoder->last_dpms = DRM_MODE_DPMS_OFF; + + encoder = to_drm_encoder(nv_encoder); + encoder->possible_crtcs = dcbe->heads; + encoder->possible_clones = 0; + drm_encoder_init(connector->dev, encoder, &nv50_sor_func, type); + drm_encoder_helper_add(encoder, &nv50_sor_hfunc); + + drm_mode_connector_attach_encoder(connector, encoder); + return 0; +} + +/****************************************************************************** + * PIOR + *****************************************************************************/ + +static void +nv50_pior_dpms(struct drm_encoder *encoder, int mode) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nv50_disp *disp = nv50_disp(encoder->dev); + struct { + struct nv50_disp_mthd_v1 base; + struct nv50_disp_pior_pwr_v0 pwr; + } args = { + .base.version = 1, + .base.method = NV50_DISP_MTHD_V1_PIOR_PWR, + .base.hasht = nv_encoder->dcb->hasht, + .base.hashm = nv_encoder->dcb->hashm, + .pwr.state = mode == DRM_MODE_DPMS_ON, + .pwr.type = nv_encoder->dcb->type, + }; + + nvif_mthd(disp->disp, 0, &args, sizeof(args)); +} + +static bool +nv50_pior_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + if (!nv50_encoder_mode_fixup(encoder, mode, adjusted_mode)) + return false; + adjusted_mode->clock *= 2; + return true; +} + +static void +nv50_pior_commit(struct drm_encoder *encoder) +{ +} + +static void +nv50_pior_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct nv50_mast *mast = nv50_mast(encoder->dev); + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); + struct nouveau_connector *nv_connector; + u8 owner = 1 << nv_crtc->index; + u8 proto, depth; + u32 *push; + + nv_connector = nouveau_encoder_connector_get(nv_encoder); + switch (nv_connector->base.display_info.bpc) { + case 10: depth = 0x6; break; + case 8: depth = 0x5; break; + case 6: depth = 0x2; break; + default: depth = 0x0; break; + } + + switch (nv_encoder->dcb->type) { + case DCB_OUTPUT_TMDS: + case DCB_OUTPUT_DP: + proto = 0x0; + break; + default: + BUG_ON(1); + break; + } + + nv50_pior_dpms(encoder, DRM_MODE_DPMS_ON); + + push = evo_wait(mast, 8); + if (push) { + if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) { + u32 ctrl = (depth << 16) | (proto << 8) | owner; + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + ctrl |= 0x00001000; + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + ctrl |= 0x00002000; + evo_mthd(push, 0x0700 + (nv_encoder->or * 0x040), 1); + evo_data(push, ctrl); + } + + evo_kick(push, mast); + } + + nv_encoder->crtc = encoder->crtc; +} + +static void +nv50_pior_disconnect(struct drm_encoder *encoder) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nv50_mast *mast = nv50_mast(encoder->dev); + const int or = nv_encoder->or; + u32 *push; + + if (nv_encoder->crtc) { + nv50_crtc_prepare(nv_encoder->crtc); + + push = evo_wait(mast, 4); + if (push) { + if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) { + evo_mthd(push, 0x0700 + (or * 0x040), 1); + evo_data(push, 0x00000000); + } + evo_kick(push, mast); + } + } + + nv_encoder->crtc = NULL; +} + +static void +nv50_pior_destroy(struct drm_encoder *encoder) +{ + drm_encoder_cleanup(encoder); + kfree(encoder); +} + +static const struct drm_encoder_helper_funcs nv50_pior_hfunc = { + .dpms = nv50_pior_dpms, + .mode_fixup = nv50_pior_mode_fixup, + .prepare = nv50_pior_disconnect, + .commit = nv50_pior_commit, + .mode_set = nv50_pior_mode_set, + .disable = nv50_pior_disconnect, + .get_crtc = nv50_display_crtc_get, +}; + +static const struct drm_encoder_funcs nv50_pior_func = { + .destroy = nv50_pior_destroy, +}; + +static int +nv50_pior_create(struct drm_connector *connector, struct dcb_output *dcbe) +{ + struct nouveau_drm *drm = nouveau_drm(connector->dev); + struct nvkm_i2c *i2c = nvxx_i2c(&drm->device); + struct nvkm_i2c_port *ddc = NULL; + struct nouveau_encoder *nv_encoder; + struct drm_encoder *encoder; + int type; + + switch (dcbe->type) { + case DCB_OUTPUT_TMDS: + ddc = i2c->find_type(i2c, NV_I2C_TYPE_EXTDDC(dcbe->extdev)); + type = DRM_MODE_ENCODER_TMDS; + break; + case DCB_OUTPUT_DP: + ddc = i2c->find_type(i2c, NV_I2C_TYPE_EXTAUX(dcbe->extdev)); + type = DRM_MODE_ENCODER_TMDS; + break; + default: + return -ENODEV; + } + + nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); + if (!nv_encoder) + return -ENOMEM; + nv_encoder->dcb = dcbe; + nv_encoder->or = ffs(dcbe->or) - 1; + nv_encoder->i2c = ddc; + + encoder = to_drm_encoder(nv_encoder); + encoder->possible_crtcs = dcbe->heads; + encoder->possible_clones = 0; + drm_encoder_init(connector->dev, encoder, &nv50_pior_func, type); + drm_encoder_helper_add(encoder, &nv50_pior_hfunc); + + drm_mode_connector_attach_encoder(connector, encoder); + return 0; +} + +/****************************************************************************** + * Framebuffer + *****************************************************************************/ + +static void +nv50_fbdma_fini(struct nv50_fbdma *fbdma) +{ + int i; + for (i = 0; i < ARRAY_SIZE(fbdma->base); i++) + nvif_object_fini(&fbdma->base[i]); + nvif_object_fini(&fbdma->core); + list_del(&fbdma->head); + kfree(fbdma); +} + +static int +nv50_fbdma_init(struct drm_device *dev, u32 name, u64 offset, u64 length, u8 kind) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nv50_disp *disp = nv50_disp(dev); + struct nv50_mast *mast = nv50_mast(dev); + struct __attribute__ ((packed)) { + struct nv_dma_v0 base; + union { + struct nv50_dma_v0 nv50; + struct gf100_dma_v0 gf100; + struct gf110_dma_v0 gf110; + }; + } args = {}; + struct nv50_fbdma *fbdma; + struct drm_crtc *crtc; + u32 size = sizeof(args.base); + int ret; + + list_for_each_entry(fbdma, &disp->fbdma, head) { + if (fbdma->core.handle == name) + return 0; + } + + fbdma = kzalloc(sizeof(*fbdma), GFP_KERNEL); + if (!fbdma) + return -ENOMEM; + list_add(&fbdma->head, &disp->fbdma); + + args.base.target = NV_DMA_V0_TARGET_VRAM; + args.base.access = NV_DMA_V0_ACCESS_RDWR; + args.base.start = offset; + args.base.limit = offset + length - 1; + + if (drm->device.info.chipset < 0x80) { + args.nv50.part = NV50_DMA_V0_PART_256; + size += sizeof(args.nv50); + } else + if (drm->device.info.chipset < 0xc0) { + args.nv50.part = NV50_DMA_V0_PART_256; + args.nv50.kind = kind; + size += sizeof(args.nv50); + } else + if (drm->device.info.chipset < 0xd0) { + args.gf100.kind = kind; + size += sizeof(args.gf100); + } else { + args.gf110.page = GF110_DMA_V0_PAGE_LP; + args.gf110.kind = kind; + size += sizeof(args.gf110); + } + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct nv50_head *head = nv50_head(crtc); + int ret = nvif_object_init(&head->sync.base.base.user, NULL, + name, NV_DMA_IN_MEMORY, &args, size, + &fbdma->base[head->base.index]); + if (ret) { + nv50_fbdma_fini(fbdma); + return ret; + } + } + + ret = nvif_object_init(&mast->base.base.user, NULL, name, + NV_DMA_IN_MEMORY, &args, size, + &fbdma->core); + if (ret) { + nv50_fbdma_fini(fbdma); + return ret; + } + + return 0; +} + +static void +nv50_fb_dtor(struct drm_framebuffer *fb) +{ +} + +static int +nv50_fb_ctor(struct drm_framebuffer *fb) +{ + struct nouveau_framebuffer *nv_fb = nouveau_framebuffer(fb); + struct nouveau_drm *drm = nouveau_drm(fb->dev); + struct nouveau_bo *nvbo = nv_fb->nvbo; + struct nv50_disp *disp = nv50_disp(fb->dev); + u8 kind = nouveau_bo_tile_layout(nvbo) >> 8; + u8 tile = nvbo->tile_mode; + + if (drm->device.info.chipset >= 0xc0) + tile >>= 4; /* yep.. */ + + switch (fb->depth) { + case 8: nv_fb->r_format = 0x1e00; break; + case 15: nv_fb->r_format = 0xe900; break; + case 16: nv_fb->r_format = 0xe800; break; + case 24: + case 32: nv_fb->r_format = 0xcf00; break; + case 30: nv_fb->r_format = 0xd100; break; + default: + NV_ERROR(drm, "unknown depth %d\n", fb->depth); + return -EINVAL; + } + + if (disp->disp->oclass < G82_DISP) { + nv_fb->r_pitch = kind ? (((fb->pitches[0] / 4) << 4) | tile) : + (fb->pitches[0] | 0x00100000); + nv_fb->r_format |= kind << 16; + } else + if (disp->disp->oclass < GF110_DISP) { + nv_fb->r_pitch = kind ? (((fb->pitches[0] / 4) << 4) | tile) : + (fb->pitches[0] | 0x00100000); + } else { + nv_fb->r_pitch = kind ? (((fb->pitches[0] / 4) << 4) | tile) : + (fb->pitches[0] | 0x01000000); + } + nv_fb->r_handle = 0xffff0000 | kind; + + return nv50_fbdma_init(fb->dev, nv_fb->r_handle, 0, + drm->device.info.ram_user, kind); +} + +/****************************************************************************** + * Init + *****************************************************************************/ + +void +nv50_display_fini(struct drm_device *dev) +{ +} + +int +nv50_display_init(struct drm_device *dev) +{ + struct nv50_disp *disp = nv50_disp(dev); + struct drm_crtc *crtc; + u32 *push; + + push = evo_wait(nv50_mast(dev), 32); + if (!push) + return -EBUSY; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct nv50_sync *sync = nv50_sync(crtc); + + nv50_crtc_lut_load(crtc); + nouveau_bo_wr32(disp->sync, sync->addr / 4, sync->data); + } + + evo_mthd(push, 0x0088, 1); + evo_data(push, nv50_mast(dev)->base.sync.handle); + evo_kick(push, nv50_mast(dev)); + return 0; +} + +void +nv50_display_destroy(struct drm_device *dev) +{ + struct nv50_disp *disp = nv50_disp(dev); + struct nv50_fbdma *fbdma, *fbtmp; + + list_for_each_entry_safe(fbdma, fbtmp, &disp->fbdma, head) { + nv50_fbdma_fini(fbdma); + } + + nv50_dmac_destroy(&disp->mast.base, disp->disp); + + nouveau_bo_unmap(disp->sync); + if (disp->sync) + nouveau_bo_unpin(disp->sync); + nouveau_bo_ref(NULL, &disp->sync); + + nouveau_display(dev)->priv = NULL; + kfree(disp); +} + +int +nv50_display_create(struct drm_device *dev) +{ + struct nvif_device *device = &nouveau_drm(dev)->device; + struct nouveau_drm *drm = nouveau_drm(dev); + struct dcb_table *dcb = &drm->vbios.dcb; + struct drm_connector *connector, *tmp; + struct nv50_disp *disp; + struct dcb_output *dcbe; + int crtcs, ret, i; + + disp = kzalloc(sizeof(*disp), GFP_KERNEL); + if (!disp) + return -ENOMEM; + INIT_LIST_HEAD(&disp->fbdma); + + nouveau_display(dev)->priv = disp; + nouveau_display(dev)->dtor = nv50_display_destroy; + nouveau_display(dev)->init = nv50_display_init; + nouveau_display(dev)->fini = nv50_display_fini; + nouveau_display(dev)->fb_ctor = nv50_fb_ctor; + nouveau_display(dev)->fb_dtor = nv50_fb_dtor; + disp->disp = &nouveau_display(dev)->disp; + + /* small shared memory area we use for notifiers and semaphores */ + ret = nouveau_bo_new(dev, 4096, 0x1000, TTM_PL_FLAG_VRAM, + 0, 0x0000, NULL, NULL, &disp->sync); + if (!ret) { + ret = nouveau_bo_pin(disp->sync, TTM_PL_FLAG_VRAM, true); + if (!ret) { + ret = nouveau_bo_map(disp->sync); + if (ret) + nouveau_bo_unpin(disp->sync); + } + if (ret) + nouveau_bo_ref(NULL, &disp->sync); + } + + if (ret) + goto out; + + /* allocate master evo channel */ + ret = nv50_core_create(disp->disp, disp->sync->bo.offset, + &disp->mast); + if (ret) + goto out; + + /* create crtc objects to represent the hw heads */ + if (disp->disp->oclass >= GF110_DISP) + crtcs = nvif_rd32(device, 0x022448); + else + crtcs = 2; + + for (i = 0; i < crtcs; i++) { + ret = nv50_crtc_create(dev, i); + if (ret) + goto out; + } + + /* create encoder/connector objects based on VBIOS DCB table */ + for (i = 0, dcbe = &dcb->entry[0]; i < dcb->entries; i++, dcbe++) { + connector = nouveau_connector_create(dev, dcbe->connector); + if (IS_ERR(connector)) + continue; + + if (dcbe->location == DCB_LOC_ON_CHIP) { + switch (dcbe->type) { + case DCB_OUTPUT_TMDS: + case DCB_OUTPUT_LVDS: + case DCB_OUTPUT_DP: + ret = nv50_sor_create(connector, dcbe); + break; + case DCB_OUTPUT_ANALOG: + ret = nv50_dac_create(connector, dcbe); + break; + default: + ret = -ENODEV; + break; + } + } else { + ret = nv50_pior_create(connector, dcbe); + } + + if (ret) { + NV_WARN(drm, "failed to create encoder %d/%d/%d: %d\n", + dcbe->location, dcbe->type, + ffs(dcbe->or) - 1, ret); + ret = 0; + } + } + + /* cull any connectors we created that don't have an encoder */ + list_for_each_entry_safe(connector, tmp, &dev->mode_config.connector_list, head) { + if (connector->encoder_ids[0]) + continue; + + NV_WARN(drm, "%s has no encoders, removing\n", + connector->name); + connector->funcs->destroy(connector); + } + +out: + if (ret) + nv50_display_destroy(dev); + return ret; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nv50_display.h b/kernel/drivers/gpu/drm/nouveau/nv50_display.h new file mode 100644 index 000000000..70da347aa --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nv50_display.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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. + * + */ + +#ifndef __NV50_DISPLAY_H__ +#define __NV50_DISPLAY_H__ + +#include "nouveau_display.h" +#include "nouveau_crtc.h" +#include "nouveau_reg.h" + +int nv50_display_create(struct drm_device *); +void nv50_display_destroy(struct drm_device *); +int nv50_display_init(struct drm_device *); +void nv50_display_fini(struct drm_device *); + +void nv50_display_flip_stop(struct drm_crtc *); +int nv50_display_flip_next(struct drm_crtc *, struct drm_framebuffer *, + struct nouveau_channel *, u32 swap_interval); + +struct nouveau_bo *nv50_display_crtc_sema(struct drm_device *, int head); + +#endif /* __NV50_DISPLAY_H__ */ diff --git a/kernel/drivers/gpu/drm/nouveau/nv50_fbcon.c b/kernel/drivers/gpu/drm/nouveau/nv50_fbcon.c new file mode 100644 index 000000000..394c89abc --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nv50_fbcon.c @@ -0,0 +1,258 @@ +/* + * 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 "nouveau_drm.h" +#include "nouveau_dma.h" +#include "nouveau_fbcon.h" + +int +nv50_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect) +{ + struct nouveau_fbdev *nfbdev = info->par; + struct nouveau_drm *drm = nouveau_drm(nfbdev->dev); + struct nouveau_channel *chan = drm->channel; + int ret; + + ret = RING_SPACE(chan, rect->rop == ROP_COPY ? 7 : 11); + if (ret) + return ret; + + if (rect->rop != ROP_COPY) { + BEGIN_NV04(chan, NvSub2D, 0x02ac, 1); + OUT_RING(chan, 1); + } + BEGIN_NV04(chan, NvSub2D, 0x0588, 1); + if (info->fix.visual == FB_VISUAL_TRUECOLOR || + info->fix.visual == FB_VISUAL_DIRECTCOLOR) + OUT_RING(chan, ((uint32_t *)info->pseudo_palette)[rect->color]); + else + OUT_RING(chan, rect->color); + BEGIN_NV04(chan, NvSub2D, 0x0600, 4); + OUT_RING(chan, rect->dx); + OUT_RING(chan, rect->dy); + OUT_RING(chan, rect->dx + rect->width); + OUT_RING(chan, rect->dy + rect->height); + if (rect->rop != ROP_COPY) { + BEGIN_NV04(chan, NvSub2D, 0x02ac, 1); + OUT_RING(chan, 3); + } + FIRE_RING(chan); + return 0; +} + +int +nv50_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region) +{ + struct nouveau_fbdev *nfbdev = info->par; + struct nouveau_drm *drm = nouveau_drm(nfbdev->dev); + struct nouveau_channel *chan = drm->channel; + int ret; + + ret = RING_SPACE(chan, 12); + if (ret) + return ret; + + BEGIN_NV04(chan, NvSub2D, 0x0110, 1); + OUT_RING(chan, 0); + BEGIN_NV04(chan, NvSub2D, 0x08b0, 4); + OUT_RING(chan, region->dx); + OUT_RING(chan, region->dy); + OUT_RING(chan, region->width); + OUT_RING(chan, region->height); + BEGIN_NV04(chan, NvSub2D, 0x08d0, 4); + OUT_RING(chan, 0); + OUT_RING(chan, region->sx); + OUT_RING(chan, 0); + OUT_RING(chan, region->sy); + FIRE_RING(chan); + return 0; +} + +int +nv50_fbcon_imageblit(struct fb_info *info, const struct fb_image *image) +{ + struct nouveau_fbdev *nfbdev = info->par; + struct nouveau_drm *drm = nouveau_drm(nfbdev->dev); + struct nouveau_channel *chan = drm->channel; + uint32_t width, dwords, *data = (uint32_t *)image->data; + uint32_t mask = ~(~0 >> (32 - info->var.bits_per_pixel)); + uint32_t *palette = info->pseudo_palette; + int ret; + + if (image->depth != 1) + return -ENODEV; + + ret = RING_SPACE(chan, 11); + if (ret) + return ret; + + width = ALIGN(image->width, 32); + dwords = (width * image->height) >> 5; + + BEGIN_NV04(chan, NvSub2D, 0x0814, 2); + if (info->fix.visual == FB_VISUAL_TRUECOLOR || + info->fix.visual == FB_VISUAL_DIRECTCOLOR) { + OUT_RING(chan, palette[image->bg_color] | mask); + OUT_RING(chan, palette[image->fg_color] | mask); + } else { + OUT_RING(chan, image->bg_color); + OUT_RING(chan, image->fg_color); + } + BEGIN_NV04(chan, NvSub2D, 0x0838, 2); + OUT_RING(chan, image->width); + OUT_RING(chan, image->height); + BEGIN_NV04(chan, NvSub2D, 0x0850, 4); + OUT_RING(chan, 0); + OUT_RING(chan, image->dx); + OUT_RING(chan, 0); + OUT_RING(chan, image->dy); + + while (dwords) { + int push = dwords > 2047 ? 2047 : dwords; + + ret = RING_SPACE(chan, push + 1); + if (ret) + return ret; + + dwords -= push; + + BEGIN_NI04(chan, NvSub2D, 0x0860, push); + OUT_RINGp(chan, data, push); + data += push; + } + + FIRE_RING(chan); + return 0; +} + +int +nv50_fbcon_accel_init(struct fb_info *info) +{ + struct nouveau_fbdev *nfbdev = info->par; + struct nouveau_framebuffer *fb = &nfbdev->nouveau_fb; + struct drm_device *dev = nfbdev->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_channel *chan = drm->channel; + int ret, format; + + switch (info->var.bits_per_pixel) { + case 8: + format = 0xf3; + break; + case 15: + format = 0xf8; + break; + case 16: + format = 0xe8; + break; + case 32: + switch (info->var.transp.length) { + case 0: /* depth 24 */ + case 8: /* depth 32, just use 24.. */ + format = 0xe6; + break; + case 2: /* depth 30 */ + format = 0xd1; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + ret = nvif_object_init(chan->object, NULL, 0x502d, 0x502d, NULL, 0, + &nfbdev->twod); + if (ret) + return ret; + + ret = RING_SPACE(chan, 59); + if (ret) { + nouveau_fbcon_gpu_lockup(info); + return ret; + } + + BEGIN_NV04(chan, NvSub2D, 0x0000, 1); + OUT_RING(chan, nfbdev->twod.handle); + BEGIN_NV04(chan, NvSub2D, 0x0184, 3); + OUT_RING(chan, chan->vram.handle); + OUT_RING(chan, chan->vram.handle); + OUT_RING(chan, chan->vram.handle); + BEGIN_NV04(chan, NvSub2D, 0x0290, 1); + OUT_RING(chan, 0); + BEGIN_NV04(chan, NvSub2D, 0x0888, 1); + OUT_RING(chan, 1); + BEGIN_NV04(chan, NvSub2D, 0x02ac, 1); + OUT_RING(chan, 3); + BEGIN_NV04(chan, NvSub2D, 0x02a0, 1); + OUT_RING(chan, 0x55); + BEGIN_NV04(chan, NvSub2D, 0x08c0, 4); + OUT_RING(chan, 0); + OUT_RING(chan, 1); + OUT_RING(chan, 0); + OUT_RING(chan, 1); + BEGIN_NV04(chan, NvSub2D, 0x0580, 2); + OUT_RING(chan, 4); + OUT_RING(chan, format); + BEGIN_NV04(chan, NvSub2D, 0x02e8, 2); + OUT_RING(chan, 2); + OUT_RING(chan, 1); + BEGIN_NV04(chan, NvSub2D, 0x0804, 1); + OUT_RING(chan, format); + BEGIN_NV04(chan, NvSub2D, 0x0800, 1); + OUT_RING(chan, 1); + BEGIN_NV04(chan, NvSub2D, 0x0808, 3); + OUT_RING(chan, 0); + OUT_RING(chan, 0); + OUT_RING(chan, 1); + BEGIN_NV04(chan, NvSub2D, 0x081c, 1); + OUT_RING(chan, 1); + BEGIN_NV04(chan, NvSub2D, 0x0840, 4); + OUT_RING(chan, 0); + OUT_RING(chan, 1); + OUT_RING(chan, 0); + OUT_RING(chan, 1); + BEGIN_NV04(chan, NvSub2D, 0x0200, 2); + OUT_RING(chan, format); + OUT_RING(chan, 1); + BEGIN_NV04(chan, NvSub2D, 0x0214, 5); + OUT_RING(chan, info->fix.line_length); + OUT_RING(chan, info->var.xres_virtual); + OUT_RING(chan, info->var.yres_virtual); + OUT_RING(chan, upper_32_bits(fb->vma.offset)); + OUT_RING(chan, lower_32_bits(fb->vma.offset)); + BEGIN_NV04(chan, NvSub2D, 0x0230, 2); + OUT_RING(chan, format); + OUT_RING(chan, 1); + BEGIN_NV04(chan, NvSub2D, 0x0244, 5); + OUT_RING(chan, info->fix.line_length); + OUT_RING(chan, info->var.xres_virtual); + OUT_RING(chan, info->var.yres_virtual); + OUT_RING(chan, upper_32_bits(fb->vma.offset)); + OUT_RING(chan, lower_32_bits(fb->vma.offset)); + + return 0; +} + diff --git a/kernel/drivers/gpu/drm/nouveau/nv50_fence.c b/kernel/drivers/gpu/drm/nouveau/nv50_fence.c new file mode 100644 index 000000000..a82d9ea7c --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nv50_fence.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 +#include + +#include "nouveau_drm.h" +#include "nouveau_dma.h" +#include "nv10_fence.h" + +#include "nv50_display.h" + +static int +nv50_fence_context_new(struct nouveau_channel *chan) +{ + struct drm_device *dev = chan->drm->dev; + struct nv10_fence_priv *priv = chan->drm->fence; + struct nv10_fence_chan *fctx; + struct ttm_mem_reg *mem = &priv->bo->bo.mem; + u32 start = mem->start * PAGE_SIZE; + u32 limit = start + mem->size - 1; + int ret, i; + + fctx = chan->fence = kzalloc(sizeof(*fctx), GFP_KERNEL); + if (!fctx) + return -ENOMEM; + + nouveau_fence_context_new(chan, &fctx->base); + fctx->base.emit = nv10_fence_emit; + fctx->base.read = nv10_fence_read; + fctx->base.sync = nv17_fence_sync; + + ret = nvif_object_init(chan->object, NULL, NvSema, NV_DMA_IN_MEMORY, + &(struct nv_dma_v0) { + .target = NV_DMA_V0_TARGET_VRAM, + .access = NV_DMA_V0_ACCESS_RDWR, + .start = start, + .limit = limit, + }, sizeof(struct nv_dma_v0), + &fctx->sema); + + /* dma objects for display sync channel semaphore blocks */ + for (i = 0; !ret && i < dev->mode_config.num_crtc; i++) { + struct nouveau_bo *bo = nv50_display_crtc_sema(dev, i); + u32 start = bo->bo.mem.start * PAGE_SIZE; + u32 limit = start + bo->bo.mem.size - 1; + + ret = nvif_object_init(chan->object, NULL, NvEvoSema0 + i, + NV_DMA_IN_MEMORY, &(struct nv_dma_v0) { + .target = NV_DMA_V0_TARGET_VRAM, + .access = NV_DMA_V0_ACCESS_RDWR, + .start = start, + .limit = limit, + }, sizeof(struct nv_dma_v0), + &fctx->head[i]); + } + + if (ret) + nv10_fence_context_del(chan); + return ret; +} + +int +nv50_fence_create(struct nouveau_drm *drm) +{ + struct nv10_fence_priv *priv; + int ret = 0; + + priv = drm->fence = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base.dtor = nv10_fence_destroy; + priv->base.resume = nv17_fence_resume; + priv->base.context_new = nv50_fence_context_new; + priv->base.context_del = nv10_fence_context_del; + priv->base.contexts = 127; + priv->base.context_base = fence_context_alloc(priv->base.contexts); + spin_lock_init(&priv->lock); + + ret = nouveau_bo_new(drm->dev, 4096, 0x1000, TTM_PL_FLAG_VRAM, + 0, 0x0000, NULL, NULL, &priv->bo); + if (!ret) { + ret = nouveau_bo_pin(priv->bo, TTM_PL_FLAG_VRAM, false); + if (!ret) { + ret = nouveau_bo_map(priv->bo); + if (ret) + nouveau_bo_unpin(priv->bo); + } + if (ret) + nouveau_bo_ref(NULL, &priv->bo); + } + + if (ret) { + nv10_fence_destroy(drm); + return ret; + } + + nouveau_bo_wr32(priv->bo, 0x000, 0x00000000); + return ret; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nv84_fence.c b/kernel/drivers/gpu/drm/nouveau/nv84_fence.c new file mode 100644 index 000000000..a03db4368 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nv84_fence.c @@ -0,0 +1,273 @@ +/* + * 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 "nouveau_drm.h" +#include "nouveau_dma.h" +#include "nouveau_fence.h" + +#include "nv50_display.h" + +u64 +nv84_fence_crtc(struct nouveau_channel *chan, int crtc) +{ + struct nv84_fence_chan *fctx = chan->fence; + return fctx->dispc_vma[crtc].offset; +} + +static int +nv84_fence_emit32(struct nouveau_channel *chan, u64 virtual, u32 sequence) +{ + int ret = RING_SPACE(chan, 8); + if (ret == 0) { + BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 1); + OUT_RING (chan, chan->vram.handle); + BEGIN_NV04(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 5); + OUT_RING (chan, upper_32_bits(virtual)); + OUT_RING (chan, lower_32_bits(virtual)); + OUT_RING (chan, sequence); + OUT_RING (chan, NV84_SUBCHAN_SEMAPHORE_TRIGGER_WRITE_LONG); + OUT_RING (chan, 0x00000000); + FIRE_RING (chan); + } + return ret; +} + +static int +nv84_fence_sync32(struct nouveau_channel *chan, u64 virtual, u32 sequence) +{ + int ret = RING_SPACE(chan, 7); + if (ret == 0) { + BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 1); + OUT_RING (chan, chan->vram.handle); + BEGIN_NV04(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); + OUT_RING (chan, upper_32_bits(virtual)); + OUT_RING (chan, lower_32_bits(virtual)); + OUT_RING (chan, sequence); + OUT_RING (chan, NV84_SUBCHAN_SEMAPHORE_TRIGGER_ACQUIRE_GEQUAL); + FIRE_RING (chan); + } + return ret; +} + +static int +nv84_fence_emit(struct nouveau_fence *fence) +{ + struct nouveau_channel *chan = fence->channel; + struct nv84_fence_chan *fctx = chan->fence; + u64 addr = chan->chid * 16; + + if (fence->sysmem) + addr += fctx->vma_gart.offset; + else + addr += fctx->vma.offset; + + return fctx->base.emit32(chan, addr, fence->base.seqno); +} + +static int +nv84_fence_sync(struct nouveau_fence *fence, + struct nouveau_channel *prev, struct nouveau_channel *chan) +{ + struct nv84_fence_chan *fctx = chan->fence; + u64 addr = prev->chid * 16; + + if (fence->sysmem) + addr += fctx->vma_gart.offset; + else + addr += fctx->vma.offset; + + return fctx->base.sync32(chan, addr, fence->base.seqno); +} + +static u32 +nv84_fence_read(struct nouveau_channel *chan) +{ + struct nv84_fence_priv *priv = chan->drm->fence; + return nouveau_bo_rd32(priv->bo, chan->chid * 16/4); +} + +static void +nv84_fence_context_del(struct nouveau_channel *chan) +{ + struct drm_device *dev = chan->drm->dev; + struct nv84_fence_priv *priv = chan->drm->fence; + struct nv84_fence_chan *fctx = chan->fence; + int i; + + for (i = 0; i < dev->mode_config.num_crtc; i++) { + struct nouveau_bo *bo = nv50_display_crtc_sema(dev, i); + nouveau_bo_vma_del(bo, &fctx->dispc_vma[i]); + } + + nouveau_bo_wr32(priv->bo, chan->chid * 16 / 4, fctx->base.sequence); + nouveau_bo_vma_del(priv->bo, &fctx->vma_gart); + nouveau_bo_vma_del(priv->bo, &fctx->vma); + nouveau_fence_context_del(&fctx->base); + chan->fence = NULL; + nouveau_fence_context_free(&fctx->base); +} + +int +nv84_fence_context_new(struct nouveau_channel *chan) +{ + struct nouveau_cli *cli = (void *)nvif_client(&chan->device->base); + struct nv84_fence_priv *priv = chan->drm->fence; + struct nv84_fence_chan *fctx; + int ret, i; + + fctx = chan->fence = kzalloc(sizeof(*fctx), GFP_KERNEL); + if (!fctx) + return -ENOMEM; + + nouveau_fence_context_new(chan, &fctx->base); + fctx->base.emit = nv84_fence_emit; + fctx->base.sync = nv84_fence_sync; + fctx->base.read = nv84_fence_read; + fctx->base.emit32 = nv84_fence_emit32; + fctx->base.sync32 = nv84_fence_sync32; + fctx->base.sequence = nv84_fence_read(chan); + + ret = nouveau_bo_vma_add(priv->bo, cli->vm, &fctx->vma); + if (ret == 0) { + ret = nouveau_bo_vma_add(priv->bo_gart, cli->vm, + &fctx->vma_gart); + } + + /* map display semaphore buffers into channel's vm */ + for (i = 0; !ret && i < chan->drm->dev->mode_config.num_crtc; i++) { + struct nouveau_bo *bo = nv50_display_crtc_sema(chan->drm->dev, i); + ret = nouveau_bo_vma_add(bo, cli->vm, &fctx->dispc_vma[i]); + } + + if (ret) + nv84_fence_context_del(chan); + return ret; +} + +static bool +nv84_fence_suspend(struct nouveau_drm *drm) +{ + struct nv84_fence_priv *priv = drm->fence; + int i; + + priv->suspend = vmalloc(priv->base.contexts * sizeof(u32)); + if (priv->suspend) { + for (i = 0; i < priv->base.contexts; i++) + priv->suspend[i] = nouveau_bo_rd32(priv->bo, i*4); + } + + return priv->suspend != NULL; +} + +static void +nv84_fence_resume(struct nouveau_drm *drm) +{ + struct nv84_fence_priv *priv = drm->fence; + int i; + + if (priv->suspend) { + for (i = 0; i < priv->base.contexts; i++) + nouveau_bo_wr32(priv->bo, i*4, priv->suspend[i]); + vfree(priv->suspend); + priv->suspend = NULL; + } +} + +static void +nv84_fence_destroy(struct nouveau_drm *drm) +{ + struct nv84_fence_priv *priv = drm->fence; + nouveau_bo_unmap(priv->bo_gart); + if (priv->bo_gart) + nouveau_bo_unpin(priv->bo_gart); + nouveau_bo_ref(NULL, &priv->bo_gart); + nouveau_bo_unmap(priv->bo); + if (priv->bo) + nouveau_bo_unpin(priv->bo); + nouveau_bo_ref(NULL, &priv->bo); + drm->fence = NULL; + kfree(priv); +} + +int +nv84_fence_create(struct nouveau_drm *drm) +{ + struct nvkm_fifo *pfifo = nvxx_fifo(&drm->device); + struct nv84_fence_priv *priv; + u32 domain; + int ret; + + priv = drm->fence = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base.dtor = nv84_fence_destroy; + priv->base.suspend = nv84_fence_suspend; + priv->base.resume = nv84_fence_resume; + priv->base.context_new = nv84_fence_context_new; + priv->base.context_del = nv84_fence_context_del; + + priv->base.contexts = pfifo->max + 1; + priv->base.context_base = fence_context_alloc(priv->base.contexts); + priv->base.uevent = true; + + /* Use VRAM if there is any ; otherwise fallback to system memory */ + domain = drm->device.info.ram_size != 0 ? TTM_PL_FLAG_VRAM : + /* + * fences created in sysmem must be non-cached or we + * will lose CPU/GPU coherency! + */ + TTM_PL_FLAG_TT | TTM_PL_FLAG_UNCACHED; + ret = nouveau_bo_new(drm->dev, 16 * priv->base.contexts, 0, domain, 0, + 0, NULL, NULL, &priv->bo); + if (ret == 0) { + ret = nouveau_bo_pin(priv->bo, domain, false); + if (ret == 0) { + ret = nouveau_bo_map(priv->bo); + if (ret) + nouveau_bo_unpin(priv->bo); + } + if (ret) + nouveau_bo_ref(NULL, &priv->bo); + } + + if (ret == 0) + ret = nouveau_bo_new(drm->dev, 16 * priv->base.contexts, 0, + TTM_PL_FLAG_TT | TTM_PL_FLAG_UNCACHED, 0, + 0, NULL, NULL, &priv->bo_gart); + if (ret == 0) { + ret = nouveau_bo_pin(priv->bo_gart, TTM_PL_FLAG_TT, false); + if (ret == 0) { + ret = nouveau_bo_map(priv->bo_gart); + if (ret) + nouveau_bo_unpin(priv->bo_gart); + } + if (ret) + nouveau_bo_ref(NULL, &priv->bo_gart); + } + + if (ret) + nv84_fence_destroy(drm); + return ret; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvc0_fbcon.c b/kernel/drivers/gpu/drm/nouveau/nvc0_fbcon.c new file mode 100644 index 000000000..61246677e --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvc0_fbcon.c @@ -0,0 +1,261 @@ +/* + * 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 "nouveau_drm.h" +#include "nouveau_dma.h" +#include "nouveau_fbcon.h" + +int +nvc0_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect) +{ + struct nouveau_fbdev *nfbdev = info->par; + struct nouveau_drm *drm = nouveau_drm(nfbdev->dev); + struct nouveau_channel *chan = drm->channel; + int ret; + + ret = RING_SPACE(chan, rect->rop == ROP_COPY ? 7 : 11); + if (ret) + return ret; + + if (rect->rop != ROP_COPY) { + BEGIN_NVC0(chan, NvSub2D, 0x02ac, 1); + OUT_RING (chan, 1); + } + BEGIN_NVC0(chan, NvSub2D, 0x0588, 1); + if (info->fix.visual == FB_VISUAL_TRUECOLOR || + info->fix.visual == FB_VISUAL_DIRECTCOLOR) + OUT_RING (chan, ((uint32_t *)info->pseudo_palette)[rect->color]); + else + OUT_RING (chan, rect->color); + BEGIN_NVC0(chan, NvSub2D, 0x0600, 4); + OUT_RING (chan, rect->dx); + OUT_RING (chan, rect->dy); + OUT_RING (chan, rect->dx + rect->width); + OUT_RING (chan, rect->dy + rect->height); + if (rect->rop != ROP_COPY) { + BEGIN_NVC0(chan, NvSub2D, 0x02ac, 1); + OUT_RING (chan, 3); + } + FIRE_RING(chan); + return 0; +} + +int +nvc0_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region) +{ + struct nouveau_fbdev *nfbdev = info->par; + struct nouveau_drm *drm = nouveau_drm(nfbdev->dev); + struct nouveau_channel *chan = drm->channel; + int ret; + + ret = RING_SPACE(chan, 12); + if (ret) + return ret; + + BEGIN_NVC0(chan, NvSub2D, 0x0110, 1); + OUT_RING (chan, 0); + BEGIN_NVC0(chan, NvSub2D, 0x08b0, 4); + OUT_RING (chan, region->dx); + OUT_RING (chan, region->dy); + OUT_RING (chan, region->width); + OUT_RING (chan, region->height); + BEGIN_NVC0(chan, NvSub2D, 0x08d0, 4); + OUT_RING (chan, 0); + OUT_RING (chan, region->sx); + OUT_RING (chan, 0); + OUT_RING (chan, region->sy); + FIRE_RING(chan); + return 0; +} + +int +nvc0_fbcon_imageblit(struct fb_info *info, const struct fb_image *image) +{ + struct nouveau_fbdev *nfbdev = info->par; + struct nouveau_drm *drm = nouveau_drm(nfbdev->dev); + struct nouveau_channel *chan = drm->channel; + uint32_t width, dwords, *data = (uint32_t *)image->data; + uint32_t mask = ~(~0 >> (32 - info->var.bits_per_pixel)); + uint32_t *palette = info->pseudo_palette; + int ret; + + if (image->depth != 1) + return -ENODEV; + + ret = RING_SPACE(chan, 11); + if (ret) + return ret; + + width = ALIGN(image->width, 32); + dwords = (width * image->height) >> 5; + + BEGIN_NVC0(chan, NvSub2D, 0x0814, 2); + if (info->fix.visual == FB_VISUAL_TRUECOLOR || + info->fix.visual == FB_VISUAL_DIRECTCOLOR) { + OUT_RING (chan, palette[image->bg_color] | mask); + OUT_RING (chan, palette[image->fg_color] | mask); + } else { + OUT_RING (chan, image->bg_color); + OUT_RING (chan, image->fg_color); + } + BEGIN_NVC0(chan, NvSub2D, 0x0838, 2); + OUT_RING (chan, image->width); + OUT_RING (chan, image->height); + BEGIN_NVC0(chan, NvSub2D, 0x0850, 4); + OUT_RING (chan, 0); + OUT_RING (chan, image->dx); + OUT_RING (chan, 0); + OUT_RING (chan, image->dy); + + while (dwords) { + int push = dwords > 2047 ? 2047 : dwords; + + ret = RING_SPACE(chan, push + 1); + if (ret) + return ret; + + dwords -= push; + + BEGIN_NIC0(chan, NvSub2D, 0x0860, push); + OUT_RINGp(chan, data, push); + data += push; + } + + FIRE_RING(chan); + return 0; +} + +int +nvc0_fbcon_accel_init(struct fb_info *info) +{ + struct nouveau_fbdev *nfbdev = info->par; + struct drm_device *dev = nfbdev->dev; + struct nouveau_framebuffer *fb = &nfbdev->nouveau_fb; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_channel *chan = drm->channel; + int ret, format; + + ret = nvif_object_init(chan->object, NULL, 0x902d, 0x902d, NULL, 0, + &nfbdev->twod); + if (ret) + return ret; + + switch (info->var.bits_per_pixel) { + case 8: + format = 0xf3; + break; + case 15: + format = 0xf8; + break; + case 16: + format = 0xe8; + break; + case 32: + switch (info->var.transp.length) { + case 0: /* depth 24 */ + case 8: /* depth 32, just use 24.. */ + format = 0xe6; + break; + case 2: /* depth 30 */ + format = 0xd1; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + ret = RING_SPACE(chan, 60); + if (ret) { + WARN_ON(1); + nouveau_fbcon_gpu_lockup(info); + return ret; + } + + BEGIN_NVC0(chan, NvSub2D, 0x0000, 1); + OUT_RING (chan, nfbdev->twod.handle); + BEGIN_NVC0(chan, NvSub2D, 0x0290, 1); + OUT_RING (chan, 0); + BEGIN_NVC0(chan, NvSub2D, 0x0888, 1); + OUT_RING (chan, 1); + BEGIN_NVC0(chan, NvSub2D, 0x02ac, 1); + OUT_RING (chan, 3); + BEGIN_NVC0(chan, NvSub2D, 0x02a0, 1); + OUT_RING (chan, 0x55); + BEGIN_NVC0(chan, NvSub2D, 0x08c0, 4); + OUT_RING (chan, 0); + OUT_RING (chan, 1); + OUT_RING (chan, 0); + OUT_RING (chan, 1); + BEGIN_NVC0(chan, NvSub2D, 0x0580, 2); + OUT_RING (chan, 4); + OUT_RING (chan, format); + BEGIN_NVC0(chan, NvSub2D, 0x02e8, 2); + OUT_RING (chan, 2); + OUT_RING (chan, 1); + + BEGIN_NVC0(chan, NvSub2D, 0x0804, 1); + OUT_RING (chan, format); + BEGIN_NVC0(chan, NvSub2D, 0x0800, 1); + OUT_RING (chan, 1); + BEGIN_NVC0(chan, NvSub2D, 0x0808, 3); + OUT_RING (chan, 0); + OUT_RING (chan, 0); + OUT_RING (chan, 1); + BEGIN_NVC0(chan, NvSub2D, 0x081c, 1); + OUT_RING (chan, 1); + BEGIN_NVC0(chan, NvSub2D, 0x0840, 4); + OUT_RING (chan, 0); + OUT_RING (chan, 1); + OUT_RING (chan, 0); + OUT_RING (chan, 1); + BEGIN_NVC0(chan, NvSub2D, 0x0200, 10); + OUT_RING (chan, format); + OUT_RING (chan, 1); + OUT_RING (chan, 0); + OUT_RING (chan, 1); + OUT_RING (chan, 0); + OUT_RING (chan, info->fix.line_length); + OUT_RING (chan, info->var.xres_virtual); + OUT_RING (chan, info->var.yres_virtual); + OUT_RING (chan, upper_32_bits(fb->vma.offset)); + OUT_RING (chan, lower_32_bits(fb->vma.offset)); + BEGIN_NVC0(chan, NvSub2D, 0x0230, 10); + OUT_RING (chan, format); + OUT_RING (chan, 1); + OUT_RING (chan, 0); + OUT_RING (chan, 1); + OUT_RING (chan, 0); + OUT_RING (chan, info->fix.line_length); + OUT_RING (chan, info->var.xres_virtual); + OUT_RING (chan, info->var.yres_virtual); + OUT_RING (chan, upper_32_bits(fb->vma.offset)); + OUT_RING (chan, lower_32_bits(fb->vma.offset)); + FIRE_RING (chan); + + return 0; +} + diff --git a/kernel/drivers/gpu/drm/nouveau/nvc0_fence.c b/kernel/drivers/gpu/drm/nouveau/nvc0_fence.c new file mode 100644 index 000000000..becf19abd --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvc0_fence.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 "nouveau_drm.h" +#include "nouveau_dma.h" +#include "nouveau_fence.h" + +#include "nv50_display.h" + +static int +nvc0_fence_emit32(struct nouveau_channel *chan, u64 virtual, u32 sequence) +{ + int ret = RING_SPACE(chan, 6); + if (ret == 0) { + BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 5); + OUT_RING (chan, upper_32_bits(virtual)); + OUT_RING (chan, lower_32_bits(virtual)); + OUT_RING (chan, sequence); + OUT_RING (chan, NV84_SUBCHAN_SEMAPHORE_TRIGGER_WRITE_LONG); + OUT_RING (chan, 0x00000000); + FIRE_RING (chan); + } + return ret; +} + +static int +nvc0_fence_sync32(struct nouveau_channel *chan, u64 virtual, u32 sequence) +{ + int ret = RING_SPACE(chan, 5); + if (ret == 0) { + BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); + OUT_RING (chan, upper_32_bits(virtual)); + OUT_RING (chan, lower_32_bits(virtual)); + OUT_RING (chan, sequence); + OUT_RING (chan, NV84_SUBCHAN_SEMAPHORE_TRIGGER_ACQUIRE_GEQUAL | + NVC0_SUBCHAN_SEMAPHORE_TRIGGER_YIELD); + FIRE_RING (chan); + } + return ret; +} + +static int +nvc0_fence_context_new(struct nouveau_channel *chan) +{ + int ret = nv84_fence_context_new(chan); + if (ret == 0) { + struct nv84_fence_chan *fctx = chan->fence; + fctx->base.emit32 = nvc0_fence_emit32; + fctx->base.sync32 = nvc0_fence_sync32; + } + return ret; +} + +int +nvc0_fence_create(struct nouveau_drm *drm) +{ + int ret = nv84_fence_create(drm); + if (ret == 0) { + struct nv84_fence_priv *priv = drm->fence; + priv->base.context_new = nvc0_fence_context_new; + } + return ret; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvif/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvif/Kbuild new file mode 100644 index 000000000..ff8ed3a04 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvif/Kbuild @@ -0,0 +1,4 @@ +nvif-y := nvif/object.o +nvif-y += nvif/client.o +nvif-y += nvif/device.o +nvif-y += nvif/notify.o diff --git a/kernel/drivers/gpu/drm/nouveau/nvif/client.c b/kernel/drivers/gpu/drm/nouveau/nvif/client.c new file mode 100644 index 000000000..80b968442 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvif/client.c @@ -0,0 +1,130 @@ +/* + * 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 + +int +nvif_client_ioctl(struct nvif_client *client, void *data, u32 size) +{ + return client->driver->ioctl(client->base.priv, client->super, data, size, NULL); +} + +int +nvif_client_suspend(struct nvif_client *client) +{ + return client->driver->suspend(client->base.priv); +} + +int +nvif_client_resume(struct nvif_client *client) +{ + return client->driver->resume(client->base.priv); +} + +void +nvif_client_fini(struct nvif_client *client) +{ + if (client->driver) { + client->driver->fini(client->base.priv); + client->driver = NULL; + client->base.parent = NULL; + nvif_object_fini(&client->base); + } +} + +const struct nvif_driver * +nvif_drivers[] = { +#ifdef __KERNEL__ + &nvif_driver_nvkm, +#else + &nvif_driver_drm, + &nvif_driver_lib, + &nvif_driver_null, +#endif + NULL +}; + +int +nvif_client_init(void (*dtor)(struct nvif_client *), const char *driver, + const char *name, u64 device, const char *cfg, const char *dbg, + struct nvif_client *client) +{ + int ret, i; + + ret = nvif_object_init(NULL, (void*)dtor, 0, 0, NULL, 0, &client->base); + if (ret) + return ret; + + client->base.parent = &client->base; + client->base.handle = ~0; + client->object = &client->base; + client->super = true; + + for (i = 0, ret = -EINVAL; (client->driver = nvif_drivers[i]); i++) { + if (!driver || !strcmp(client->driver->name, driver)) { + ret = client->driver->init(name, device, cfg, dbg, + &client->base.priv); + if (!ret || driver) + break; + } + } + + if (ret) + nvif_client_fini(client); + return ret; +} + +static void +nvif_client_del(struct nvif_client *client) +{ + nvif_client_fini(client); + kfree(client); +} + +int +nvif_client_new(const char *driver, const char *name, u64 device, + const char *cfg, const char *dbg, + struct nvif_client **pclient) +{ + struct nvif_client *client = kzalloc(sizeof(*client), GFP_KERNEL); + if (client) { + int ret = nvif_client_init(nvif_client_del, driver, name, + device, cfg, dbg, client); + if (ret) { + kfree(client); + client = NULL; + } + *pclient = client; + return ret; + } + return -ENOMEM; +} + +void +nvif_client_ref(struct nvif_client *client, struct nvif_client **pclient) +{ + nvif_object_ref(&client->base, (struct nvif_object **)pclient); +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvif/device.c b/kernel/drivers/gpu/drm/nouveau/nvif/device.c new file mode 100644 index 000000000..6f72244c5 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvif/device.c @@ -0,0 +1,78 @@ +/* + * 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 + +void +nvif_device_fini(struct nvif_device *device) +{ + nvif_object_fini(&device->base); +} + +int +nvif_device_init(struct nvif_object *parent, void (*dtor)(struct nvif_device *), + u32 handle, u32 oclass, void *data, u32 size, + struct nvif_device *device) +{ + int ret = nvif_object_init(parent, (void *)dtor, handle, oclass, + data, size, &device->base); + if (ret == 0) { + device->object = &device->base; + device->info.version = 0; + ret = nvif_object_mthd(&device->base, NV_DEVICE_V0_INFO, + &device->info, sizeof(device->info)); + } + return ret; +} + +static void +nvif_device_del(struct nvif_device *device) +{ + nvif_device_fini(device); + kfree(device); +} + +int +nvif_device_new(struct nvif_object *parent, u32 handle, u32 oclass, + void *data, u32 size, struct nvif_device **pdevice) +{ + struct nvif_device *device = kzalloc(sizeof(*device), GFP_KERNEL); + if (device) { + int ret = nvif_device_init(parent, nvif_device_del, handle, + oclass, data, size, device); + if (ret) { + kfree(device); + device = NULL; + } + *pdevice = device; + return ret; + } + return -ENOMEM; +} + +void +nvif_device_ref(struct nvif_device *device, struct nvif_device **pdevice) +{ + nvif_object_ref(&device->base, (struct nvif_object **)pdevice); +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvif/notify.c b/kernel/drivers/gpu/drm/nouveau/nvif/notify.c new file mode 100644 index 000000000..8e3474870 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvif/notify.c @@ -0,0 +1,248 @@ +/* + * 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 inline int +nvif_notify_put_(struct nvif_notify *notify) +{ + struct nvif_object *object = notify->object; + struct { + struct nvif_ioctl_v0 ioctl; + struct nvif_ioctl_ntfy_put_v0 ntfy; + } args = { + .ioctl.type = NVIF_IOCTL_V0_NTFY_PUT, + .ntfy.index = notify->index, + }; + + if (atomic_inc_return(¬ify->putcnt) != 1) + return 0; + + return nvif_object_ioctl(object, &args, sizeof(args), NULL); +} + +int +nvif_notify_put(struct nvif_notify *notify) +{ + if (likely(notify->object) && + test_and_clear_bit(NVIF_NOTIFY_USER, ¬ify->flags)) { + int ret = nvif_notify_put_(notify); + if (test_bit(NVIF_NOTIFY_WORK, ¬ify->flags)) + flush_work(¬ify->work); + return ret; + } + return 0; +} + +static inline int +nvif_notify_get_(struct nvif_notify *notify) +{ + struct nvif_object *object = notify->object; + struct { + struct nvif_ioctl_v0 ioctl; + struct nvif_ioctl_ntfy_get_v0 ntfy; + } args = { + .ioctl.type = NVIF_IOCTL_V0_NTFY_GET, + .ntfy.index = notify->index, + }; + + if (atomic_dec_return(¬ify->putcnt) != 0) + return 0; + + return nvif_object_ioctl(object, &args, sizeof(args), NULL); +} + +int +nvif_notify_get(struct nvif_notify *notify) +{ + if (likely(notify->object) && + !test_and_set_bit(NVIF_NOTIFY_USER, ¬ify->flags)) + return nvif_notify_get_(notify); + return 0; +} + +static inline int +nvif_notify_func(struct nvif_notify *notify, bool keep) +{ + int ret = notify->func(notify); + if (ret == NVIF_NOTIFY_KEEP || + !test_and_clear_bit(NVIF_NOTIFY_USER, ¬ify->flags)) { + if (!keep) + atomic_dec(¬ify->putcnt); + else + nvif_notify_get_(notify); + } + return ret; +} + +static void +nvif_notify_work(struct work_struct *work) +{ + struct nvif_notify *notify = container_of(work, typeof(*notify), work); + nvif_notify_func(notify, true); +} + +int +nvif_notify(const void *header, u32 length, const void *data, u32 size) +{ + struct nvif_notify *notify = NULL; + const union { + struct nvif_notify_rep_v0 v0; + } *args = header; + int ret = NVIF_NOTIFY_DROP; + + if (length == sizeof(args->v0) && args->v0.version == 0) { + if (WARN_ON(args->v0.route)) + return NVIF_NOTIFY_DROP; + notify = (void *)(unsigned long)args->v0.token; + } + + if (!WARN_ON(notify == NULL)) { + struct nvif_client *client = nvif_client(notify->object); + if (!WARN_ON(notify->size != size)) { + atomic_inc(¬ify->putcnt); + if (test_bit(NVIF_NOTIFY_WORK, ¬ify->flags)) { + memcpy((void *)notify->data, data, size); + schedule_work(¬ify->work); + return NVIF_NOTIFY_DROP; + } + notify->data = data; + ret = nvif_notify_func(notify, client->driver->keep); + notify->data = NULL; + } + } + + return ret; +} + +int +nvif_notify_fini(struct nvif_notify *notify) +{ + struct nvif_object *object = notify->object; + struct { + struct nvif_ioctl_v0 ioctl; + struct nvif_ioctl_ntfy_del_v0 ntfy; + } args = { + .ioctl.type = NVIF_IOCTL_V0_NTFY_DEL, + .ntfy.index = notify->index, + }; + int ret = nvif_notify_put(notify); + if (ret >= 0 && object) { + ret = nvif_object_ioctl(object, &args, sizeof(args), NULL); + if (ret == 0) { + nvif_object_ref(NULL, ¬ify->object); + kfree((void *)notify->data); + } + } + return ret; +} + +int +nvif_notify_init(struct nvif_object *object, void (*dtor)(struct nvif_notify *), + int (*func)(struct nvif_notify *), bool work, u8 event, + void *data, u32 size, u32 reply, struct nvif_notify *notify) +{ + struct { + struct nvif_ioctl_v0 ioctl; + struct nvif_ioctl_ntfy_new_v0 ntfy; + struct nvif_notify_req_v0 req; + } *args; + int ret = -ENOMEM; + + notify->object = NULL; + nvif_object_ref(object, ¬ify->object); + notify->flags = 0; + atomic_set(¬ify->putcnt, 1); + notify->dtor = dtor; + notify->func = func; + notify->data = NULL; + notify->size = reply; + if (work) { + INIT_WORK(¬ify->work, nvif_notify_work); + set_bit(NVIF_NOTIFY_WORK, ¬ify->flags); + notify->data = kmalloc(notify->size, GFP_KERNEL); + if (!notify->data) + goto done; + } + + if (!(args = kmalloc(sizeof(*args) + size, GFP_KERNEL))) + goto done; + args->ioctl.version = 0; + args->ioctl.type = NVIF_IOCTL_V0_NTFY_NEW; + args->ntfy.version = 0; + args->ntfy.event = event; + args->req.version = 0; + args->req.reply = notify->size; + args->req.route = 0; + args->req.token = (unsigned long)(void *)notify; + + memcpy(args->req.data, data, size); + ret = nvif_object_ioctl(object, args, sizeof(*args) + size, NULL); + notify->index = args->ntfy.index; + kfree(args); +done: + if (ret) + nvif_notify_fini(notify); + return ret; +} + +static void +nvif_notify_del(struct nvif_notify *notify) +{ + nvif_notify_fini(notify); + kfree(notify); +} + +void +nvif_notify_ref(struct nvif_notify *notify, struct nvif_notify **pnotify) +{ + BUG_ON(notify != NULL); + if (*pnotify) + (*pnotify)->dtor(*pnotify); + *pnotify = notify; +} + +int +nvif_notify_new(struct nvif_object *object, int (*func)(struct nvif_notify *), + bool work, u8 type, void *data, u32 size, u32 reply, + struct nvif_notify **pnotify) +{ + struct nvif_notify *notify = kzalloc(sizeof(*notify), GFP_KERNEL); + if (notify) { + int ret = nvif_notify_init(object, nvif_notify_del, func, work, + type, data, size, reply, notify); + if (ret) { + kfree(notify); + notify = NULL; + } + *pnotify = notify; + return ret; + } + return -ENOMEM; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvif/object.c b/kernel/drivers/gpu/drm/nouveau/nvif/object.c new file mode 100644 index 000000000..3ab4e2f8c --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvif/object.c @@ -0,0 +1,304 @@ +/* + * 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 + +int +nvif_object_ioctl(struct nvif_object *object, void *data, u32 size, void **hack) +{ + struct nvif_client *client = nvif_client(object); + union { + struct nvif_ioctl_v0 v0; + } *args = data; + + if (size >= sizeof(*args) && args->v0.version == 0) { + args->v0.owner = NVIF_IOCTL_V0_OWNER_ANY; + args->v0.path_nr = 0; + while (args->v0.path_nr < ARRAY_SIZE(args->v0.path)) { + args->v0.path[args->v0.path_nr++] = object->handle; + if (object->parent == object) + break; + object = object->parent; + } + } else + return -ENOSYS; + + return client->driver->ioctl(client->base.priv, client->super, data, size, hack); +} + +int +nvif_object_sclass(struct nvif_object *object, u32 *oclass, int count) +{ + struct { + struct nvif_ioctl_v0 ioctl; + struct nvif_ioctl_sclass_v0 sclass; + } *args; + u32 size = count * sizeof(args->sclass.oclass[0]); + int ret; + + if (!(args = kmalloc(sizeof(*args) + size, GFP_KERNEL))) + return -ENOMEM; + args->ioctl.version = 0; + args->ioctl.type = NVIF_IOCTL_V0_SCLASS; + args->sclass.version = 0; + args->sclass.count = count; + + memcpy(args->sclass.oclass, oclass, size); + ret = nvif_object_ioctl(object, args, sizeof(*args) + size, NULL); + ret = ret ? ret : args->sclass.count; + memcpy(oclass, args->sclass.oclass, size); + kfree(args); + return ret; +} + +u32 +nvif_object_rd(struct nvif_object *object, int size, u64 addr) +{ + struct { + struct nvif_ioctl_v0 ioctl; + struct nvif_ioctl_rd_v0 rd; + } args = { + .ioctl.type = NVIF_IOCTL_V0_RD, + .rd.size = size, + .rd.addr = addr, + }; + int ret = nvif_object_ioctl(object, &args, sizeof(args), NULL); + if (ret) { + /*XXX: warn? */ + return 0; + } + return args.rd.data; +} + +void +nvif_object_wr(struct nvif_object *object, int size, u64 addr, u32 data) +{ + struct { + struct nvif_ioctl_v0 ioctl; + struct nvif_ioctl_wr_v0 wr; + } args = { + .ioctl.type = NVIF_IOCTL_V0_WR, + .wr.size = size, + .wr.addr = addr, + .wr.data = data, + }; + int ret = nvif_object_ioctl(object, &args, sizeof(args), NULL); + if (ret) { + /*XXX: warn? */ + } +} + +int +nvif_object_mthd(struct nvif_object *object, u32 mthd, void *data, u32 size) +{ + struct { + struct nvif_ioctl_v0 ioctl; + struct nvif_ioctl_mthd_v0 mthd; + } *args; + u8 stack[128]; + int ret; + + if (sizeof(*args) + size > sizeof(stack)) { + if (!(args = kmalloc(sizeof(*args) + size, GFP_KERNEL))) + return -ENOMEM; + } else { + args = (void *)stack; + } + args->ioctl.version = 0; + args->ioctl.type = NVIF_IOCTL_V0_MTHD; + args->mthd.version = 0; + args->mthd.method = mthd; + + memcpy(args->mthd.data, data, size); + ret = nvif_object_ioctl(object, args, sizeof(*args) + size, NULL); + memcpy(data, args->mthd.data, size); + if (args != (void *)stack) + kfree(args); + return ret; +} + +void +nvif_object_unmap(struct nvif_object *object) +{ + if (object->map.size) { + struct nvif_client *client = nvif_client(object); + struct { + struct nvif_ioctl_v0 ioctl; + struct nvif_ioctl_unmap unmap; + } args = { + .ioctl.type = NVIF_IOCTL_V0_UNMAP, + }; + + if (object->map.ptr) { + client->driver->unmap(client, object->map.ptr, + object->map.size); + object->map.ptr = NULL; + } + + nvif_object_ioctl(object, &args, sizeof(args), NULL); + object->map.size = 0; + } +} + +int +nvif_object_map(struct nvif_object *object) +{ + struct nvif_client *client = nvif_client(object); + struct { + struct nvif_ioctl_v0 ioctl; + struct nvif_ioctl_map_v0 map; + } args = { + .ioctl.type = NVIF_IOCTL_V0_MAP, + }; + int ret = nvif_object_ioctl(object, &args, sizeof(args), NULL); + if (ret == 0) { + object->map.size = args.map.length; + object->map.ptr = client->driver->map(client, args.map.handle, + object->map.size); + if (ret = -ENOMEM, object->map.ptr) + return 0; + nvif_object_unmap(object); + } + return ret; +} + +struct ctor { + struct nvif_ioctl_v0 ioctl; + struct nvif_ioctl_new_v0 new; +}; + +void +nvif_object_fini(struct nvif_object *object) +{ + struct ctor *ctor = container_of(object->data, typeof(*ctor), new.data); + if (object->parent) { + struct { + struct nvif_ioctl_v0 ioctl; + struct nvif_ioctl_del del; + } args = { + .ioctl.type = NVIF_IOCTL_V0_DEL, + }; + + nvif_object_unmap(object); + nvif_object_ioctl(object, &args, sizeof(args), NULL); + if (object->data) { + object->size = 0; + object->data = NULL; + kfree(ctor); + } + nvif_object_ref(NULL, &object->parent); + } +} + +int +nvif_object_init(struct nvif_object *parent, void (*dtor)(struct nvif_object *), + u32 handle, u32 oclass, void *data, u32 size, + struct nvif_object *object) +{ + struct ctor *ctor; + int ret = 0; + + object->parent = NULL; + object->object = object; + nvif_object_ref(parent, &object->parent); + kref_init(&object->refcount); + object->handle = handle; + object->oclass = oclass; + object->data = NULL; + object->size = 0; + object->dtor = dtor; + object->map.ptr = NULL; + object->map.size = 0; + + if (object->parent) { + if (!(ctor = kmalloc(sizeof(*ctor) + size, GFP_KERNEL))) { + nvif_object_fini(object); + return -ENOMEM; + } + object->data = ctor->new.data; + object->size = size; + memcpy(object->data, data, size); + + ctor->ioctl.version = 0; + ctor->ioctl.type = NVIF_IOCTL_V0_NEW; + ctor->new.version = 0; + ctor->new.route = NVIF_IOCTL_V0_ROUTE_NVIF; + ctor->new.token = (unsigned long)(void *)object; + ctor->new.handle = handle; + ctor->new.oclass = oclass; + + ret = nvif_object_ioctl(parent, ctor, sizeof(*ctor) + + object->size, &object->priv); + } + + if (ret) + nvif_object_fini(object); + return ret; +} + +static void +nvif_object_del(struct nvif_object *object) +{ + nvif_object_fini(object); + kfree(object); +} + +int +nvif_object_new(struct nvif_object *parent, u32 handle, u32 oclass, + void *data, u32 size, struct nvif_object **pobject) +{ + struct nvif_object *object = kzalloc(sizeof(*object), GFP_KERNEL); + if (object) { + int ret = nvif_object_init(parent, nvif_object_del, handle, + oclass, data, size, object); + if (ret) { + kfree(object); + object = NULL; + } + *pobject = object; + return ret; + } + return -ENOMEM; +} + +static void +nvif_object_put(struct kref *kref) +{ + struct nvif_object *object = + container_of(kref, typeof(*object), refcount); + object->dtor(object); +} + +void +nvif_object_ref(struct nvif_object *object, struct nvif_object **pobject) +{ + if (object) + kref_get(&object->refcount); + if (*pobject) + kref_put(&(*pobject)->refcount, nvif_object_put); + *pobject = object; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/Kbuild new file mode 100644 index 000000000..2832147b6 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/Kbuild @@ -0,0 +1,3 @@ +include $(src)/nvkm/core/Kbuild +include $(src)/nvkm/subdev/Kbuild +include $(src)/nvkm/engine/Kbuild diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/core/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/core/Kbuild new file mode 100644 index 000000000..a2bdb2069 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/core/Kbuild @@ -0,0 +1,17 @@ +nvkm-y := nvkm/core/client.o +nvkm-y += nvkm/core/engctx.o +nvkm-y += nvkm/core/engine.o +nvkm-y += nvkm/core/enum.o +nvkm-y += nvkm/core/event.o +nvkm-y += nvkm/core/gpuobj.o +nvkm-y += nvkm/core/handle.o +nvkm-y += nvkm/core/ioctl.o +nvkm-y += nvkm/core/mm.o +nvkm-y += nvkm/core/namedb.o +nvkm-y += nvkm/core/notify.o +nvkm-y += nvkm/core/object.o +nvkm-y += nvkm/core/option.o +nvkm-y += nvkm/core/parent.o +nvkm-y += nvkm/core/printk.o +nvkm-y += nvkm/core/ramht.o +nvkm-y += nvkm/core/subdev.o diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/core/client.c b/kernel/drivers/gpu/drm/nouveau/nvkm/core/client.c new file mode 100644 index 000000000..878a82f8f --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/core/client.c @@ -0,0 +1,266 @@ +/* + * 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 + +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 = nvkm_client(object); + struct nvkm_client_notify *notify; + union { + struct nvif_notify_req_v0 v0; + } *req = data; + u8 index, reply; + int ret; + + 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; + + nv_ioctl(client, "notify new size %d\n", size); + if (nvif_unpack(req->v0, 0, 0, true)) { + nv_ioctl(client, "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 int +nvkm_client_mthd_devlist(struct nvkm_object *object, void *data, u32 size) +{ + union { + struct nv_client_devlist_v0 v0; + } *args = data; + int ret; + + nv_ioctl(object, "client devlist size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, true)) { + nv_ioctl(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) +{ + switch (mthd) { + case NV_CLIENT_DEVLIST: + return nvkm_client_mthd_devlist(object, data, size); + default: + break; + } + return -EINVAL; +} + +static void +nvkm_client_dtor(struct nvkm_object *object) +{ + struct nvkm_client *client = (void *)object; + int i; + for (i = 0; i < ARRAY_SIZE(client->notify); i++) + nvkm_client_notify_del(client, i); + nvkm_object_ref(NULL, &client->device); + nvkm_handle_destroy(client->root); + nvkm_namedb_destroy(&client->namedb); +} + +static struct nvkm_oclass +nvkm_client_oclass = { + .ofuncs = &(struct nvkm_ofuncs) { + .dtor = nvkm_client_dtor, + .mthd = nvkm_client_mthd, + }, +}; + +int +nvkm_client_create_(const char *name, u64 devname, const char *cfg, + const char *dbg, int length, void **pobject) +{ + struct nvkm_object *device; + struct nvkm_client *client; + int ret; + + device = (void *)nvkm_device_find(devname); + if (!device) + return -ENODEV; + + ret = nvkm_namedb_create_(NULL, NULL, &nvkm_client_oclass, + NV_CLIENT_CLASS, NULL, + (1ULL << NVDEV_ENGINE_DEVICE), + length, pobject); + client = *pobject; + if (ret) + return ret; + + ret = nvkm_handle_create(nv_object(client), ~0, ~0, nv_object(client), + &client->root); + if (ret) + return ret; + + /* prevent init/fini being called, os in in charge of this */ + atomic_set(&nv_object(client)->usecount, 2); + + nvkm_object_ref(device, &client->device); + snprintf(client->name, sizeof(client->name), "%s", name); + client->debug = nvkm_dbgopt(dbg, "CLIENT"); + return 0; +} + +int +nvkm_client_init(struct nvkm_client *client) +{ + int ret; + nv_debug(client, "init running\n"); + ret = nvkm_handle_init(client->root); + nv_debug(client, "init completed with %d\n", ret); + return ret; +} + +int +nvkm_client_fini(struct nvkm_client *client, bool suspend) +{ + const char *name[2] = { "fini", "suspend" }; + int ret, i; + nv_debug(client, "%s running\n", name[suspend]); + nv_debug(client, "%s notify\n", name[suspend]); + for (i = 0; i < ARRAY_SIZE(client->notify); i++) + nvkm_client_notify_put(client, i); + nv_debug(client, "%s object\n", name[suspend]); + ret = nvkm_handle_fini(client->root, suspend); + nv_debug(client, "%s completed with %d\n", name[suspend], ret); + return ret; +} + +const char * +nvkm_client_name(void *obj) +{ + const char *client_name = "unknown"; + struct nvkm_client *client = nvkm_client(obj); + if (client) + client_name = client->name; + return client_name; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/core/engctx.c b/kernel/drivers/gpu/drm/nouveau/nvkm/core/engctx.c new file mode 100644 index 000000000..fb2acbca7 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/core/engctx.c @@ -0,0 +1,239 @@ +/* + * 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 inline int +nvkm_engctx_exists(struct nvkm_object *parent, + struct nvkm_engine *engine, void **pobject) +{ + struct nvkm_engctx *engctx; + struct nvkm_object *parctx; + + list_for_each_entry(engctx, &engine->contexts, head) { + parctx = nv_pclass(nv_object(engctx), NV_PARENT_CLASS); + if (parctx == parent) { + atomic_inc(&nv_object(engctx)->refcount); + *pobject = engctx; + return 1; + } + } + + return 0; +} + +int +nvkm_engctx_create_(struct nvkm_object *parent, struct nvkm_object *engobj, + struct nvkm_oclass *oclass, struct nvkm_object *pargpu, + u32 size, u32 align, u32 flags, int length, void **pobject) +{ + struct nvkm_client *client = nvkm_client(parent); + struct nvkm_engine *engine = nv_engine(engobj); + struct nvkm_object *engctx; + unsigned long save; + int ret; + + /* check if this engine already has a context for the parent object, + * and reference it instead of creating a new one + */ + spin_lock_irqsave(&engine->lock, save); + ret = nvkm_engctx_exists(parent, engine, pobject); + spin_unlock_irqrestore(&engine->lock, save); + if (ret) + return ret; + + /* create the new context, supports creating both raw objects and + * objects backed by instance memory + */ + if (size) { + ret = nvkm_gpuobj_create_(parent, engobj, oclass, + NV_ENGCTX_CLASS, pargpu, size, + align, flags, length, pobject); + } else { + ret = nvkm_object_create_(parent, engobj, oclass, + NV_ENGCTX_CLASS, length, pobject); + } + + engctx = *pobject; + if (ret) + return ret; + + /* must take the lock again and re-check a context doesn't already + * exist (in case of a race) - the lock had to be dropped before as + * it's not possible to allocate the object with it held. + */ + spin_lock_irqsave(&engine->lock, save); + ret = nvkm_engctx_exists(parent, engine, pobject); + if (ret) { + spin_unlock_irqrestore(&engine->lock, save); + nvkm_object_ref(NULL, &engctx); + return ret; + } + + if (client->vm) + atomic_inc(&client->vm->engref[nv_engidx(engine)]); + list_add(&nv_engctx(engctx)->head, &engine->contexts); + nv_engctx(engctx)->addr = ~0ULL; + spin_unlock_irqrestore(&engine->lock, save); + return 0; +} + +void +nvkm_engctx_destroy(struct nvkm_engctx *engctx) +{ + struct nvkm_engine *engine = engctx->gpuobj.object.engine; + struct nvkm_client *client = nvkm_client(engctx); + unsigned long save; + + nvkm_gpuobj_unmap(&engctx->vma); + spin_lock_irqsave(&engine->lock, save); + list_del(&engctx->head); + spin_unlock_irqrestore(&engine->lock, save); + + if (client->vm) + atomic_dec(&client->vm->engref[nv_engidx(engine)]); + + if (engctx->gpuobj.size) + nvkm_gpuobj_destroy(&engctx->gpuobj); + else + nvkm_object_destroy(&engctx->gpuobj.object); +} + +int +nvkm_engctx_init(struct nvkm_engctx *engctx) +{ + struct nvkm_object *object = nv_object(engctx); + struct nvkm_subdev *subdev = nv_subdev(object->engine); + struct nvkm_object *parent; + struct nvkm_subdev *pardev; + int ret; + + ret = nvkm_gpuobj_init(&engctx->gpuobj); + if (ret) + return ret; + + parent = nv_pclass(object->parent, NV_PARENT_CLASS); + pardev = nv_subdev(parent->engine); + if (nv_parent(parent)->context_attach) { + mutex_lock(&pardev->mutex); + ret = nv_parent(parent)->context_attach(parent, object); + mutex_unlock(&pardev->mutex); + } + + if (ret) { + nv_error(parent, "failed to attach %s context, %d\n", + subdev->name, ret); + return ret; + } + + nv_debug(parent, "attached %s context\n", subdev->name); + return 0; +} + +int +nvkm_engctx_fini(struct nvkm_engctx *engctx, bool suspend) +{ + struct nvkm_object *object = nv_object(engctx); + struct nvkm_subdev *subdev = nv_subdev(object->engine); + struct nvkm_object *parent; + struct nvkm_subdev *pardev; + int ret = 0; + + parent = nv_pclass(object->parent, NV_PARENT_CLASS); + pardev = nv_subdev(parent->engine); + if (nv_parent(parent)->context_detach) { + mutex_lock(&pardev->mutex); + ret = nv_parent(parent)->context_detach(parent, suspend, object); + mutex_unlock(&pardev->mutex); + } + + if (ret) { + nv_error(parent, "failed to detach %s context, %d\n", + subdev->name, ret); + return ret; + } + + nv_debug(parent, "detached %s context\n", subdev->name); + return nvkm_gpuobj_fini(&engctx->gpuobj, suspend); +} + +int +_nvkm_engctx_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_engctx *engctx; + int ret; + + ret = nvkm_engctx_create(parent, engine, oclass, NULL, 256, 256, + NVOBJ_FLAG_ZERO_ALLOC, &engctx); + *pobject = nv_object(engctx); + return ret; +} + +void +_nvkm_engctx_dtor(struct nvkm_object *object) +{ + nvkm_engctx_destroy(nv_engctx(object)); +} + +int +_nvkm_engctx_init(struct nvkm_object *object) +{ + return nvkm_engctx_init(nv_engctx(object)); +} + +int +_nvkm_engctx_fini(struct nvkm_object *object, bool suspend) +{ + return nvkm_engctx_fini(nv_engctx(object), suspend); +} + +struct nvkm_object * +nvkm_engctx_get(struct nvkm_engine *engine, u64 addr) +{ + struct nvkm_engctx *engctx; + unsigned long flags; + + spin_lock_irqsave(&engine->lock, flags); + list_for_each_entry(engctx, &engine->contexts, head) { + if (engctx->addr == addr) { + engctx->save = flags; + return nv_object(engctx); + } + } + spin_unlock_irqrestore(&engine->lock, flags); + return NULL; +} + +void +nvkm_engctx_put(struct nvkm_object *object) +{ + if (object) { + struct nvkm_engine *engine = nv_engine(object->engine); + struct nvkm_engctx *engctx = nv_engctx(object); + spin_unlock_irqrestore(&engine->lock, engctx->save); + } +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/core/engine.c b/kernel/drivers/gpu/drm/nouveau/nvkm/core/engine.c new file mode 100644 index 000000000..60820173c --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/core/engine.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 +#include +#include + +struct nvkm_engine * +nvkm_engine(void *obj, int idx) +{ + obj = nvkm_subdev(obj, idx); + if (obj && nv_iclass(obj, NV_ENGINE_CLASS)) + return nv_engine(obj); + return NULL; +} + +int +nvkm_engine_create_(struct nvkm_object *parent, struct nvkm_object *engobj, + struct nvkm_oclass *oclass, bool enable, + const char *iname, const char *fname, + int length, void **pobject) +{ + struct nvkm_engine *engine; + int ret; + + ret = nvkm_subdev_create_(parent, engobj, oclass, NV_ENGINE_CLASS, + iname, fname, length, pobject); + engine = *pobject; + if (ret) + return ret; + + if (parent) { + struct nvkm_device *device = nv_device(parent); + int engidx = nv_engidx(engine); + + if (device->disable_mask & (1ULL << engidx)) { + if (!nvkm_boolopt(device->cfgopt, iname, false)) { + nv_debug(engine, "engine disabled by hw/fw\n"); + return -ENODEV; + } + + nv_warn(engine, "ignoring hw/fw engine disable\n"); + } + + if (!nvkm_boolopt(device->cfgopt, iname, enable)) { + if (!enable) + nv_warn(engine, "disabled, %s=1 to enable\n", iname); + return -ENODEV; + } + } + + INIT_LIST_HEAD(&engine->contexts); + spin_lock_init(&engine->lock); + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/core/enum.c b/kernel/drivers/gpu/drm/nouveau/nvkm/core/enum.c new file mode 100644 index 000000000..4f92bfc13 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/core/enum.c @@ -0,0 +1,66 @@ +/* + * 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; +} + +const struct nvkm_enum * +nvkm_enum_print(const struct nvkm_enum *en, u32 value) +{ + en = nvkm_enum_find(en, value); + if (en) + pr_cont("%s", en->name); + else + pr_cont("(unknown enum 0x%08x)", value); + return en; +} + +void +nvkm_bitfield_print(const struct nvkm_bitfield *bf, u32 value) +{ + while (bf->name) { + if (value & bf->mask) { + pr_cont(" %s", bf->name); + value &= ~bf->mask; + } + + bf++; + } + + if (value) + pr_cont(" (unknown bits 0x%08x)", value); +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/core/event.c b/kernel/drivers/gpu/drm/nouveau/nvkm/core/event.c new file mode 100644 index 000000000..4e8d3fa04 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/core/event.c @@ -0,0 +1,99 @@ +/* + * 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(sizeof(*event->refs) * index_nr * types_nr, + 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/kernel/drivers/gpu/drm/nouveau/nvkm/core/gpuobj.c b/kernel/drivers/gpu/drm/nouveau/nvkm/core/gpuobj.c new file mode 100644 index 000000000..2eba801aa --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/core/gpuobj.c @@ -0,0 +1,316 @@ +/* + * 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 + +void +nvkm_gpuobj_destroy(struct nvkm_gpuobj *gpuobj) +{ + int i; + + if (gpuobj->flags & NVOBJ_FLAG_ZERO_FREE) { + for (i = 0; i < gpuobj->size; i += 4) + nv_wo32(gpuobj, i, 0x00000000); + } + + if (gpuobj->node) + nvkm_mm_free(&nv_gpuobj(gpuobj->parent)->heap, &gpuobj->node); + + if (gpuobj->heap.block_size) + nvkm_mm_fini(&gpuobj->heap); + + nvkm_object_destroy(&gpuobj->object); +} + +int +nvkm_gpuobj_create_(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, u32 pclass, + struct nvkm_object *pargpu, u32 size, u32 align, u32 flags, + int length, void **pobject) +{ + struct nvkm_instmem *imem = nvkm_instmem(parent); + struct nvkm_bar *bar = nvkm_bar(parent); + struct nvkm_gpuobj *gpuobj; + struct nvkm_mm *heap = NULL; + int ret, i; + u64 addr; + + *pobject = NULL; + + if (pargpu) { + while ((pargpu = nv_pclass(pargpu, NV_GPUOBJ_CLASS))) { + if (nv_gpuobj(pargpu)->heap.block_size) + break; + pargpu = pargpu->parent; + } + + if (unlikely(pargpu == NULL)) { + nv_error(parent, "no gpuobj heap\n"); + return -EINVAL; + } + + addr = nv_gpuobj(pargpu)->addr; + heap = &nv_gpuobj(pargpu)->heap; + atomic_inc(&parent->refcount); + } else { + ret = imem->alloc(imem, parent, size, align, &parent); + pargpu = parent; + if (ret) + return ret; + + addr = nv_memobj(pargpu)->addr; + size = nv_memobj(pargpu)->size; + + if (bar && bar->alloc) { + struct nvkm_instobj *iobj = (void *)parent; + struct nvkm_mem **mem = (void *)(iobj + 1); + struct nvkm_mem *node = *mem; + if (!bar->alloc(bar, parent, node, &pargpu)) { + nvkm_object_ref(NULL, &parent); + parent = pargpu; + } + } + } + + ret = nvkm_object_create_(parent, engine, oclass, pclass | + NV_GPUOBJ_CLASS, length, pobject); + nvkm_object_ref(NULL, &parent); + gpuobj = *pobject; + if (ret) + return ret; + + gpuobj->parent = pargpu; + gpuobj->flags = flags; + gpuobj->addr = addr; + gpuobj->size = size; + + if (heap) { + ret = nvkm_mm_head(heap, 0, 1, size, size, max(align, (u32)1), + &gpuobj->node); + if (ret) + return ret; + + gpuobj->addr += gpuobj->node->offset; + } + + if (gpuobj->flags & NVOBJ_FLAG_HEAP) { + ret = nvkm_mm_init(&gpuobj->heap, 0, gpuobj->size, 1); + if (ret) + return ret; + } + + if (flags & NVOBJ_FLAG_ZERO_ALLOC) { + for (i = 0; i < gpuobj->size; i += 4) + nv_wo32(gpuobj, i, 0x00000000); + } + + return ret; +} + +struct nvkm_gpuobj_class { + struct nvkm_object *pargpu; + u64 size; + u32 align; + u32 flags; +}; + +static int +_nvkm_gpuobj_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_gpuobj_class *args = data; + struct nvkm_gpuobj *object; + int ret; + + ret = nvkm_gpuobj_create(parent, engine, oclass, 0, args->pargpu, + args->size, args->align, args->flags, + &object); + *pobject = nv_object(object); + if (ret) + return ret; + + return 0; +} + +void +_nvkm_gpuobj_dtor(struct nvkm_object *object) +{ + nvkm_gpuobj_destroy(nv_gpuobj(object)); +} + +int +_nvkm_gpuobj_init(struct nvkm_object *object) +{ + return nvkm_gpuobj_init(nv_gpuobj(object)); +} + +int +_nvkm_gpuobj_fini(struct nvkm_object *object, bool suspend) +{ + return nvkm_gpuobj_fini(nv_gpuobj(object), suspend); +} + +u32 +_nvkm_gpuobj_rd32(struct nvkm_object *object, u64 addr) +{ + struct nvkm_gpuobj *gpuobj = nv_gpuobj(object); + struct nvkm_ofuncs *pfuncs = nv_ofuncs(gpuobj->parent); + if (gpuobj->node) + addr += gpuobj->node->offset; + return pfuncs->rd32(gpuobj->parent, addr); +} + +void +_nvkm_gpuobj_wr32(struct nvkm_object *object, u64 addr, u32 data) +{ + struct nvkm_gpuobj *gpuobj = nv_gpuobj(object); + struct nvkm_ofuncs *pfuncs = nv_ofuncs(gpuobj->parent); + if (gpuobj->node) + addr += gpuobj->node->offset; + pfuncs->wr32(gpuobj->parent, addr, data); +} + +static struct nvkm_oclass +_nvkm_gpuobj_oclass = { + .handle = 0x00000000, + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_gpuobj_ctor, + .dtor = _nvkm_gpuobj_dtor, + .init = _nvkm_gpuobj_init, + .fini = _nvkm_gpuobj_fini, + .rd32 = _nvkm_gpuobj_rd32, + .wr32 = _nvkm_gpuobj_wr32, + }, +}; + +int +nvkm_gpuobj_new(struct nvkm_object *parent, struct nvkm_object *pargpu, + u32 size, u32 align, u32 flags, + struct nvkm_gpuobj **pgpuobj) +{ + struct nvkm_object *engine = parent; + struct nvkm_gpuobj_class args = { + .pargpu = pargpu, + .size = size, + .align = align, + .flags = flags, + }; + + if (!nv_iclass(engine, NV_SUBDEV_CLASS)) + engine = &engine->engine->subdev.object; + BUG_ON(engine == NULL); + + return nvkm_object_ctor(parent, engine, &_nvkm_gpuobj_oclass, + &args, sizeof(args), + (struct nvkm_object **)pgpuobj); +} + +int +nvkm_gpuobj_map(struct nvkm_gpuobj *gpuobj, u32 access, struct nvkm_vma *vma) +{ + struct nvkm_bar *bar = nvkm_bar(gpuobj); + int ret = -EINVAL; + + if (bar && bar->umap) { + struct nvkm_instobj *iobj = (void *) + nv_pclass(nv_object(gpuobj), NV_MEMOBJ_CLASS); + struct nvkm_mem **mem = (void *)(iobj + 1); + ret = bar->umap(bar, *mem, access, vma); + } + + return ret; +} + +int +nvkm_gpuobj_map_vm(struct nvkm_gpuobj *gpuobj, struct nvkm_vm *vm, + u32 access, struct nvkm_vma *vma) +{ + struct nvkm_instobj *iobj = (void *) + nv_pclass(nv_object(gpuobj), NV_MEMOBJ_CLASS); + struct nvkm_mem **mem = (void *)(iobj + 1); + int ret; + + ret = nvkm_vm_get(vm, gpuobj->size, 12, access, vma); + if (ret) + return ret; + + nvkm_vm_map(vma, *mem); + return 0; +} + +void +nvkm_gpuobj_unmap(struct nvkm_vma *vma) +{ + if (vma->node) { + nvkm_vm_unmap(vma); + nvkm_vm_put(vma); + } +} + +/* 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. + */ + +static void +nvkm_gpudup_dtor(struct nvkm_object *object) +{ + struct nvkm_gpuobj *gpuobj = (void *)object; + nvkm_object_ref(NULL, &gpuobj->parent); + nvkm_object_destroy(&gpuobj->object); +} + +static struct nvkm_oclass +nvkm_gpudup_oclass = { + .handle = NV_GPUOBJ_CLASS, + .ofuncs = &(struct nvkm_ofuncs) { + .dtor = nvkm_gpudup_dtor, + .init = nvkm_object_init, + .fini = nvkm_object_fini, + }, +}; + +int +nvkm_gpuobj_dup(struct nvkm_object *parent, struct nvkm_gpuobj *base, + struct nvkm_gpuobj **pgpuobj) +{ + struct nvkm_gpuobj *gpuobj; + int ret; + + ret = nvkm_object_create(parent, &parent->engine->subdev.object, + &nvkm_gpudup_oclass, 0, &gpuobj); + *pgpuobj = gpuobj; + if (ret) + return ret; + + nvkm_object_ref(nv_object(base), &gpuobj->parent); + gpuobj->addr = base->addr; + gpuobj->size = base->size; + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/core/handle.c b/kernel/drivers/gpu/drm/nouveau/nvkm/core/handle.c new file mode 100644 index 000000000..dc7ff10eb --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/core/handle.c @@ -0,0 +1,221 @@ +/* + * 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 + +#define hprintk(h,l,f,a...) do { \ + struct nvkm_client *c = nvkm_client((h)->object); \ + struct nvkm_handle *p = (h)->parent; u32 n = p ? p->name : ~0; \ + nv_printk((c), l, "0x%08x:0x%08x "f, n, (h)->name, ##a); \ +} while(0) + +int +nvkm_handle_init(struct nvkm_handle *handle) +{ + struct nvkm_handle *item; + int ret; + + hprintk(handle, TRACE, "init running\n"); + ret = nvkm_object_inc(handle->object); + if (ret) + return ret; + + hprintk(handle, TRACE, "init children\n"); + list_for_each_entry(item, &handle->tree, head) { + ret = nvkm_handle_init(item); + if (ret) + goto fail; + } + + hprintk(handle, TRACE, "init completed\n"); + return 0; +fail: + hprintk(handle, ERROR, "init failed with %d\n", ret); + list_for_each_entry_continue_reverse(item, &handle->tree, head) { + nvkm_handle_fini(item, false); + } + + nvkm_object_dec(handle->object, false); + return ret; +} + +int +nvkm_handle_fini(struct nvkm_handle *handle, bool suspend) +{ + static char *name[2] = { "fini", "suspend" }; + struct nvkm_handle *item; + int ret; + + hprintk(handle, TRACE, "%s children\n", name[suspend]); + list_for_each_entry(item, &handle->tree, head) { + ret = nvkm_handle_fini(item, suspend); + if (ret && suspend) + goto fail; + } + + hprintk(handle, TRACE, "%s running\n", name[suspend]); + if (handle->object) { + ret = nvkm_object_dec(handle->object, suspend); + if (ret && suspend) + goto fail; + } + + hprintk(handle, TRACE, "%s completed\n", name[suspend]); + return 0; +fail: + hprintk(handle, ERROR, "%s failed with %d\n", name[suspend], ret); + list_for_each_entry_continue_reverse(item, &handle->tree, head) { + int rret = nvkm_handle_init(item); + if (rret) + hprintk(handle, FATAL, "failed to restart, %d\n", rret); + } + + return ret; +} + +int +nvkm_handle_create(struct nvkm_object *parent, u32 _parent, u32 _handle, + struct nvkm_object *object, struct nvkm_handle **phandle) +{ + struct nvkm_object *namedb; + struct nvkm_handle *handle; + int ret; + + namedb = parent; + while (!nv_iclass(namedb, NV_NAMEDB_CLASS)) + namedb = namedb->parent; + + handle = kzalloc(sizeof(*handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; + + INIT_LIST_HEAD(&handle->head); + INIT_LIST_HEAD(&handle->tree); + handle->name = _handle; + handle->priv = ~0; + + ret = nvkm_namedb_insert(nv_namedb(namedb), _handle, object, handle); + if (ret) { + kfree(handle); + return ret; + } + + if (nv_parent(parent)->object_attach) { + ret = nv_parent(parent)->object_attach(parent, object, _handle); + if (ret < 0) { + nvkm_handle_destroy(handle); + return ret; + } + + handle->priv = ret; + } + + if (object != namedb) { + while (!nv_iclass(namedb, NV_CLIENT_CLASS)) + namedb = namedb->parent; + + handle->parent = nvkm_namedb_get(nv_namedb(namedb), _parent); + if (handle->parent) { + list_add(&handle->head, &handle->parent->tree); + nvkm_namedb_put(handle->parent); + } + } + + hprintk(handle, TRACE, "created\n"); + *phandle = handle; + return 0; +} + +void +nvkm_handle_destroy(struct nvkm_handle *handle) +{ + struct nvkm_handle *item, *temp; + + hprintk(handle, TRACE, "destroy running\n"); + list_for_each_entry_safe(item, temp, &handle->tree, head) { + nvkm_handle_destroy(item); + } + list_del(&handle->head); + + if (handle->priv != ~0) { + struct nvkm_object *parent = handle->parent->object; + nv_parent(parent)->object_detach(parent, handle->priv); + } + + hprintk(handle, TRACE, "destroy completed\n"); + nvkm_namedb_remove(handle); + kfree(handle); +} + +struct nvkm_object * +nvkm_handle_ref(struct nvkm_object *parent, u32 name) +{ + struct nvkm_object *object = NULL; + struct nvkm_handle *handle; + + while (!nv_iclass(parent, NV_NAMEDB_CLASS)) + parent = parent->parent; + + handle = nvkm_namedb_get(nv_namedb(parent), name); + if (handle) { + nvkm_object_ref(handle->object, &object); + nvkm_namedb_put(handle); + } + + return object; +} + +struct nvkm_handle * +nvkm_handle_get_class(struct nvkm_object *engctx, u16 oclass) +{ + struct nvkm_namedb *namedb; + if (engctx && (namedb = (void *)nv_pclass(engctx, NV_NAMEDB_CLASS))) + return nvkm_namedb_get_class(namedb, oclass); + return NULL; +} + +struct nvkm_handle * +nvkm_handle_get_vinst(struct nvkm_object *engctx, u64 vinst) +{ + struct nvkm_namedb *namedb; + if (engctx && (namedb = (void *)nv_pclass(engctx, NV_NAMEDB_CLASS))) + return nvkm_namedb_get_vinst(namedb, vinst); + return NULL; +} + +struct nvkm_handle * +nvkm_handle_get_cinst(struct nvkm_object *engctx, u32 cinst) +{ + struct nvkm_namedb *namedb; + if (engctx && (namedb = (void *)nv_pclass(engctx, NV_NAMEDB_CLASS))) + return nvkm_namedb_get_cinst(namedb, cinst); + return NULL; +} + +void +nvkm_handle_put(struct nvkm_handle *handle) +{ + if (handle) + nvkm_namedb_put(handle); +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/core/ioctl.c b/kernel/drivers/gpu/drm/nouveau/nvkm/core/ioctl.c new file mode 100644 index 000000000..4459ff5f4 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/core/ioctl.c @@ -0,0 +1,526 @@ +/* + * 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 +#include + +static int +nvkm_ioctl_nop(struct nvkm_handle *handle, void *data, u32 size) +{ + struct nvkm_object *object = handle->object; + union { + struct nvif_ioctl_nop none; + } *args = data; + int ret; + + nv_ioctl(object, "nop size %d\n", size); + if (nvif_unvers(args->none)) { + nv_ioctl(object, "nop\n"); + } + + return ret; +} + +static int +nvkm_ioctl_sclass(struct nvkm_handle *handle, void *data, u32 size) +{ + struct nvkm_object *object = handle->object; + union { + struct nvif_ioctl_sclass_v0 v0; + } *args = data; + int ret; + + if (!nv_iclass(object, NV_PARENT_CLASS)) { + nv_debug(object, "cannot have children (sclass)\n"); + return -ENODEV; + } + + nv_ioctl(object, "sclass size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, true)) { + nv_ioctl(object, "sclass vers %d count %d\n", + args->v0.version, args->v0.count); + if (size == args->v0.count * sizeof(args->v0.oclass[0])) { + ret = nvkm_parent_lclass(object, args->v0.oclass, + args->v0.count); + if (ret >= 0) { + args->v0.count = ret; + ret = 0; + } + } else { + ret = -EINVAL; + } + } + + return ret; +} + +static int +nvkm_ioctl_new(struct nvkm_handle *handle, void *data, u32 size) +{ + union { + struct nvif_ioctl_new_v0 v0; + } *args = data; + struct nvkm_client *client = nvkm_client(handle->object); + struct nvkm_object *engctx = NULL; + struct nvkm_object *object = NULL; + struct nvkm_parent *parent; + struct nvkm_object *engine; + struct nvkm_oclass *oclass; + u32 _handle, _oclass; + int ret; + + nv_ioctl(client, "new size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, true)) { + _handle = args->v0.handle; + _oclass = args->v0.oclass; + } else + return ret; + + nv_ioctl(client, "new vers %d handle %08x class %08x " + "route %02x token %llx\n", + args->v0.version, _handle, _oclass, + args->v0.route, args->v0.token); + + if (!nv_iclass(handle->object, NV_PARENT_CLASS)) { + nv_debug(handle->object, "cannot have children (ctor)\n"); + ret = -ENODEV; + goto fail_class; + } + + parent = nv_parent(handle->object); + + /* check that parent supports the requested subclass */ + ret = nvkm_parent_sclass(&parent->object, _oclass, &engine, &oclass); + if (ret) { + nv_debug(parent, "illegal class 0x%04x\n", _oclass); + goto fail_class; + } + + /* make sure engine init has been completed *before* any objects + * it controls are created - the constructors may depend on + * state calculated at init (ie. default context construction) + */ + if (engine) { + ret = nvkm_object_inc(engine); + if (ret) + goto fail_class; + } + + /* if engine requires it, create a context object to insert + * between the parent and its children (eg. PGRAPH context) + */ + if (engine && nv_engine(engine)->cclass) { + ret = nvkm_object_ctor(&parent->object, engine, + nv_engine(engine)->cclass, + data, size, &engctx); + if (ret) + goto fail_engctx; + } else { + nvkm_object_ref(&parent->object, &engctx); + } + + /* finally, create new object and bind it to its handle */ + ret = nvkm_object_ctor(engctx, engine, oclass, data, size, &object); + client->data = object; + if (ret) + goto fail_ctor; + + ret = nvkm_object_inc(object); + if (ret) + goto fail_init; + + ret = nvkm_handle_create(&parent->object, handle->name, + _handle, object, &handle); + if (ret) + goto fail_handle; + + ret = nvkm_handle_init(handle); + handle->route = args->v0.route; + handle->token = args->v0.token; + if (ret) + nvkm_handle_destroy(handle); + +fail_handle: + nvkm_object_dec(object, false); +fail_init: + nvkm_object_ref(NULL, &object); +fail_ctor: + nvkm_object_ref(NULL, &engctx); +fail_engctx: + if (engine) + nvkm_object_dec(engine, false); +fail_class: + return ret; +} + +static int +nvkm_ioctl_del(struct nvkm_handle *handle, void *data, u32 size) +{ + struct nvkm_object *object = handle->object; + union { + struct nvif_ioctl_del none; + } *args = data; + int ret; + + nv_ioctl(object, "delete size %d\n", size); + if (nvif_unvers(args->none)) { + nv_ioctl(object, "delete\n"); + nvkm_handle_fini(handle, false); + nvkm_handle_destroy(handle); + } + + return ret; +} + +static int +nvkm_ioctl_mthd(struct nvkm_handle *handle, void *data, u32 size) +{ + struct nvkm_object *object = handle->object; + struct nvkm_ofuncs *ofuncs = object->oclass->ofuncs; + union { + struct nvif_ioctl_mthd_v0 v0; + } *args = data; + int ret; + + nv_ioctl(object, "mthd size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, true)) { + nv_ioctl(object, "mthd vers %d mthd %02x\n", + args->v0.version, args->v0.method); + if (ret = -ENODEV, ofuncs->mthd) + ret = ofuncs->mthd(object, args->v0.method, data, size); + } + + return ret; +} + + +static int +nvkm_ioctl_rd(struct nvkm_handle *handle, void *data, u32 size) +{ + struct nvkm_object *object = handle->object; + struct nvkm_ofuncs *ofuncs = object->oclass->ofuncs; + union { + struct nvif_ioctl_rd_v0 v0; + } *args = data; + int ret; + + nv_ioctl(object, "rd size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_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: + if (ret = -ENODEV, ofuncs->rd08) { + args->v0.data = nv_ro08(object, args->v0.addr); + ret = 0; + } + break; + case 2: + if (ret = -ENODEV, ofuncs->rd16) { + args->v0.data = nv_ro16(object, args->v0.addr); + ret = 0; + } + break; + case 4: + if (ret = -ENODEV, ofuncs->rd32) { + args->v0.data = nv_ro32(object, args->v0.addr); + ret = 0; + } + break; + default: + ret = -EINVAL; + break; + } + } + + return ret; +} + +static int +nvkm_ioctl_wr(struct nvkm_handle *handle, void *data, u32 size) +{ + struct nvkm_object *object = handle->object; + struct nvkm_ofuncs *ofuncs = object->oclass->ofuncs; + union { + struct nvif_ioctl_wr_v0 v0; + } *args = data; + int ret; + + nv_ioctl(object, "wr size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(object, "wr vers %d size %d addr %016llx data %08x\n", + args->v0.version, args->v0.size, args->v0.addr, + args->v0.data); + switch (args->v0.size) { + case 1: + if (ret = -ENODEV, ofuncs->wr08) { + nv_wo08(object, args->v0.addr, args->v0.data); + ret = 0; + } + break; + case 2: + if (ret = -ENODEV, ofuncs->wr16) { + nv_wo16(object, args->v0.addr, args->v0.data); + ret = 0; + } + break; + case 4: + if (ret = -ENODEV, ofuncs->wr32) { + nv_wo32(object, args->v0.addr, args->v0.data); + ret = 0; + } + break; + default: + ret = -EINVAL; + break; + } + } + + return ret; +} + +static int +nvkm_ioctl_map(struct nvkm_handle *handle, void *data, u32 size) +{ + struct nvkm_object *object = handle->object; + struct nvkm_ofuncs *ofuncs = object->oclass->ofuncs; + union { + struct nvif_ioctl_map_v0 v0; + } *args = data; + int ret; + + nv_ioctl(object, "map size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(object, "map vers %d\n", args->v0.version); + if (ret = -ENODEV, ofuncs->map) { + ret = ofuncs->map(object, &args->v0.handle, + &args->v0.length); + } + } + + return ret; +} + +static int +nvkm_ioctl_unmap(struct nvkm_handle *handle, void *data, u32 size) +{ + struct nvkm_object *object = handle->object; + union { + struct nvif_ioctl_unmap none; + } *args = data; + int ret; + + nv_ioctl(object, "unmap size %d\n", size); + if (nvif_unvers(args->none)) { + nv_ioctl(object, "unmap\n"); + } + + return ret; +} + +static int +nvkm_ioctl_ntfy_new(struct nvkm_handle *handle, void *data, u32 size) +{ + struct nvkm_object *object = handle->object; + struct nvkm_ofuncs *ofuncs = object->oclass->ofuncs; + union { + struct nvif_ioctl_ntfy_new_v0 v0; + } *args = data; + struct nvkm_event *event; + int ret; + + nv_ioctl(object, "ntfy new size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, true)) { + nv_ioctl(object, "ntfy new vers %d event %02x\n", + args->v0.version, args->v0.event); + if (ret = -ENODEV, ofuncs->ntfy) + ret = ofuncs->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_handle *handle, void *data, u32 size) +{ + struct nvkm_client *client = nvkm_client(handle->object); + struct nvkm_object *object = handle->object; + union { + struct nvif_ioctl_ntfy_del_v0 v0; + } *args = data; + int ret; + + nv_ioctl(object, "ntfy del size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_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_handle *handle, void *data, u32 size) +{ + struct nvkm_client *client = nvkm_client(handle->object); + struct nvkm_object *object = handle->object; + union { + struct nvif_ioctl_ntfy_get_v0 v0; + } *args = data; + int ret; + + nv_ioctl(object, "ntfy get size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_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_handle *handle, void *data, u32 size) +{ + struct nvkm_client *client = nvkm_client(handle->object); + struct nvkm_object *object = handle->object; + union { + struct nvif_ioctl_ntfy_put_v0 v0; + } *args = data; + int ret; + + nv_ioctl(object, "ntfy put size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_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_handle *, 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_handle *parent, u32 type, u32 nr, u32 *path, + void *data, u32 size, u8 owner, u8 *route, u64 *token) +{ + struct nvkm_handle *handle = parent; + struct nvkm_namedb *namedb; + struct nvkm_object *object; + int ret; + + while ((object = parent->object), nr--) { + nv_ioctl(object, "path 0x%08x\n", path[nr]); + if (!nv_iclass(object, NV_PARENT_CLASS)) { + nv_debug(object, "cannot have children (path)\n"); + return -EINVAL; + } + + if (!(namedb = (void *)nv_pclass(object, NV_NAMEDB_CLASS)) || + !(handle = nvkm_namedb_get(namedb, path[nr]))) { + nv_debug(object, "handle 0x%08x not found\n", path[nr]); + return -ENOENT; + } + nvkm_namedb_put(handle); + parent = handle; + } + + if (owner != NVIF_IOCTL_V0_OWNER_ANY && owner != handle->route) { + nv_ioctl(object, "object route != owner\n"); + return -EACCES; + } + *route = handle->route; + *token = handle->token; + + if (ret = -EINVAL, type < ARRAY_SIZE(nvkm_ioctl_v0)) { + if (nvkm_ioctl_v0[type].version == 0) + ret = nvkm_ioctl_v0[type].func(handle, data, size); + } + + return ret; +} + +int +nvkm_ioctl(struct nvkm_client *client, bool supervisor, + void *data, u32 size, void **hack) +{ + union { + struct nvif_ioctl_v0 v0; + } *args = data; + int ret; + + client->super = supervisor; + nv_ioctl(client, "size %d\n", size); + + if (nvif_unpack(args->v0, 0, 0, true)) { + nv_ioctl(client, "vers %d type %02x path %d owner %02x\n", + args->v0.version, args->v0.type, args->v0.path_nr, + args->v0.owner); + ret = nvkm_ioctl_path(client->root, args->v0.type, + args->v0.path_nr, args->v0.path, + data, size, args->v0.owner, + &args->v0.route, &args->v0.token); + } + + nv_ioctl(client, "return %d\n", ret); + if (hack) { + *hack = client->data; + client->data = NULL; + } + + client->super = false; + return ret; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/core/mm.c b/kernel/drivers/gpu/drm/nouveau/nvkm/core/mm.c new file mode 100644 index 000000000..7f458dfd5 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/core/mm.c @@ -0,0 +1,304 @@ +/* + * 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) + +static void +nvkm_mm_dump(struct nvkm_mm *mm, const char *header) +{ + struct nvkm_mm_node *node; + + printk(KERN_ERR "nvkm: %s\n", header); + printk(KERN_ERR "nvkm: node list:\n"); + list_for_each_entry(node, &mm->nodes, nl_entry) { + printk(KERN_ERR "nvkm: \t%08x %08x %d\n", + node->offset, node->length, node->type); + } + printk(KERN_ERR "nvkm: free list:\n"); + list_for_each_entry(node, &mm->free, fl_entry) { + printk(KERN_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->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->type = type; + list_del(&this->fl_entry); + *pnode = this; + return 0; + } + + return -ENOSPC; +} + +int +nvkm_mm_init(struct nvkm_mm *mm, 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 = ++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/kernel/drivers/gpu/drm/nouveau/nvkm/core/namedb.c b/kernel/drivers/gpu/drm/nouveau/nvkm/core/namedb.c new file mode 100644 index 000000000..6400767c5 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/core/namedb.c @@ -0,0 +1,199 @@ +/* + * 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 struct nvkm_handle * +nvkm_namedb_lookup(struct nvkm_namedb *namedb, u32 name) +{ + struct nvkm_handle *handle; + + list_for_each_entry(handle, &namedb->list, node) { + if (handle->name == name) + return handle; + } + + return NULL; +} + +static struct nvkm_handle * +nvkm_namedb_lookup_class(struct nvkm_namedb *namedb, u16 oclass) +{ + struct nvkm_handle *handle; + + list_for_each_entry(handle, &namedb->list, node) { + if (nv_mclass(handle->object) == oclass) + return handle; + } + + return NULL; +} + +static struct nvkm_handle * +nvkm_namedb_lookup_vinst(struct nvkm_namedb *namedb, u64 vinst) +{ + struct nvkm_handle *handle; + + list_for_each_entry(handle, &namedb->list, node) { + if (nv_iclass(handle->object, NV_GPUOBJ_CLASS)) { + if (nv_gpuobj(handle->object)->addr == vinst) + return handle; + } + } + + return NULL; +} + +static struct nvkm_handle * +nvkm_namedb_lookup_cinst(struct nvkm_namedb *namedb, u32 cinst) +{ + struct nvkm_handle *handle; + + list_for_each_entry(handle, &namedb->list, node) { + if (nv_iclass(handle->object, NV_GPUOBJ_CLASS)) { + if (nv_gpuobj(handle->object)->node && + nv_gpuobj(handle->object)->node->offset == cinst) + return handle; + } + } + + return NULL; +} + +int +nvkm_namedb_insert(struct nvkm_namedb *namedb, u32 name, + struct nvkm_object *object, + struct nvkm_handle *handle) +{ + int ret = -EEXIST; + write_lock_irq(&namedb->lock); + if (!nvkm_namedb_lookup(namedb, name)) { + nvkm_object_ref(object, &handle->object); + handle->namedb = namedb; + list_add(&handle->node, &namedb->list); + ret = 0; + } + write_unlock_irq(&namedb->lock); + return ret; +} + +void +nvkm_namedb_remove(struct nvkm_handle *handle) +{ + struct nvkm_namedb *namedb = handle->namedb; + struct nvkm_object *object = handle->object; + write_lock_irq(&namedb->lock); + list_del(&handle->node); + write_unlock_irq(&namedb->lock); + nvkm_object_ref(NULL, &object); +} + +struct nvkm_handle * +nvkm_namedb_get(struct nvkm_namedb *namedb, u32 name) +{ + struct nvkm_handle *handle; + read_lock(&namedb->lock); + handle = nvkm_namedb_lookup(namedb, name); + if (handle == NULL) + read_unlock(&namedb->lock); + return handle; +} + +struct nvkm_handle * +nvkm_namedb_get_class(struct nvkm_namedb *namedb, u16 oclass) +{ + struct nvkm_handle *handle; + read_lock(&namedb->lock); + handle = nvkm_namedb_lookup_class(namedb, oclass); + if (handle == NULL) + read_unlock(&namedb->lock); + return handle; +} + +struct nvkm_handle * +nvkm_namedb_get_vinst(struct nvkm_namedb *namedb, u64 vinst) +{ + struct nvkm_handle *handle; + read_lock(&namedb->lock); + handle = nvkm_namedb_lookup_vinst(namedb, vinst); + if (handle == NULL) + read_unlock(&namedb->lock); + return handle; +} + +struct nvkm_handle * +nvkm_namedb_get_cinst(struct nvkm_namedb *namedb, u32 cinst) +{ + struct nvkm_handle *handle; + read_lock(&namedb->lock); + handle = nvkm_namedb_lookup_cinst(namedb, cinst); + if (handle == NULL) + read_unlock(&namedb->lock); + return handle; +} + +void +nvkm_namedb_put(struct nvkm_handle *handle) +{ + if (handle) + read_unlock(&handle->namedb->lock); +} + +int +nvkm_namedb_create_(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, u32 pclass, + struct nvkm_oclass *sclass, u64 engcls, + int length, void **pobject) +{ + struct nvkm_namedb *namedb; + int ret; + + ret = nvkm_parent_create_(parent, engine, oclass, pclass | + NV_NAMEDB_CLASS, sclass, engcls, + length, pobject); + namedb = *pobject; + if (ret) + return ret; + + rwlock_init(&namedb->lock); + INIT_LIST_HEAD(&namedb->list); + return 0; +} + +int +_nvkm_namedb_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_namedb *object; + int ret; + + ret = nvkm_namedb_create(parent, engine, oclass, 0, NULL, 0, &object); + *pobject = nv_object(object); + if (ret) + return ret; + + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/core/notify.c b/kernel/drivers/gpu/drm/nouveau/nvkm/core/notify.c new file mode 100644 index 000000000..023610d01 --- /dev/null +++ b/kernel/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/kernel/drivers/gpu/drm/nouveau/nvkm/core/object.c b/kernel/drivers/gpu/drm/nouveau/nvkm/core/object.c new file mode 100644 index 000000000..979f3627d --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/core/object.c @@ -0,0 +1,330 @@ +/* + * 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 + +#ifdef NVKM_OBJECT_MAGIC +static struct list_head _objlist = LIST_HEAD_INIT(_objlist); +static DEFINE_SPINLOCK(_objlist_lock); +#endif + +int +nvkm_object_create_(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, u32 pclass, + int size, void **pobject) +{ + struct nvkm_object *object; + + object = *pobject = kzalloc(size, GFP_KERNEL); + if (!object) + return -ENOMEM; + + nvkm_object_ref(parent, &object->parent); + nvkm_object_ref(engine, (struct nvkm_object **)&object->engine); + object->oclass = oclass; + object->oclass->handle |= pclass; + atomic_set(&object->refcount, 1); + atomic_set(&object->usecount, 0); + +#ifdef NVKM_OBJECT_MAGIC + object->_magic = NVKM_OBJECT_MAGIC; + spin_lock(&_objlist_lock); + list_add(&object->list, &_objlist); + spin_unlock(&_objlist_lock); +#endif + return 0; +} + +int +_nvkm_object_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + if (size != 0) + return -ENOSYS; + return nvkm_object_create(parent, engine, oclass, 0, pobject); +} + +void +nvkm_object_destroy(struct nvkm_object *object) +{ +#ifdef NVKM_OBJECT_MAGIC + spin_lock(&_objlist_lock); + list_del(&object->list); + spin_unlock(&_objlist_lock); +#endif + nvkm_object_ref(NULL, (struct nvkm_object **)&object->engine); + nvkm_object_ref(NULL, &object->parent); + kfree(object); +} + +int +nvkm_object_init(struct nvkm_object *object) +{ + return 0; +} + +int +nvkm_object_fini(struct nvkm_object *object, bool suspend) +{ + return 0; +} + +struct nvkm_ofuncs +nvkm_object_ofuncs = { + .ctor = _nvkm_object_ctor, + .dtor = nvkm_object_destroy, + .init = nvkm_object_init, + .fini = nvkm_object_fini, +}; + +int +nvkm_object_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_ofuncs *ofuncs = oclass->ofuncs; + struct nvkm_object *object = NULL; + int ret; + + ret = ofuncs->ctor(parent, engine, oclass, data, size, &object); + *pobject = object; + if (ret < 0) { + if (ret != -ENODEV) { + nv_error(parent, "failed to create 0x%08x, %d\n", + oclass->handle, ret); + } + + if (object) { + ofuncs->dtor(object); + *pobject = NULL; + } + + return ret; + } + + if (ret == 0) { + nv_trace(object, "created\n"); + atomic_set(&object->refcount, 1); + } + + return 0; +} + +static void +nvkm_object_dtor(struct nvkm_object *object) +{ + nv_trace(object, "destroying\n"); + nv_ofuncs(object)->dtor(object); +} + +void +nvkm_object_ref(struct nvkm_object *obj, struct nvkm_object **ref) +{ + if (obj) { + atomic_inc(&obj->refcount); + nv_trace(obj, "inc() == %d\n", atomic_read(&obj->refcount)); + } + + if (*ref) { + int dead = atomic_dec_and_test(&(*ref)->refcount); + nv_trace(*ref, "dec() == %d\n", atomic_read(&(*ref)->refcount)); + if (dead) + nvkm_object_dtor(*ref); + } + + *ref = obj; +} + +int +nvkm_object_inc(struct nvkm_object *object) +{ + int ref = atomic_add_return(1, &object->usecount); + int ret; + + nv_trace(object, "use(+1) == %d\n", atomic_read(&object->usecount)); + if (ref != 1) + return 0; + + nv_trace(object, "initialising...\n"); + if (object->parent) { + ret = nvkm_object_inc(object->parent); + if (ret) { + nv_error(object, "parent failed, %d\n", ret); + goto fail_parent; + } + } + + if (object->engine) { + mutex_lock(&nv_subdev(object->engine)->mutex); + ret = nvkm_object_inc(&object->engine->subdev.object); + mutex_unlock(&nv_subdev(object->engine)->mutex); + if (ret) { + nv_error(object, "engine failed, %d\n", ret); + goto fail_engine; + } + } + + ret = nv_ofuncs(object)->init(object); + atomic_set(&object->usecount, 1); + if (ret) { + nv_error(object, "init failed, %d\n", ret); + goto fail_self; + } + + nv_trace(object, "initialised\n"); + return 0; + +fail_self: + if (object->engine) { + mutex_lock(&nv_subdev(object->engine)->mutex); + nvkm_object_dec(&object->engine->subdev.object, false); + mutex_unlock(&nv_subdev(object->engine)->mutex); + } +fail_engine: + if (object->parent) + nvkm_object_dec(object->parent, false); +fail_parent: + atomic_dec(&object->usecount); + return ret; +} + +static int +nvkm_object_decf(struct nvkm_object *object) +{ + int ret; + + nv_trace(object, "stopping...\n"); + + ret = nv_ofuncs(object)->fini(object, false); + atomic_set(&object->usecount, 0); + if (ret) + nv_warn(object, "failed fini, %d\n", ret); + + if (object->engine) { + mutex_lock(&nv_subdev(object->engine)->mutex); + nvkm_object_dec(&object->engine->subdev.object, false); + mutex_unlock(&nv_subdev(object->engine)->mutex); + } + + if (object->parent) + nvkm_object_dec(object->parent, false); + + nv_trace(object, "stopped\n"); + return 0; +} + +static int +nvkm_object_decs(struct nvkm_object *object) +{ + int ret, rret; + + nv_trace(object, "suspending...\n"); + + ret = nv_ofuncs(object)->fini(object, true); + atomic_set(&object->usecount, 0); + if (ret) { + nv_error(object, "failed suspend, %d\n", ret); + return ret; + } + + if (object->engine) { + mutex_lock(&nv_subdev(object->engine)->mutex); + ret = nvkm_object_dec(&object->engine->subdev.object, true); + mutex_unlock(&nv_subdev(object->engine)->mutex); + if (ret) { + nv_warn(object, "engine failed suspend, %d\n", ret); + goto fail_engine; + } + } + + if (object->parent) { + ret = nvkm_object_dec(object->parent, true); + if (ret) { + nv_warn(object, "parent failed suspend, %d\n", ret); + goto fail_parent; + } + } + + nv_trace(object, "suspended\n"); + return 0; + +fail_parent: + if (object->engine) { + mutex_lock(&nv_subdev(object->engine)->mutex); + rret = nvkm_object_inc(&object->engine->subdev.object); + mutex_unlock(&nv_subdev(object->engine)->mutex); + if (rret) + nv_fatal(object, "engine failed to reinit, %d\n", rret); + } + +fail_engine: + rret = nv_ofuncs(object)->init(object); + if (rret) + nv_fatal(object, "failed to reinit, %d\n", rret); + + return ret; +} + +int +nvkm_object_dec(struct nvkm_object *object, bool suspend) +{ + int ref = atomic_add_return(-1, &object->usecount); + int ret; + + nv_trace(object, "use(-1) == %d\n", atomic_read(&object->usecount)); + + if (ref == 0) { + if (suspend) + ret = nvkm_object_decs(object); + else + ret = nvkm_object_decf(object); + + if (ret) { + atomic_inc(&object->usecount); + return ret; + } + } + + return 0; +} + +void +nvkm_object_debug(void) +{ +#ifdef NVKM_OBJECT_MAGIC + struct nvkm_object *object; + if (!list_empty(&_objlist)) { + nv_fatal(NULL, "*******************************************\n"); + nv_fatal(NULL, "* AIIIII! object(s) still exist!!!\n"); + nv_fatal(NULL, "*******************************************\n"); + list_for_each_entry(object, &_objlist, list) { + nv_fatal(object, "%p/%p/%d/%d\n", + object->parent, object->engine, + atomic_read(&object->refcount), + atomic_read(&object->usecount)); + } + } +#endif +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/core/option.c b/kernel/drivers/gpu/drm/nouveau/nvkm/core/option.c new file mode 100644 index 000000000..19d153f8c --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/core/option.c @@ -0,0 +1,121 @@ +/* + * 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; +} + +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_NORMAL; + 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/kernel/drivers/gpu/drm/nouveau/nvkm/core/parent.c b/kernel/drivers/gpu/drm/nouveau/nvkm/core/parent.c new file mode 100644 index 000000000..dd56cd1ee --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/core/parent.c @@ -0,0 +1,159 @@ +/* + * 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 + +int +nvkm_parent_sclass(struct nvkm_object *parent, u16 handle, + struct nvkm_object **pengine, + struct nvkm_oclass **poclass) +{ + struct nvkm_sclass *sclass; + struct nvkm_engine *engine; + struct nvkm_oclass *oclass; + u64 mask; + + sclass = nv_parent(parent)->sclass; + while (sclass) { + if ((sclass->oclass->handle & 0xffff) == handle) { + *pengine = &parent->engine->subdev.object; + *poclass = sclass->oclass; + return 0; + } + + sclass = sclass->sclass; + } + + mask = nv_parent(parent)->engine; + while (mask) { + int i = __ffs64(mask); + + if (nv_iclass(parent, NV_CLIENT_CLASS)) + engine = nv_engine(nv_client(parent)->device); + else + engine = nvkm_engine(parent, i); + + if (engine) { + oclass = engine->sclass; + while (oclass->ofuncs) { + if ((oclass->handle & 0xffff) == handle) { + *pengine = nv_object(engine); + *poclass = oclass; + return 0; + } + oclass++; + } + } + + mask &= ~(1ULL << i); + } + + return -EINVAL; +} + +int +nvkm_parent_lclass(struct nvkm_object *parent, u32 *lclass, int size) +{ + struct nvkm_sclass *sclass; + struct nvkm_engine *engine; + struct nvkm_oclass *oclass; + int nr = -1, i; + u64 mask; + + sclass = nv_parent(parent)->sclass; + while (sclass) { + if (++nr < size) + lclass[nr] = sclass->oclass->handle & 0xffff; + sclass = sclass->sclass; + } + + mask = nv_parent(parent)->engine; + while (i = __ffs64(mask), mask) { + engine = nvkm_engine(parent, i); + if (engine && (oclass = engine->sclass)) { + while (oclass->ofuncs) { + if (++nr < size) + lclass[nr] = oclass->handle & 0xffff; + oclass++; + } + } + + mask &= ~(1ULL << i); + } + + return nr + 1; +} + +int +nvkm_parent_create_(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, u32 pclass, + struct nvkm_oclass *sclass, u64 engcls, + int size, void **pobject) +{ + struct nvkm_parent *object; + struct nvkm_sclass *nclass; + int ret; + + ret = nvkm_object_create_(parent, engine, oclass, pclass | + NV_PARENT_CLASS, size, pobject); + object = *pobject; + if (ret) + return ret; + + while (sclass && sclass->ofuncs) { + nclass = kzalloc(sizeof(*nclass), GFP_KERNEL); + if (!nclass) + return -ENOMEM; + + nclass->sclass = object->sclass; + object->sclass = nclass; + nclass->engine = engine ? nv_engine(engine) : NULL; + nclass->oclass = sclass; + sclass++; + } + + object->engine = engcls; + return 0; +} + +void +nvkm_parent_destroy(struct nvkm_parent *parent) +{ + struct nvkm_sclass *sclass; + + while ((sclass = parent->sclass)) { + parent->sclass = sclass->sclass; + kfree(sclass); + } + + nvkm_object_destroy(&parent->object); +} + + +void +_nvkm_parent_dtor(struct nvkm_object *object) +{ + nvkm_parent_destroy(nv_parent(object)); +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/core/printk.c b/kernel/drivers/gpu/drm/nouveau/nvkm/core/printk.c new file mode 100644 index 000000000..4a220eb91 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/core/printk.c @@ -0,0 +1,103 @@ +/* + * 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 + +int nv_info_debug_level = NV_DBG_INFO_NORMAL; + +void +nv_printk_(struct nvkm_object *object, int level, const char *fmt, ...) +{ + static const char name[] = { '!', 'E', 'W', ' ', 'D', 'T', 'P', 'S' }; + const char *pfx; + char mfmt[256]; + va_list args; + + switch (level) { + case NV_DBG_FATAL: + pfx = KERN_CRIT; + break; + case NV_DBG_ERROR: + pfx = KERN_ERR; + break; + case NV_DBG_WARN: + pfx = KERN_WARNING; + break; + case NV_DBG_INFO_NORMAL: + pfx = KERN_INFO; + break; + case NV_DBG_DEBUG: + case NV_DBG_PARANOIA: + case NV_DBG_TRACE: + case NV_DBG_SPAM: + default: + pfx = KERN_DEBUG; + break; + } + + if (object && !nv_iclass(object, NV_CLIENT_CLASS)) { + struct nvkm_object *device; + struct nvkm_object *subdev; + char obuf[64], *ofmt = ""; + + if (object->engine == NULL) { + subdev = object; + while (subdev && !nv_iclass(subdev, NV_SUBDEV_CLASS)) + subdev = subdev->parent; + } else { + subdev = &object->engine->subdev.object; + } + + device = subdev; + if (device->parent) + device = device->parent; + + if (object != subdev) { + snprintf(obuf, sizeof(obuf), "[0x%08x]", + nv_hclass(object)); + ofmt = obuf; + } + + if (level > nv_subdev(subdev)->debug) + return; + + snprintf(mfmt, sizeof(mfmt), "%snouveau %c[%8s][%s]%s %s", pfx, + name[level], nv_subdev(subdev)->name, + nv_device(device)->name, ofmt, fmt); + } else + if (object && nv_iclass(object, NV_CLIENT_CLASS)) { + if (level > nv_client(object)->debug) + return; + + snprintf(mfmt, sizeof(mfmt), "%snouveau %c[%8s] %s", pfx, + name[level], nv_client(object)->name, fmt); + } else { + snprintf(mfmt, sizeof(mfmt), "%snouveau: %s", pfx, fmt); + } + + va_start(args, fmt); + vprintk(mfmt, args); + va_end(args); +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/core/ramht.c b/kernel/drivers/gpu/drm/nouveau/nvkm/core/ramht.c new file mode 100644 index 000000000..ebd4d1547 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/core/ramht.c @@ -0,0 +1,106 @@ +/* + * 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); + hash = hash << 3; + return hash; +} + +int +nvkm_ramht_insert(struct nvkm_ramht *ramht, int chid, u32 handle, u32 context) +{ + struct nvkm_bar *bar = nvkm_bar(ramht); + u32 co, ho; + + co = ho = nvkm_ramht_hash(ramht, chid, handle); + do { + if (!nv_ro32(ramht, co + 4)) { + nv_wo32(ramht, co + 0, handle); + nv_wo32(ramht, co + 4, context); + if (bar) + bar->flush(bar); + return co; + } + + co += 8; + if (co >= nv_gpuobj(ramht)->size) + co = 0; + } while (co != ho); + + return -ENOMEM; +} + +void +nvkm_ramht_remove(struct nvkm_ramht *ramht, int cookie) +{ + struct nvkm_bar *bar = nvkm_bar(ramht); + nv_wo32(ramht, cookie + 0, 0x00000000); + nv_wo32(ramht, cookie + 4, 0x00000000); + if (bar) + bar->flush(bar); +} + +static struct nvkm_oclass +nvkm_ramht_oclass = { + .handle = 0x0000abcd, + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = NULL, + .dtor = _nvkm_gpuobj_dtor, + .init = _nvkm_gpuobj_init, + .fini = _nvkm_gpuobj_fini, + .rd32 = _nvkm_gpuobj_rd32, + .wr32 = _nvkm_gpuobj_wr32, + }, +}; + +int +nvkm_ramht_new(struct nvkm_object *parent, struct nvkm_object *pargpu, + u32 size, u32 align, struct nvkm_ramht **pramht) +{ + struct nvkm_ramht *ramht; + int ret; + + ret = nvkm_gpuobj_create(parent, parent->engine ? + &parent->engine->subdev.object : parent, /* bits = order_base_2(nv_gpuobj(ramht)->size >> 3); + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/core/subdev.c b/kernel/drivers/gpu/drm/nouveau/nvkm/core/subdev.c new file mode 100644 index 000000000..c5fb3a793 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/core/subdev.c @@ -0,0 +1,120 @@ +/* + * 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_subdev * +nvkm_subdev(void *obj, int idx) +{ + struct nvkm_object *object = nv_object(obj); + while (object && !nv_iclass(object, NV_SUBDEV_CLASS)) + object = object->parent; + if (object == NULL || nv_subidx(nv_subdev(object)) != idx) + object = nv_device(obj)->subdev[idx]; + return object ? nv_subdev(object) : NULL; +} + +void +nvkm_subdev_reset(struct nvkm_object *subdev) +{ + nv_trace(subdev, "resetting...\n"); + nv_ofuncs(subdev)->fini(subdev, false); + nv_debug(subdev, "reset\n"); +} + +int +nvkm_subdev_init(struct nvkm_subdev *subdev) +{ + int ret = nvkm_object_init(&subdev->object); + if (ret) + return ret; + + nvkm_subdev_reset(&subdev->object); + return 0; +} + +int +_nvkm_subdev_init(struct nvkm_object *object) +{ + return nvkm_subdev_init(nv_subdev(object)); +} + +int +nvkm_subdev_fini(struct nvkm_subdev *subdev, bool suspend) +{ + if (subdev->unit) { + nv_mask(subdev, 0x000200, subdev->unit, 0x00000000); + nv_mask(subdev, 0x000200, subdev->unit, subdev->unit); + } + + return nvkm_object_fini(&subdev->object, suspend); +} + +int +_nvkm_subdev_fini(struct nvkm_object *object, bool suspend) +{ + return nvkm_subdev_fini(nv_subdev(object), suspend); +} + +void +nvkm_subdev_destroy(struct nvkm_subdev *subdev) +{ + int subidx = nv_hclass(subdev) & 0xff; + nv_device(subdev)->subdev[subidx] = NULL; + nvkm_object_destroy(&subdev->object); +} + +void +_nvkm_subdev_dtor(struct nvkm_object *object) +{ + nvkm_subdev_destroy(nv_subdev(object)); +} + +int +nvkm_subdev_create_(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, u32 pclass, + const char *subname, const char *sysname, + int size, void **pobject) +{ + struct nvkm_subdev *subdev; + int ret; + + ret = nvkm_object_create_(parent, engine, oclass, pclass | + NV_SUBDEV_CLASS, size, pobject); + subdev = *pobject; + if (ret) + return ret; + + __mutex_init(&subdev->mutex, subname, &oclass->lock_class_key); + subdev->name = subname; + + if (parent) { + struct nvkm_device *device = nv_device(parent); + subdev->debug = nvkm_dbgopt(device->dbgopt, subname); + subdev->mmio = nv_subdev(device)->mmio; + } + + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/Kbuild new file mode 100644 index 000000000..6bd3d756f --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/Kbuild @@ -0,0 +1,19 @@ +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/dmaobj/Kbuild +include $(src)/nvkm/engine/fifo/Kbuild +include $(src)/nvkm/engine/gr/Kbuild +include $(src)/nvkm/engine/mpeg/Kbuild +include $(src)/nvkm/engine/mspdec/Kbuild +include $(src)/nvkm/engine/msppp/Kbuild +include $(src)/nvkm/engine/msvld/Kbuild +include $(src)/nvkm/engine/pm/Kbuild +include $(src)/nvkm/engine/sec/Kbuild +include $(src)/nvkm/engine/sw/Kbuild +include $(src)/nvkm/engine/vp/Kbuild diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/bsp/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/bsp/Kbuild new file mode 100644 index 000000000..5ac9f9e1a --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/bsp/Kbuild @@ -0,0 +1 @@ +nvkm-y += nvkm/engine/bsp/g84.o diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/bsp/g84.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/bsp/g84.c new file mode 100644 index 000000000..a0b1fd80f --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/bsp/g84.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, Ilia Mirkin + */ +#include +#include + +#include + +/******************************************************************************* + * BSP object classes + ******************************************************************************/ + +static struct nvkm_oclass +g84_bsp_sclass[] = { + { 0x74b0, &nvkm_object_ofuncs }, + {}, +}; + +/******************************************************************************* + * BSP context + ******************************************************************************/ + +static struct nvkm_oclass +g84_bsp_cclass = { + .handle = NV_ENGCTX(BSP, 0x84), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_xtensa_engctx_ctor, + .dtor = _nvkm_engctx_dtor, + .init = _nvkm_engctx_init, + .fini = _nvkm_engctx_fini, + .rd32 = _nvkm_engctx_rd32, + .wr32 = _nvkm_engctx_wr32, + }, +}; + +/******************************************************************************* + * BSP engine/subdev functions + ******************************************************************************/ + +static int +g84_bsp_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_xtensa *priv; + int ret; + + ret = nvkm_xtensa_create(parent, engine, oclass, 0x103000, true, + "PBSP", "bsp", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x04008000; + nv_engine(priv)->cclass = &g84_bsp_cclass; + nv_engine(priv)->sclass = g84_bsp_sclass; + priv->fifo_val = 0x1111; + priv->unkd28 = 0x90044; + return 0; +} + +struct nvkm_oclass +g84_bsp_oclass = { + .handle = NV_ENGINE(BSP, 0x84), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = g84_bsp_ctor, + .dtor = _nvkm_xtensa_dtor, + .init = _nvkm_xtensa_init, + .fini = _nvkm_xtensa_fini, + .rd32 = _nvkm_xtensa_rd32, + .wr32 = _nvkm_xtensa_wr32, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/Kbuild new file mode 100644 index 000000000..fa8cda705 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/Kbuild @@ -0,0 +1,4 @@ +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/gm204.o diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/com.fuc b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/com.fuc new file mode 100644 index 000000000..a558dfa4d --- /dev/null +++ b/kernel/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_pce_data +#else +.section #gf100_pce_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_pce_code +#else +.section #gf100_pce_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/kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/gf100.fuc3 b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/gf100.fuc3 new file mode 100644 index 000000000..36f0a99ac --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/gf100.fuc3 @@ -0,0 +1,2 @@ +#define GF100 +#include "com.fuc" diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/gf100.fuc3.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/gf100.fuc3.h new file mode 100644 index 000000000..d9af6e4e4 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/gf100.fuc3.h @@ -0,0 +1,606 @@ +uint32_t gf100_pce_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, +}; + +uint32_t gf100_pce_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/kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/gt215.fuc3 b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/gt215.fuc3 new file mode 100644 index 000000000..07bda93cf --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/gt215.fuc3 @@ -0,0 +1,2 @@ +#define GT215 +#include "com.fuc" diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/gt215.fuc3.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/gt215.fuc3.h new file mode 100644 index 000000000..f42c0d0d6 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/gt215.fuc3.h @@ -0,0 +1,620 @@ +uint32_t gt215_pce_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, +}; + +uint32_t gt215_pce_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/kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/gf100.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/gf100.c new file mode 100644 index 000000000..2d2e549c2 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/gf100.c @@ -0,0 +1,166 @@ +/* + * 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/gf100.fuc3.h" + +struct gf100_ce_priv { + struct nvkm_falcon base; +}; + +/******************************************************************************* + * Copy object classes + ******************************************************************************/ + +static struct nvkm_oclass +gf100_ce0_sclass[] = { + { 0x90b5, &nvkm_object_ofuncs }, + {}, +}; + +static struct nvkm_oclass +gf100_ce1_sclass[] = { + { 0x90b8, &nvkm_object_ofuncs }, + {}, +}; + +/******************************************************************************* + * PCE context + ******************************************************************************/ + +static struct nvkm_ofuncs +gf100_ce_context_ofuncs = { + .ctor = _nvkm_falcon_context_ctor, + .dtor = _nvkm_falcon_context_dtor, + .init = _nvkm_falcon_context_init, + .fini = _nvkm_falcon_context_fini, + .rd32 = _nvkm_falcon_context_rd32, + .wr32 = _nvkm_falcon_context_wr32, +}; + +static struct nvkm_oclass +gf100_ce0_cclass = { + .handle = NV_ENGCTX(CE0, 0xc0), + .ofuncs = &gf100_ce_context_ofuncs, +}; + +static struct nvkm_oclass +gf100_ce1_cclass = { + .handle = NV_ENGCTX(CE1, 0xc0), + .ofuncs = &gf100_ce_context_ofuncs, +}; + +/******************************************************************************* + * PCE engine/subdev functions + ******************************************************************************/ + +static int +gf100_ce_init(struct nvkm_object *object) +{ + struct gf100_ce_priv *priv = (void *)object; + int ret; + + ret = nvkm_falcon_init(&priv->base); + if (ret) + return ret; + + nv_wo32(priv, 0x084, nv_engidx(&priv->base.base) - NVDEV_ENGINE_CE0); + return 0; +} + +static int +gf100_ce0_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gf100_ce_priv *priv; + int ret; + + ret = nvkm_falcon_create(parent, engine, oclass, 0x104000, true, + "PCE0", "ce0", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00000040; + nv_subdev(priv)->intr = gt215_ce_intr; + nv_engine(priv)->cclass = &gf100_ce0_cclass; + nv_engine(priv)->sclass = gf100_ce0_sclass; + nv_falcon(priv)->code.data = gf100_pce_code; + nv_falcon(priv)->code.size = sizeof(gf100_pce_code); + nv_falcon(priv)->data.data = gf100_pce_data; + nv_falcon(priv)->data.size = sizeof(gf100_pce_data); + return 0; +} + +static int +gf100_ce1_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gf100_ce_priv *priv; + int ret; + + ret = nvkm_falcon_create(parent, engine, oclass, 0x105000, true, + "PCE1", "ce1", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00000080; + nv_subdev(priv)->intr = gt215_ce_intr; + nv_engine(priv)->cclass = &gf100_ce1_cclass; + nv_engine(priv)->sclass = gf100_ce1_sclass; + nv_falcon(priv)->code.data = gf100_pce_code; + nv_falcon(priv)->code.size = sizeof(gf100_pce_code); + nv_falcon(priv)->data.data = gf100_pce_data; + nv_falcon(priv)->data.size = sizeof(gf100_pce_data); + return 0; +} + +struct nvkm_oclass +gf100_ce0_oclass = { + .handle = NV_ENGINE(CE0, 0xc0), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_ce0_ctor, + .dtor = _nvkm_falcon_dtor, + .init = gf100_ce_init, + .fini = _nvkm_falcon_fini, + .rd32 = _nvkm_falcon_rd32, + .wr32 = _nvkm_falcon_wr32, + }, +}; + +struct nvkm_oclass +gf100_ce1_oclass = { + .handle = NV_ENGINE(CE1, 0xc0), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_ce1_ctor, + .dtor = _nvkm_falcon_dtor, + .init = gf100_ce_init, + .fini = _nvkm_falcon_fini, + .rd32 = _nvkm_falcon_rd32, + .wr32 = _nvkm_falcon_wr32, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/gk104.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/gk104.c new file mode 100644 index 000000000..a998932fa --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/gk104.c @@ -0,0 +1,173 @@ +/* + * 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 + +struct gk104_ce_priv { + struct nvkm_engine base; +}; + +/******************************************************************************* + * Copy object classes + ******************************************************************************/ + +static struct nvkm_oclass +gk104_ce_sclass[] = { + { 0xa0b5, &nvkm_object_ofuncs }, + {}, +}; + +/******************************************************************************* + * PCE context + ******************************************************************************/ + +static struct nvkm_ofuncs +gk104_ce_context_ofuncs = { + .ctor = _nvkm_engctx_ctor, + .dtor = _nvkm_engctx_dtor, + .init = _nvkm_engctx_init, + .fini = _nvkm_engctx_fini, + .rd32 = _nvkm_engctx_rd32, + .wr32 = _nvkm_engctx_wr32, +}; + +static struct nvkm_oclass +gk104_ce_cclass = { + .handle = NV_ENGCTX(CE0, 0xc0), + .ofuncs = &gk104_ce_context_ofuncs, +}; + +/******************************************************************************* + * PCE engine/subdev functions + ******************************************************************************/ + +static void +gk104_ce_intr(struct nvkm_subdev *subdev) +{ + const int ce = nv_subidx(subdev) - NVDEV_ENGINE_CE0; + struct gk104_ce_priv *priv = (void *)subdev; + u32 stat = nv_rd32(priv, 0x104908 + (ce * 0x1000)); + + if (stat) { + nv_warn(priv, "unhandled intr 0x%08x\n", stat); + nv_wr32(priv, 0x104908 + (ce * 0x1000), stat); + } +} + +static int +gk104_ce0_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gk104_ce_priv *priv; + int ret; + + ret = nvkm_engine_create(parent, engine, oclass, true, + "PCE0", "ce0", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00000040; + nv_subdev(priv)->intr = gk104_ce_intr; + nv_engine(priv)->cclass = &gk104_ce_cclass; + nv_engine(priv)->sclass = gk104_ce_sclass; + return 0; +} + +static int +gk104_ce1_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gk104_ce_priv *priv; + int ret; + + ret = nvkm_engine_create(parent, engine, oclass, true, + "PCE1", "ce1", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00000080; + nv_subdev(priv)->intr = gk104_ce_intr; + nv_engine(priv)->cclass = &gk104_ce_cclass; + nv_engine(priv)->sclass = gk104_ce_sclass; + return 0; +} + +static int +gk104_ce2_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gk104_ce_priv *priv; + int ret; + + ret = nvkm_engine_create(parent, engine, oclass, true, + "PCE2", "ce2", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00200000; + nv_subdev(priv)->intr = gk104_ce_intr; + nv_engine(priv)->cclass = &gk104_ce_cclass; + nv_engine(priv)->sclass = gk104_ce_sclass; + return 0; +} + +struct nvkm_oclass +gk104_ce0_oclass = { + .handle = NV_ENGINE(CE0, 0xe0), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gk104_ce0_ctor, + .dtor = _nvkm_engine_dtor, + .init = _nvkm_engine_init, + .fini = _nvkm_engine_fini, + }, +}; + +struct nvkm_oclass +gk104_ce1_oclass = { + .handle = NV_ENGINE(CE1, 0xe0), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gk104_ce1_ctor, + .dtor = _nvkm_engine_dtor, + .init = _nvkm_engine_init, + .fini = _nvkm_engine_fini, + }, +}; + +struct nvkm_oclass +gk104_ce2_oclass = { + .handle = NV_ENGINE(CE2, 0xe0), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gk104_ce2_ctor, + .dtor = _nvkm_engine_dtor, + .init = _nvkm_engine_init, + .fini = _nvkm_engine_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/gm204.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/gm204.c new file mode 100644 index 000000000..577eb2eea --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/gm204.c @@ -0,0 +1,173 @@ +/* + * 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 + +struct gm204_ce_priv { + struct nvkm_engine base; +}; + +/******************************************************************************* + * Copy object classes + ******************************************************************************/ + +static struct nvkm_oclass +gm204_ce_sclass[] = { + { 0xb0b5, &nvkm_object_ofuncs }, + {}, +}; + +/******************************************************************************* + * PCE context + ******************************************************************************/ + +static struct nvkm_ofuncs +gm204_ce_context_ofuncs = { + .ctor = _nvkm_engctx_ctor, + .dtor = _nvkm_engctx_dtor, + .init = _nvkm_engctx_init, + .fini = _nvkm_engctx_fini, + .rd32 = _nvkm_engctx_rd32, + .wr32 = _nvkm_engctx_wr32, +}; + +static struct nvkm_oclass +gm204_ce_cclass = { + .handle = NV_ENGCTX(CE0, 0x24), + .ofuncs = &gm204_ce_context_ofuncs, +}; + +/******************************************************************************* + * PCE engine/subdev functions + ******************************************************************************/ + +static void +gm204_ce_intr(struct nvkm_subdev *subdev) +{ + const int ce = nv_subidx(subdev) - NVDEV_ENGINE_CE0; + struct gm204_ce_priv *priv = (void *)subdev; + u32 stat = nv_rd32(priv, 0x104908 + (ce * 0x1000)); + + if (stat) { + nv_warn(priv, "unhandled intr 0x%08x\n", stat); + nv_wr32(priv, 0x104908 + (ce * 0x1000), stat); + } +} + +static int +gm204_ce0_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gm204_ce_priv *priv; + int ret; + + ret = nvkm_engine_create(parent, engine, oclass, true, + "PCE0", "ce0", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00000040; + nv_subdev(priv)->intr = gm204_ce_intr; + nv_engine(priv)->cclass = &gm204_ce_cclass; + nv_engine(priv)->sclass = gm204_ce_sclass; + return 0; +} + +static int +gm204_ce1_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gm204_ce_priv *priv; + int ret; + + ret = nvkm_engine_create(parent, engine, oclass, true, + "PCE1", "ce1", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00000080; + nv_subdev(priv)->intr = gm204_ce_intr; + nv_engine(priv)->cclass = &gm204_ce_cclass; + nv_engine(priv)->sclass = gm204_ce_sclass; + return 0; +} + +static int +gm204_ce2_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gm204_ce_priv *priv; + int ret; + + ret = nvkm_engine_create(parent, engine, oclass, true, + "PCE2", "ce2", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00200000; + nv_subdev(priv)->intr = gm204_ce_intr; + nv_engine(priv)->cclass = &gm204_ce_cclass; + nv_engine(priv)->sclass = gm204_ce_sclass; + return 0; +} + +struct nvkm_oclass +gm204_ce0_oclass = { + .handle = NV_ENGINE(CE0, 0x24), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gm204_ce0_ctor, + .dtor = _nvkm_engine_dtor, + .init = _nvkm_engine_init, + .fini = _nvkm_engine_fini, + }, +}; + +struct nvkm_oclass +gm204_ce1_oclass = { + .handle = NV_ENGINE(CE1, 0x24), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gm204_ce1_ctor, + .dtor = _nvkm_engine_dtor, + .init = _nvkm_engine_init, + .fini = _nvkm_engine_fini, + }, +}; + +struct nvkm_oclass +gm204_ce2_oclass = { + .handle = NV_ENGINE(CE2, 0x24), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gm204_ce2_ctor, + .dtor = _nvkm_engine_dtor, + .init = _nvkm_engine_init, + .fini = _nvkm_engine_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/gt215.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/gt215.c new file mode 100644 index 000000000..d8bb4293b --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/ce/gt215.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 +#include +#include +#include "fuc/gt215.fuc3.h" + +#include +#include +#include + +struct gt215_ce_priv { + struct nvkm_falcon base; +}; + +/******************************************************************************* + * Copy object classes + ******************************************************************************/ + +static struct nvkm_oclass +gt215_ce_sclass[] = { + { 0x85b5, &nvkm_object_ofuncs }, + {} +}; + +/******************************************************************************* + * PCE context + ******************************************************************************/ + +static struct nvkm_oclass +gt215_ce_cclass = { + .handle = NV_ENGCTX(CE0, 0xa3), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_falcon_context_ctor, + .dtor = _nvkm_falcon_context_dtor, + .init = _nvkm_falcon_context_init, + .fini = _nvkm_falcon_context_fini, + .rd32 = _nvkm_falcon_context_rd32, + .wr32 = _nvkm_falcon_context_wr32, + + }, +}; + +/******************************************************************************* + * PCE engine/subdev functions + ******************************************************************************/ + +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_subdev *subdev) +{ + struct nvkm_fifo *pfifo = nvkm_fifo(subdev); + struct nvkm_engine *engine = nv_engine(subdev); + struct nvkm_falcon *falcon = (void *)subdev; + struct nvkm_object *engctx; + u32 dispatch = nv_ro32(falcon, 0x01c); + u32 stat = nv_ro32(falcon, 0x008) & dispatch & ~(dispatch >> 16); + u64 inst = nv_ro32(falcon, 0x050) & 0x3fffffff; + u32 ssta = nv_ro32(falcon, 0x040) & 0x0000ffff; + u32 addr = nv_ro32(falcon, 0x040) >> 16; + u32 mthd = (addr & 0x07ff) << 2; + u32 subc = (addr & 0x3800) >> 11; + u32 data = nv_ro32(falcon, 0x044); + int chid; + + engctx = nvkm_engctx_get(engine, inst); + chid = pfifo->chid(pfifo, engctx); + + if (stat & 0x00000040) { + nv_error(falcon, "DISPATCH_ERROR ["); + nvkm_enum_print(gt215_ce_isr_error_name, ssta); + pr_cont("] ch %d [0x%010llx %s] subc %d mthd 0x%04x data 0x%08x\n", + chid, inst << 12, nvkm_client_name(engctx), subc, + mthd, data); + nv_wo32(falcon, 0x004, 0x00000040); + stat &= ~0x00000040; + } + + if (stat) { + nv_error(falcon, "unhandled intr 0x%08x\n", stat); + nv_wo32(falcon, 0x004, stat); + } + + nvkm_engctx_put(engctx); +} + +static int +gt215_ce_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + bool enable = (nv_device(parent)->chipset != 0xaf); + struct gt215_ce_priv *priv; + int ret; + + ret = nvkm_falcon_create(parent, engine, oclass, 0x104000, enable, + "PCE0", "ce0", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00802000; + nv_subdev(priv)->intr = gt215_ce_intr; + nv_engine(priv)->cclass = >215_ce_cclass; + nv_engine(priv)->sclass = gt215_ce_sclass; + nv_falcon(priv)->code.data = gt215_pce_code; + nv_falcon(priv)->code.size = sizeof(gt215_pce_code); + nv_falcon(priv)->data.data = gt215_pce_data; + nv_falcon(priv)->data.size = sizeof(gt215_pce_data); + return 0; +} + +struct nvkm_oclass +gt215_ce_oclass = { + .handle = NV_ENGINE(CE0, 0xa3), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gt215_ce_ctor, + .dtor = _nvkm_falcon_dtor, + .init = _nvkm_falcon_init, + .fini = _nvkm_falcon_fini, + .rd32 = _nvkm_falcon_rd32, + .wr32 = _nvkm_falcon_wr32, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/cipher/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/cipher/Kbuild new file mode 100644 index 000000000..fa3994532 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/cipher/Kbuild @@ -0,0 +1 @@ +nvkm-y += nvkm/engine/cipher/g84.o diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/cipher/g84.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/cipher/g84.c new file mode 100644 index 000000000..13f30428a --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/cipher/g84.c @@ -0,0 +1,184 @@ +/* + * 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 + +struct g84_cipher_priv { + struct nvkm_engine base; +}; + +/******************************************************************************* + * Crypt object classes + ******************************************************************************/ + +static int +g84_cipher_object_ctor(struct nvkm_object *parent, + struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_gpuobj *obj; + int ret; + + ret = nvkm_gpuobj_create(parent, engine, oclass, 0, parent, + 16, 16, 0, &obj); + *pobject = nv_object(obj); + if (ret) + return ret; + + nv_wo32(obj, 0x00, nv_mclass(obj)); + nv_wo32(obj, 0x04, 0x00000000); + nv_wo32(obj, 0x08, 0x00000000); + nv_wo32(obj, 0x0c, 0x00000000); + return 0; +} + +static struct nvkm_ofuncs +g84_cipher_ofuncs = { + .ctor = g84_cipher_object_ctor, + .dtor = _nvkm_gpuobj_dtor, + .init = _nvkm_gpuobj_init, + .fini = _nvkm_gpuobj_fini, + .rd32 = _nvkm_gpuobj_rd32, + .wr32 = _nvkm_gpuobj_wr32, +}; + +static struct nvkm_oclass +g84_cipher_sclass[] = { + { 0x74c1, &g84_cipher_ofuncs }, + {} +}; + +/******************************************************************************* + * PCIPHER context + ******************************************************************************/ + +static struct nvkm_oclass +g84_cipher_cclass = { + .handle = NV_ENGCTX(CIPHER, 0x84), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_engctx_ctor, + .dtor = _nvkm_engctx_dtor, + .init = _nvkm_engctx_init, + .fini = _nvkm_engctx_fini, + .rd32 = _nvkm_engctx_rd32, + .wr32 = _nvkm_engctx_wr32, + }, +}; + +/******************************************************************************* + * PCIPHER engine/subdev functions + ******************************************************************************/ + +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_subdev *subdev) +{ + struct nvkm_fifo *pfifo = nvkm_fifo(subdev); + struct nvkm_engine *engine = nv_engine(subdev); + struct nvkm_object *engctx; + struct g84_cipher_priv *priv = (void *)subdev; + u32 stat = nv_rd32(priv, 0x102130); + u32 mthd = nv_rd32(priv, 0x102190); + u32 data = nv_rd32(priv, 0x102194); + u32 inst = nv_rd32(priv, 0x102188) & 0x7fffffff; + int chid; + + engctx = nvkm_engctx_get(engine, inst); + chid = pfifo->chid(pfifo, engctx); + + if (stat) { + nv_error(priv, "%s", ""); + nvkm_bitfield_print(g84_cipher_intr_mask, stat); + pr_cont(" ch %d [0x%010llx %s] mthd 0x%04x data 0x%08x\n", + chid, (u64)inst << 12, nvkm_client_name(engctx), + mthd, data); + } + + nv_wr32(priv, 0x102130, stat); + nv_wr32(priv, 0x10200c, 0x10); + + nvkm_engctx_put(engctx); +} + +static int +g84_cipher_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct g84_cipher_priv *priv; + int ret; + + ret = nvkm_engine_create(parent, engine, oclass, true, + "PCIPHER", "cipher", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00004000; + nv_subdev(priv)->intr = g84_cipher_intr; + nv_engine(priv)->cclass = &g84_cipher_cclass; + nv_engine(priv)->sclass = g84_cipher_sclass; + return 0; +} + +static int +g84_cipher_init(struct nvkm_object *object) +{ + struct g84_cipher_priv *priv = (void *)object; + int ret; + + ret = nvkm_engine_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, 0x102130, 0xffffffff); + nv_wr32(priv, 0x102140, 0xffffffbf); + nv_wr32(priv, 0x10200c, 0x00000010); + return 0; +} + +struct nvkm_oclass +g84_cipher_oclass = { + .handle = NV_ENGINE(CIPHER, 0x84), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = g84_cipher_ctor, + .dtor = _nvkm_engine_dtor, + .init = g84_cipher_init, + .fini = _nvkm_engine_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/Kbuild new file mode 100644 index 000000000..de1bf092b --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/Kbuild @@ -0,0 +1,12 @@ +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/nv04.o +nvkm-y += nvkm/engine/device/nv10.o +nvkm-y += nvkm/engine/device/nv20.o +nvkm-y += nvkm/engine/device/nv30.o +nvkm-y += nvkm/engine/device/nv40.o +nvkm-y += nvkm/engine/device/nv50.o +nvkm-y += nvkm/engine/device/gf100.o +nvkm-y += nvkm/engine/device/gk104.o +nvkm-y += nvkm/engine/device/gm100.o diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/acpi.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/acpi.c new file mode 100644 index 000000000..f42706e1d --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/acpi.c @@ -0,0 +1,60 @@ +/* + * 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 + +#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_event_send(&device->event, 1, 0, NULL, 0); + + return NOTIFY_DONE; +} +#endif + +int +nvkm_acpi_fini(struct nvkm_device *device, bool suspend) +{ +#ifdef CONFIG_ACPI + unregister_acpi_notifier(&device->acpi.nb); +#endif + return 0; +} + +int +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 + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/acpi.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/acpi.h new file mode 100644 index 000000000..82dd359dd --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/acpi.h @@ -0,0 +1,8 @@ +#ifndef __NVKM_DEVICE_ACPI_H__ +#define __NVKM_DEVICE_ACPI_H__ +#include +struct nvkm_device; + +int nvkm_acpi_init(struct nvkm_device *); +int nvkm_acpi_fini(struct nvkm_device *, bool); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c new file mode 100644 index 000000000..63d8e52f4 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c @@ -0,0 +1,736 @@ +/* + * 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 +#include +#include +#include +#include + +#include +#include + +static DEFINE_MUTEX(nv_devices_mutex); +static LIST_HEAD(nv_devices); + +struct nvkm_device * +nvkm_device_find(u64 name) +{ + struct nvkm_device *device, *match = NULL; + mutex_lock(&nv_devices_mutex); + list_for_each_entry(device, &nv_devices, head) { + if (device->handle == name) { + match = device; + break; + } + } + mutex_unlock(&nv_devices_mutex); + return match; +} + +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; +} + +/****************************************************************************** + * nvkm_devobj (0x0080): class implementation + *****************************************************************************/ + +struct nvkm_devobj { + struct nvkm_parent base; + struct nvkm_object *subdev[NVDEV_SUBDEV_NR]; +}; + +static int +nvkm_devobj_info(struct nvkm_object *object, void *data, u32 size) +{ + struct nvkm_device *device = nv_device(object); + struct nvkm_fb *pfb = nvkm_fb(device); + struct nvkm_instmem *imem = nvkm_instmem(device); + union { + struct nv_device_info_v0 v0; + } *args = data; + int ret; + + nv_ioctl(object, "device info size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_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: + if (device->pdev) { + if (pci_find_capability(device->pdev, PCI_CAP_ID_AGP)) + args->v0.platform = NV_DEVICE_INFO_V0_AGP; + else + if (pci_is_pcie(device->pdev)) + args->v0.platform = NV_DEVICE_INFO_V0_PCIE; + else + args->v0.platform = NV_DEVICE_INFO_V0_PCI; + } else { + args->v0.platform = NV_DEVICE_INFO_V0_SOC; + } + 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; + default: + args->v0.family = 0; + break; + } + + args->v0.chipset = device->chipset; + args->v0.revision = device->chiprev; + if (pfb && pfb->ram) + args->v0.ram_size = args->v0.ram_user = pfb->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; + + return 0; +} + +static int +nvkm_devobj_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size) +{ + switch (mthd) { + case NV_DEVICE_V0_INFO: + return nvkm_devobj_info(object, data, size); + default: + break; + } + return -EINVAL; +} + +static u8 +nvkm_devobj_rd08(struct nvkm_object *object, u64 addr) +{ + return nv_rd08(object->engine, addr); +} + +static u16 +nvkm_devobj_rd16(struct nvkm_object *object, u64 addr) +{ + return nv_rd16(object->engine, addr); +} + +static u32 +nvkm_devobj_rd32(struct nvkm_object *object, u64 addr) +{ + return nv_rd32(object->engine, addr); +} + +static void +nvkm_devobj_wr08(struct nvkm_object *object, u64 addr, u8 data) +{ + nv_wr08(object->engine, addr, data); +} + +static void +nvkm_devobj_wr16(struct nvkm_object *object, u64 addr, u16 data) +{ + nv_wr16(object->engine, addr, data); +} + +static void +nvkm_devobj_wr32(struct nvkm_object *object, u64 addr, u32 data) +{ + nv_wr32(object->engine, addr, data); +} + +static int +nvkm_devobj_map(struct nvkm_object *object, u64 *addr, u32 *size) +{ + struct nvkm_device *device = nv_device(object); + *addr = nv_device_resource_start(device, 0); + *size = nv_device_resource_len(device, 0); + return 0; +} + +static const u64 disable_map[] = { + [NVDEV_SUBDEV_VBIOS] = NV_DEVICE_V0_DISABLE_VBIOS, + [NVDEV_SUBDEV_DEVINIT] = NV_DEVICE_V0_DISABLE_CORE, + [NVDEV_SUBDEV_GPIO] = NV_DEVICE_V0_DISABLE_CORE, + [NVDEV_SUBDEV_I2C] = NV_DEVICE_V0_DISABLE_CORE, + [NVDEV_SUBDEV_CLK ] = NV_DEVICE_V0_DISABLE_CORE, + [NVDEV_SUBDEV_MXM] = NV_DEVICE_V0_DISABLE_CORE, + [NVDEV_SUBDEV_MC] = NV_DEVICE_V0_DISABLE_CORE, + [NVDEV_SUBDEV_BUS] = NV_DEVICE_V0_DISABLE_CORE, + [NVDEV_SUBDEV_TIMER] = NV_DEVICE_V0_DISABLE_CORE, + [NVDEV_SUBDEV_FB] = NV_DEVICE_V0_DISABLE_CORE, + [NVDEV_SUBDEV_LTC] = NV_DEVICE_V0_DISABLE_CORE, + [NVDEV_SUBDEV_IBUS] = NV_DEVICE_V0_DISABLE_CORE, + [NVDEV_SUBDEV_INSTMEM] = NV_DEVICE_V0_DISABLE_CORE, + [NVDEV_SUBDEV_MMU] = NV_DEVICE_V0_DISABLE_CORE, + [NVDEV_SUBDEV_BAR] = NV_DEVICE_V0_DISABLE_CORE, + [NVDEV_SUBDEV_VOLT] = NV_DEVICE_V0_DISABLE_CORE, + [NVDEV_SUBDEV_THERM] = NV_DEVICE_V0_DISABLE_CORE, + [NVDEV_SUBDEV_PMU] = NV_DEVICE_V0_DISABLE_CORE, + [NVDEV_SUBDEV_FUSE] = NV_DEVICE_V0_DISABLE_CORE, + [NVDEV_ENGINE_DMAOBJ] = NV_DEVICE_V0_DISABLE_CORE, + [NVDEV_ENGINE_PM ] = NV_DEVICE_V0_DISABLE_CORE, + [NVDEV_ENGINE_FIFO] = NV_DEVICE_V0_DISABLE_FIFO, + [NVDEV_ENGINE_SW] = NV_DEVICE_V0_DISABLE_FIFO, + [NVDEV_ENGINE_GR] = NV_DEVICE_V0_DISABLE_GR, + [NVDEV_ENGINE_MPEG] = NV_DEVICE_V0_DISABLE_MPEG, + [NVDEV_ENGINE_ME] = NV_DEVICE_V0_DISABLE_ME, + [NVDEV_ENGINE_VP] = NV_DEVICE_V0_DISABLE_VP, + [NVDEV_ENGINE_CIPHER] = NV_DEVICE_V0_DISABLE_CIPHER, + [NVDEV_ENGINE_BSP] = NV_DEVICE_V0_DISABLE_BSP, + [NVDEV_ENGINE_MSPPP] = NV_DEVICE_V0_DISABLE_MSPPP, + [NVDEV_ENGINE_CE0] = NV_DEVICE_V0_DISABLE_CE0, + [NVDEV_ENGINE_CE1] = NV_DEVICE_V0_DISABLE_CE1, + [NVDEV_ENGINE_CE2] = NV_DEVICE_V0_DISABLE_CE2, + [NVDEV_ENGINE_VIC] = NV_DEVICE_V0_DISABLE_VIC, + [NVDEV_ENGINE_MSENC] = NV_DEVICE_V0_DISABLE_MSENC, + [NVDEV_ENGINE_DISP] = NV_DEVICE_V0_DISABLE_DISP, + [NVDEV_ENGINE_MSVLD] = NV_DEVICE_V0_DISABLE_MSVLD, + [NVDEV_ENGINE_SEC] = NV_DEVICE_V0_DISABLE_SEC, + [NVDEV_SUBDEV_NR] = 0, +}; + +static void +nvkm_devobj_dtor(struct nvkm_object *object) +{ + struct nvkm_devobj *devobj = (void *)object; + int i; + + for (i = NVDEV_SUBDEV_NR - 1; i >= 0; i--) + nvkm_object_ref(NULL, &devobj->subdev[i]); + + nvkm_parent_destroy(&devobj->base); +} + +static struct nvkm_oclass +nvkm_devobj_oclass_super = { + .handle = NV_DEVICE, + .ofuncs = &(struct nvkm_ofuncs) { + .dtor = nvkm_devobj_dtor, + .init = _nvkm_parent_init, + .fini = _nvkm_parent_fini, + .mthd = nvkm_devobj_mthd, + .map = nvkm_devobj_map, + .rd08 = nvkm_devobj_rd08, + .rd16 = nvkm_devobj_rd16, + .rd32 = nvkm_devobj_rd32, + .wr08 = nvkm_devobj_wr08, + .wr16 = nvkm_devobj_wr16, + .wr32 = nvkm_devobj_wr32, + } +}; + +static int +nvkm_devobj_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + union { + struct nv_device_v0 v0; + } *args = data; + struct nvkm_client *client = nv_client(parent); + struct nvkm_device *device; + struct nvkm_devobj *devobj; + u32 boot0, strap; + u64 disable, mmio_base, mmio_size; + void __iomem *map; + int ret, i, c; + + nv_ioctl(parent, "create device size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(parent, "create device v%d device %016llx " + "disable %016llx debug0 %016llx\n", + args->v0.version, args->v0.device, + args->v0.disable, args->v0.debug0); + } else + return ret; + + /* give priviledged clients register access */ + if (client->super) + oclass = &nvkm_devobj_oclass_super; + + /* find the device subdev that matches what the client requested */ + device = nv_device(client->device); + if (args->v0.device != ~0) { + device = nvkm_device_find(args->v0.device); + if (!device) + return -ENODEV; + } + + ret = nvkm_parent_create(parent, nv_object(device), oclass, 0, + nvkm_control_oclass, + (1ULL << NVDEV_ENGINE_DMAOBJ) | + (1ULL << NVDEV_ENGINE_FIFO) | + (1ULL << NVDEV_ENGINE_DISP) | + (1ULL << NVDEV_ENGINE_PM), &devobj); + *pobject = nv_object(devobj); + if (ret) + return ret; + + mmio_base = nv_device_resource_start(device, 0); + mmio_size = nv_device_resource_len(device, 0); + + /* translate api disable mask into internal mapping */ + disable = args->v0.debug0; + for (i = 0; i < NVDEV_SUBDEV_NR; i++) { + if (args->v0.disable & disable_map[i]) + disable |= (1ULL << i); + } + + /* identify the chipset, and determine classes of subdev/engines */ + if (!(args->v0.disable & NV_DEVICE_V0_DISABLE_IDENTIFY) && + !device->card_type) { + map = ioremap(mmio_base, 0x102000); + if (map == NULL) + return -ENOMEM; + + /* switch mmio to cpu's native endianness */ +#ifndef __BIG_ENDIAN + if (ioread32_native(map + 0x000004) != 0x00000000) { +#else + if (ioread32_native(map + 0x000004) == 0x00000000) { +#endif + iowrite32_native(0x01000001, map + 0x000004); + ioread32_native(map); + } + + /* read boot0 and strapping information */ + boot0 = ioread32_native(map + 0x000000); + strap = ioread32_native(map + 0x101000); + iounmap(map); + + /* 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; + default: + break; + } + } else + if ((boot0 & 0xff00fff0) == 0x20004000) { + if (boot0 & 0x00f00000) + device->chipset = 0x05; + else + device->chipset = 0x04; + device->card_type = NV_04; + } + + switch (device->card_type) { + case NV_04: ret = nv04_identify(device); break; + case NV_10: + case NV_11: ret = nv10_identify(device); break; + case NV_20: ret = nv20_identify(device); break; + case NV_30: ret = nv30_identify(device); break; + case NV_40: ret = nv40_identify(device); break; + case NV_50: ret = nv50_identify(device); break; + case NV_C0: ret = gf100_identify(device); break; + case NV_E0: ret = gk104_identify(device); break; + case GM100: ret = gm100_identify(device); break; + default: + ret = -EINVAL; + break; + } + + if (ret) { + nv_error(device, "unknown chipset, 0x%08x\n", boot0); + return ret; + } + + nv_info(device, "BOOT0 : 0x%08x\n", boot0); + nv_info(device, "Chipset: %s (NV%02X)\n", + device->cname, device->chipset); + nv_info(device, "Family : NV%02X\n", device->card_type); + + /* 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; + } + + nv_debug(device, "crystal freq: %dKHz\n", device->crystal); + } else + if ( (args->v0.disable & NV_DEVICE_V0_DISABLE_IDENTIFY)) { + device->cname = "NULL"; + device->oclass[NVDEV_SUBDEV_VBIOS] = &nvkm_bios_oclass; + } + + if (!(args->v0.disable & NV_DEVICE_V0_DISABLE_MMIO) && + !nv_subdev(device)->mmio) { + nv_subdev(device)->mmio = ioremap(mmio_base, mmio_size); + if (!nv_subdev(device)->mmio) { + nv_error(device, "unable to map device registers\n"); + return -ENOMEM; + } + } + + /* ensure requested subsystems are available for use */ + for (i = 1, c = 1; i < NVDEV_SUBDEV_NR; i++) { + if (!(oclass = device->oclass[i]) || (disable & (1ULL << i))) + continue; + + if (device->subdev[i]) { + nvkm_object_ref(device->subdev[i], &devobj->subdev[i]); + continue; + } + + ret = nvkm_object_ctor(nv_object(device), NULL, oclass, + NULL, i, &devobj->subdev[i]); + if (ret == -ENODEV) + continue; + if (ret) + return ret; + + device->subdev[i] = devobj->subdev[i]; + + /* note: can't init *any* subdevs until devinit has been run + * due to not knowing exactly what the vbios init tables will + * mess with. devinit also can't be run until all of its + * dependencies have been created. + * + * this code delays init of any subdev until all of devinit's + * dependencies have been created, and then initialises each + * subdev in turn as they're created. + */ + while (i >= NVDEV_SUBDEV_DEVINIT_LAST && c <= i) { + struct nvkm_object *subdev = devobj->subdev[c++]; + if (subdev && !nv_iclass(subdev, NV_ENGINE_CLASS)) { + ret = nvkm_object_inc(subdev); + if (ret) + return ret; + atomic_dec(&nv_object(device)->usecount); + } else + if (subdev) { + nvkm_subdev_reset(subdev); + } + } + } + + return 0; +} + +static struct nvkm_ofuncs +nvkm_devobj_ofuncs = { + .ctor = nvkm_devobj_ctor, + .dtor = nvkm_devobj_dtor, + .init = _nvkm_parent_init, + .fini = _nvkm_parent_fini, + .mthd = nvkm_devobj_mthd, +}; + +/****************************************************************************** + * nvkm_device: engine functions + *****************************************************************************/ + +struct nvkm_device * +nv_device(void *obj) +{ + struct nvkm_object *device = nv_object(obj); + if (device->engine == NULL) { + while (device && device->parent) + device = device->parent; + } else { + device = &nv_object(obj)->engine->subdev.object; + if (device && device->parent) + device = device->parent; + } +#if CONFIG_NOUVEAU_DEBUG >= NV_DBG_PARANOIA + if (unlikely(!device)) + nv_assert("BAD CAST -> NvDevice, 0x%08x\n", nv_hclass(obj)); +#endif + return (void *)device; +} + +static struct nvkm_oclass +nvkm_device_sclass[] = { + { 0x0080, &nvkm_devobj_ofuncs }, + {} +}; + +static int +nvkm_device_event_ctor(struct nvkm_object *object, void *data, u32 size, + struct nvkm_notify *notify) +{ + if (!WARN_ON(size != 0)) { + notify->size = 0; + notify->types = 1; + notify->index = 0; + return 0; + } + return -EINVAL; +} + +static const struct nvkm_event_func +nvkm_device_event_func = { + .ctor = nvkm_device_event_ctor, +}; + +static int +nvkm_device_fini(struct nvkm_object *object, bool suspend) +{ + struct nvkm_device *device = (void *)object; + struct nvkm_object *subdev; + int ret, i; + + for (i = NVDEV_SUBDEV_NR - 1; i >= 0; i--) { + if ((subdev = device->subdev[i])) { + if (!nv_iclass(subdev, NV_ENGINE_CLASS)) { + ret = nvkm_object_dec(subdev, suspend); + if (ret && suspend) + goto fail; + } + } + } + + ret = nvkm_acpi_fini(device, suspend); +fail: + for (; ret && i < NVDEV_SUBDEV_NR; i++) { + if ((subdev = device->subdev[i])) { + if (!nv_iclass(subdev, NV_ENGINE_CLASS)) { + ret = nvkm_object_inc(subdev); + if (ret) { + /* XXX */ + } + } + } + } + + return ret; +} + +static int +nvkm_device_init(struct nvkm_object *object) +{ + struct nvkm_device *device = (void *)object; + struct nvkm_object *subdev; + int ret, i = 0; + + ret = nvkm_acpi_init(device); + if (ret) + goto fail; + + for (i = 0; i < NVDEV_SUBDEV_NR; i++) { + if ((subdev = device->subdev[i])) { + if (!nv_iclass(subdev, NV_ENGINE_CLASS)) { + ret = nvkm_object_inc(subdev); + if (ret) + goto fail; + } else { + nvkm_subdev_reset(subdev); + } + } + } + + ret = 0; +fail: + for (--i; ret && i >= 0; i--) { + if ((subdev = device->subdev[i])) { + if (!nv_iclass(subdev, NV_ENGINE_CLASS)) + nvkm_object_dec(subdev, false); + } + } + + if (ret) + nvkm_acpi_fini(device, false); + return ret; +} + +static void +nvkm_device_dtor(struct nvkm_object *object) +{ + struct nvkm_device *device = (void *)object; + + nvkm_event_fini(&device->event); + + mutex_lock(&nv_devices_mutex); + list_del(&device->head); + mutex_unlock(&nv_devices_mutex); + + if (nv_subdev(device)->mmio) + iounmap(nv_subdev(device)->mmio); + + nvkm_engine_destroy(&device->engine); +} + +resource_size_t +nv_device_resource_start(struct nvkm_device *device, unsigned int bar) +{ + if (nv_device_is_pci(device)) { + return pci_resource_start(device->pdev, bar); + } else { + struct resource *res; + res = platform_get_resource(device->platformdev, + IORESOURCE_MEM, bar); + if (!res) + return 0; + return res->start; + } +} + +resource_size_t +nv_device_resource_len(struct nvkm_device *device, unsigned int bar) +{ + if (nv_device_is_pci(device)) { + return pci_resource_len(device->pdev, bar); + } else { + struct resource *res; + res = platform_get_resource(device->platformdev, + IORESOURCE_MEM, bar); + if (!res) + return 0; + return resource_size(res); + } +} + +int +nv_device_get_irq(struct nvkm_device *device, bool stall) +{ + if (nv_device_is_pci(device)) { + return device->pdev->irq; + } else { + return platform_get_irq_byname(device->platformdev, + stall ? "stall" : "nonstall"); + } +} + +static struct nvkm_oclass +nvkm_device_oclass = { + .handle = NV_ENGINE(DEVICE, 0x00), + .ofuncs = &(struct nvkm_ofuncs) { + .dtor = nvkm_device_dtor, + .init = nvkm_device_init, + .fini = nvkm_device_fini, + }, +}; + +int +nvkm_device_create_(void *dev, enum nv_bus_type type, u64 name, + const char *sname, const char *cfg, const char *dbg, + int length, void **pobject) +{ + struct nvkm_device *device; + int ret = -EEXIST; + + mutex_lock(&nv_devices_mutex); + list_for_each_entry(device, &nv_devices, head) { + if (device->handle == name) + goto done; + } + + ret = nvkm_engine_create_(NULL, NULL, &nvkm_device_oclass, true, + "DEVICE", "device", length, pobject); + device = *pobject; + if (ret) + goto done; + + switch (type) { + case NVKM_BUS_PCI: + device->pdev = dev; + break; + case NVKM_BUS_PLATFORM: + device->platformdev = dev; + break; + } + device->handle = name; + device->cfgopt = cfg; + device->dbgopt = dbg; + device->name = sname; + + nv_subdev(device)->debug = nvkm_dbgopt(device->dbgopt, "DEVICE"); + nv_engine(device)->sclass = nvkm_device_sclass; + list_add(&device->head, &nv_devices); + + ret = nvkm_event_init(&nvkm_device_event_func, 1, 1, &device->event); +done: + mutex_unlock(&nv_devices_mutex); + return ret; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/ctrl.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/ctrl.c new file mode 100644 index 000000000..0b794b13c --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/ctrl.c @@ -0,0 +1,199 @@ +/* + * 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 + +static int +nvkm_control_mthd_pstate_info(struct nvkm_object *object, void *data, u32 size) +{ + union { + struct nvif_control_pstate_info_v0 v0; + } *args = data; + struct nvkm_clk *clk = nvkm_clk(object); + int ret; + + nv_ioctl(object, "control pstate info size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(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 = -ENOSYS; + args->v0.pstate = NVIF_CONTROL_PSTATE_INFO_V0_PSTATE_UNKNOWN; + } + + return 0; +} + +static int +nvkm_control_mthd_pstate_attr(struct nvkm_object *object, void *data, u32 size) +{ + union { + struct nvif_control_pstate_attr_v0 v0; + } *args = data; + struct nvkm_clk *clk = nvkm_clk(object); + struct nvkm_domain *domain; + struct nvkm_pstate *pstate; + struct nvkm_cstate *cstate; + int i = 0, j = -1; + u32 lo, hi; + int ret; + + nv_ioctl(object, "control pstate attr size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(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(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_object *object, void *data, u32 size) +{ + union { + struct nvif_control_pstate_user_v0 v0; + } *args = data; + struct nvkm_clk *clk = nvkm_clk(object); + int ret; + + nv_ioctl(object, "control pstate user size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(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) +{ + switch (mthd) { + case NVIF_CONTROL_PSTATE_INFO: + return nvkm_control_mthd_pstate_info(object, data, size); + case NVIF_CONTROL_PSTATE_ATTR: + return nvkm_control_mthd_pstate_attr(object, data, size); + case NVIF_CONTROL_PSTATE_USER: + return nvkm_control_mthd_pstate_user(object, data, size); + default: + break; + } + return -EINVAL; +} + +static struct nvkm_ofuncs +nvkm_control_ofuncs = { + .ctor = _nvkm_object_ctor, + .dtor = nvkm_object_destroy, + .init = nvkm_object_init, + .fini = nvkm_object_fini, + .mthd = nvkm_control_mthd, +}; + +struct nvkm_oclass +nvkm_control_oclass[] = { + { .handle = NVIF_IOCTL_NEW_V0_CONTROL, + .ofuncs = &nvkm_control_ofuncs + }, + {} +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/gf100.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/gf100.c new file mode 100644 index 000000000..82b38d7e9 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/gf100.c @@ -0,0 +1,358 @@ +/* + * 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 +#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 +gf100_identify(struct nvkm_device *device) +{ + switch (device->chipset) { + case 0xc0: + device->cname = "GF100"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = g94_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = g94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &gf100_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = >215_therm_oclass; + device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = gf100_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = gf100_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = gf100_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = gf100_fb_oclass; + device->oclass[NVDEV_SUBDEV_LTC ] = gf100_ltc_oclass; + device->oclass[NVDEV_SUBDEV_IBUS ] = &gf100_ibus_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &gf100_mmu_oclass; + device->oclass[NVDEV_SUBDEV_BAR ] = &gf100_bar_oclass; + device->oclass[NVDEV_SUBDEV_PMU ] = gf100_pmu_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = gf100_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = gf100_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = gf100_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = gf100_gr_oclass; + device->oclass[NVDEV_ENGINE_MSPDEC ] = &gf100_mspdec_oclass; + device->oclass[NVDEV_ENGINE_MSVLD ] = &gf100_msvld_oclass; + device->oclass[NVDEV_ENGINE_MSPPP ] = &gf100_msppp_oclass; + device->oclass[NVDEV_ENGINE_CE0 ] = &gf100_ce0_oclass; + device->oclass[NVDEV_ENGINE_CE1 ] = &gf100_ce1_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = gt215_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = &gf100_pm_oclass; + break; + case 0xc4: + device->cname = "GF104"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = g94_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = g94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &gf100_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = >215_therm_oclass; + device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = gf100_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = gf100_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = gf100_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = gf100_fb_oclass; + device->oclass[NVDEV_SUBDEV_LTC ] = gf100_ltc_oclass; + device->oclass[NVDEV_SUBDEV_IBUS ] = &gf100_ibus_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &gf100_mmu_oclass; + device->oclass[NVDEV_SUBDEV_BAR ] = &gf100_bar_oclass; + device->oclass[NVDEV_SUBDEV_PMU ] = gf100_pmu_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = gf100_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = gf100_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = gf100_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = gf104_gr_oclass; + device->oclass[NVDEV_ENGINE_MSPDEC ] = &gf100_mspdec_oclass; + device->oclass[NVDEV_ENGINE_MSVLD ] = &gf100_msvld_oclass; + device->oclass[NVDEV_ENGINE_MSPPP ] = &gf100_msppp_oclass; + device->oclass[NVDEV_ENGINE_CE0 ] = &gf100_ce0_oclass; + device->oclass[NVDEV_ENGINE_CE1 ] = &gf100_ce1_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = gt215_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = &gf100_pm_oclass; + break; + case 0xc3: + device->cname = "GF106"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = g94_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = g94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &gf100_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = >215_therm_oclass; + device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = gf100_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = gf106_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = gf100_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = gf100_fb_oclass; + device->oclass[NVDEV_SUBDEV_LTC ] = gf100_ltc_oclass; + device->oclass[NVDEV_SUBDEV_IBUS ] = &gf100_ibus_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &gf100_mmu_oclass; + device->oclass[NVDEV_SUBDEV_BAR ] = &gf100_bar_oclass; + device->oclass[NVDEV_SUBDEV_PMU ] = gf100_pmu_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = gf100_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = gf100_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = gf100_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = gf104_gr_oclass; + device->oclass[NVDEV_ENGINE_MSPDEC ] = &gf100_mspdec_oclass; + device->oclass[NVDEV_ENGINE_MSVLD ] = &gf100_msvld_oclass; + device->oclass[NVDEV_ENGINE_MSPPP ] = &gf100_msppp_oclass; + device->oclass[NVDEV_ENGINE_CE0 ] = &gf100_ce0_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = gt215_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = &gf100_pm_oclass; + break; + case 0xce: + device->cname = "GF114"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = g94_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = g94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &gf100_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = >215_therm_oclass; + device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = gf100_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = gf100_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = gf100_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = gf100_fb_oclass; + device->oclass[NVDEV_SUBDEV_LTC ] = gf100_ltc_oclass; + device->oclass[NVDEV_SUBDEV_IBUS ] = &gf100_ibus_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &gf100_mmu_oclass; + device->oclass[NVDEV_SUBDEV_BAR ] = &gf100_bar_oclass; + device->oclass[NVDEV_SUBDEV_PMU ] = gf100_pmu_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = gf100_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = gf100_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = gf100_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = gf104_gr_oclass; + device->oclass[NVDEV_ENGINE_MSPDEC ] = &gf100_mspdec_oclass; + device->oclass[NVDEV_ENGINE_MSVLD ] = &gf100_msvld_oclass; + device->oclass[NVDEV_ENGINE_MSPPP ] = &gf100_msppp_oclass; + device->oclass[NVDEV_ENGINE_CE0 ] = &gf100_ce0_oclass; + device->oclass[NVDEV_ENGINE_CE1 ] = &gf100_ce1_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = gt215_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = &gf100_pm_oclass; + break; + case 0xcf: + device->cname = "GF116"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = g94_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = g94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &gf100_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = >215_therm_oclass; + device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = gf100_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = gf106_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = gf100_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = gf100_fb_oclass; + device->oclass[NVDEV_SUBDEV_LTC ] = gf100_ltc_oclass; + device->oclass[NVDEV_SUBDEV_IBUS ] = &gf100_ibus_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &gf100_mmu_oclass; + device->oclass[NVDEV_SUBDEV_BAR ] = &gf100_bar_oclass; + device->oclass[NVDEV_SUBDEV_PMU ] = gf100_pmu_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = gf100_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = gf100_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = gf100_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = gf104_gr_oclass; + device->oclass[NVDEV_ENGINE_MSPDEC ] = &gf100_mspdec_oclass; + device->oclass[NVDEV_ENGINE_MSVLD ] = &gf100_msvld_oclass; + device->oclass[NVDEV_ENGINE_MSPPP ] = &gf100_msppp_oclass; + device->oclass[NVDEV_ENGINE_CE0 ] = &gf100_ce0_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = gt215_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = &gf100_pm_oclass; + break; + case 0xc1: + device->cname = "GF108"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = g94_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = g94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &gf100_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = >215_therm_oclass; + device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = gf100_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = gf106_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = gf100_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = gf100_fb_oclass; + device->oclass[NVDEV_SUBDEV_LTC ] = gf100_ltc_oclass; + device->oclass[NVDEV_SUBDEV_IBUS ] = &gf100_ibus_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &gf100_mmu_oclass; + device->oclass[NVDEV_SUBDEV_BAR ] = &gf100_bar_oclass; + device->oclass[NVDEV_SUBDEV_PMU ] = gf100_pmu_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = gf100_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = gf100_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = gf100_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = gf108_gr_oclass; + device->oclass[NVDEV_ENGINE_MSPDEC ] = &gf100_mspdec_oclass; + device->oclass[NVDEV_ENGINE_MSVLD ] = &gf100_msvld_oclass; + device->oclass[NVDEV_ENGINE_MSPPP ] = &gf100_msppp_oclass; + device->oclass[NVDEV_ENGINE_CE0 ] = &gf100_ce0_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = gt215_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = &gf100_pm_oclass; + break; + case 0xc8: + device->cname = "GF110"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = g94_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = g94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &gf100_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = >215_therm_oclass; + device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = gf100_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = gf100_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = gf100_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = gf100_fb_oclass; + device->oclass[NVDEV_SUBDEV_LTC ] = gf100_ltc_oclass; + device->oclass[NVDEV_SUBDEV_IBUS ] = &gf100_ibus_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &gf100_mmu_oclass; + device->oclass[NVDEV_SUBDEV_BAR ] = &gf100_bar_oclass; + device->oclass[NVDEV_SUBDEV_PMU ] = gf100_pmu_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = gf100_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = gf100_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = gf100_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = gf110_gr_oclass; + device->oclass[NVDEV_ENGINE_MSPDEC ] = &gf100_mspdec_oclass; + device->oclass[NVDEV_ENGINE_MSVLD ] = &gf100_msvld_oclass; + device->oclass[NVDEV_ENGINE_MSPPP ] = &gf100_msppp_oclass; + device->oclass[NVDEV_ENGINE_CE0 ] = &gf100_ce0_oclass; + device->oclass[NVDEV_ENGINE_CE1 ] = &gf100_ce1_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = gt215_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = &gf100_pm_oclass; + break; + case 0xd9: + device->cname = "GF119"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = gf110_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = gf110_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &gf100_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &gf110_therm_oclass; + device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = gf100_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = gf106_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = gf100_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = gf100_fb_oclass; + device->oclass[NVDEV_SUBDEV_LTC ] = gf100_ltc_oclass; + device->oclass[NVDEV_SUBDEV_IBUS ] = &gf100_ibus_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &gf100_mmu_oclass; + device->oclass[NVDEV_SUBDEV_BAR ] = &gf100_bar_oclass; + device->oclass[NVDEV_SUBDEV_PMU ] = gf110_pmu_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = gf110_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = gf100_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = gf100_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = gf119_gr_oclass; + device->oclass[NVDEV_ENGINE_MSPDEC ] = &gf100_mspdec_oclass; + device->oclass[NVDEV_ENGINE_MSVLD ] = &gf100_msvld_oclass; + device->oclass[NVDEV_ENGINE_MSPPP ] = &gf100_msppp_oclass; + device->oclass[NVDEV_ENGINE_CE0 ] = &gf100_ce0_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = gf110_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = &gf100_pm_oclass; + break; + case 0xd7: + device->cname = "GF117"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = gf110_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = gf117_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &gf100_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &gf110_therm_oclass; + device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = gf100_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = gf106_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = gf100_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = gf100_fb_oclass; + device->oclass[NVDEV_SUBDEV_LTC ] = gf100_ltc_oclass; + device->oclass[NVDEV_SUBDEV_IBUS ] = &gf100_ibus_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &gf100_mmu_oclass; + device->oclass[NVDEV_SUBDEV_BAR ] = &gf100_bar_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = gf110_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = gf100_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = gf100_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = gf117_gr_oclass; + device->oclass[NVDEV_ENGINE_MSPDEC ] = &gf100_mspdec_oclass; + device->oclass[NVDEV_ENGINE_MSVLD ] = &gf100_msvld_oclass; + device->oclass[NVDEV_ENGINE_MSPPP ] = &gf100_msppp_oclass; + device->oclass[NVDEV_ENGINE_CE0 ] = &gf100_ce0_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = gf110_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = &gf100_pm_oclass; + break; + default: + nv_fatal(device, "unknown Fermi chipset\n"); + return -EINVAL; + } + + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/gk104.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/gk104.c new file mode 100644 index 000000000..6a9483f65 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/gk104.c @@ -0,0 +1,326 @@ +/* + * 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 +#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 +gk104_identify(struct nvkm_device *device) +{ + switch (device->chipset) { + case 0xe4: + device->cname = "GK104"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = gk104_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = gk104_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &gk104_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &gf110_therm_oclass; + device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = gf100_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = gf106_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = gf100_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = gk104_fb_oclass; + device->oclass[NVDEV_SUBDEV_LTC ] = gk104_ltc_oclass; + device->oclass[NVDEV_SUBDEV_IBUS ] = &gk104_ibus_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &gf100_mmu_oclass; + device->oclass[NVDEV_SUBDEV_BAR ] = &gf100_bar_oclass; + device->oclass[NVDEV_SUBDEV_PMU ] = gk104_pmu_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = gf110_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = gk104_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = gf100_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = gk104_gr_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = gk104_disp_oclass; + device->oclass[NVDEV_ENGINE_CE0 ] = &gk104_ce0_oclass; + device->oclass[NVDEV_ENGINE_CE1 ] = &gk104_ce1_oclass; + device->oclass[NVDEV_ENGINE_CE2 ] = &gk104_ce2_oclass; + device->oclass[NVDEV_ENGINE_MSVLD ] = &gk104_msvld_oclass; + device->oclass[NVDEV_ENGINE_MSPDEC ] = &gk104_mspdec_oclass; + device->oclass[NVDEV_ENGINE_MSPPP ] = &gf100_msppp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = &gk104_pm_oclass; + break; + case 0xe7: + device->cname = "GK107"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = gk104_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = gk104_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &gk104_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &gf110_therm_oclass; + device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = gf100_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = gf106_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = gf100_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = gk104_fb_oclass; + device->oclass[NVDEV_SUBDEV_LTC ] = gk104_ltc_oclass; + device->oclass[NVDEV_SUBDEV_IBUS ] = &gk104_ibus_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &gf100_mmu_oclass; + device->oclass[NVDEV_SUBDEV_BAR ] = &gf100_bar_oclass; + device->oclass[NVDEV_SUBDEV_PMU ] = gf110_pmu_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = gf110_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = gk104_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = gf100_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = gk104_gr_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = gk104_disp_oclass; + device->oclass[NVDEV_ENGINE_CE0 ] = &gk104_ce0_oclass; + device->oclass[NVDEV_ENGINE_CE1 ] = &gk104_ce1_oclass; + device->oclass[NVDEV_ENGINE_CE2 ] = &gk104_ce2_oclass; + device->oclass[NVDEV_ENGINE_MSVLD ] = &gk104_msvld_oclass; + device->oclass[NVDEV_ENGINE_MSPDEC ] = &gk104_mspdec_oclass; + device->oclass[NVDEV_ENGINE_MSPPP ] = &gf100_msppp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = &gk104_pm_oclass; + break; + case 0xe6: + device->cname = "GK106"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = gk104_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = gk104_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &gk104_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &gf110_therm_oclass; + device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = gf100_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = gf106_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = gf100_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = gk104_fb_oclass; + device->oclass[NVDEV_SUBDEV_LTC ] = gk104_ltc_oclass; + device->oclass[NVDEV_SUBDEV_IBUS ] = &gk104_ibus_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &gf100_mmu_oclass; + device->oclass[NVDEV_SUBDEV_BAR ] = &gf100_bar_oclass; + device->oclass[NVDEV_SUBDEV_PMU ] = gk104_pmu_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = gf110_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = gk104_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = gf100_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = gk104_gr_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = gk104_disp_oclass; + device->oclass[NVDEV_ENGINE_CE0 ] = &gk104_ce0_oclass; + device->oclass[NVDEV_ENGINE_CE1 ] = &gk104_ce1_oclass; + device->oclass[NVDEV_ENGINE_CE2 ] = &gk104_ce2_oclass; + device->oclass[NVDEV_ENGINE_MSVLD ] = &gk104_msvld_oclass; + device->oclass[NVDEV_ENGINE_MSPDEC ] = &gk104_mspdec_oclass; + device->oclass[NVDEV_ENGINE_MSPPP ] = &gf100_msppp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = &gk104_pm_oclass; + break; + case 0xea: + device->cname = "GK20A"; + device->oclass[NVDEV_SUBDEV_CLK ] = &gk20a_clk_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = gk20a_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = gf100_bus_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &gk20a_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = gk20a_fb_oclass; + device->oclass[NVDEV_SUBDEV_LTC ] = gk104_ltc_oclass; + device->oclass[NVDEV_SUBDEV_IBUS ] = &gk20a_ibus_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = gk20a_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &gf100_mmu_oclass; + device->oclass[NVDEV_SUBDEV_BAR ] = &gk20a_bar_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = gf110_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = gk20a_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = gf100_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = gk20a_gr_oclass; + device->oclass[NVDEV_ENGINE_CE2 ] = &gk104_ce2_oclass; + device->oclass[NVDEV_ENGINE_PM ] = &gk104_pm_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &gk20a_volt_oclass; + device->oclass[NVDEV_SUBDEV_PMU ] = gk20a_pmu_oclass; + break; + case 0xf0: + device->cname = "GK110"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = gk104_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = gk104_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &gk104_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &gf110_therm_oclass; + device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = gf100_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = gf106_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = gf100_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = gk104_fb_oclass; + device->oclass[NVDEV_SUBDEV_LTC ] = gk104_ltc_oclass; + device->oclass[NVDEV_SUBDEV_IBUS ] = &gk104_ibus_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &gf100_mmu_oclass; + device->oclass[NVDEV_SUBDEV_BAR ] = &gf100_bar_oclass; + device->oclass[NVDEV_SUBDEV_PMU ] = gk110_pmu_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = gf110_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = gk104_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = gf100_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = gk110_gr_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = gk110_disp_oclass; + device->oclass[NVDEV_ENGINE_CE0 ] = &gk104_ce0_oclass; + device->oclass[NVDEV_ENGINE_CE1 ] = &gk104_ce1_oclass; + device->oclass[NVDEV_ENGINE_CE2 ] = &gk104_ce2_oclass; + device->oclass[NVDEV_ENGINE_MSVLD ] = &gk104_msvld_oclass; + device->oclass[NVDEV_ENGINE_MSPDEC ] = &gk104_mspdec_oclass; + device->oclass[NVDEV_ENGINE_MSPPP ] = &gf100_msppp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = &gk110_pm_oclass; + break; + case 0xf1: + device->cname = "GK110B"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = gk104_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = gf110_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &gk104_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &gf110_therm_oclass; + device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = gf100_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = gf106_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = gf100_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = gk104_fb_oclass; + device->oclass[NVDEV_SUBDEV_LTC ] = gk104_ltc_oclass; + device->oclass[NVDEV_SUBDEV_IBUS ] = &gk104_ibus_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &gf100_mmu_oclass; + device->oclass[NVDEV_SUBDEV_BAR ] = &gf100_bar_oclass; + device->oclass[NVDEV_SUBDEV_PMU ] = gk110_pmu_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = gf110_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = gk104_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = gf100_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = gk110b_gr_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = gk110_disp_oclass; + device->oclass[NVDEV_ENGINE_CE0 ] = &gk104_ce0_oclass; + device->oclass[NVDEV_ENGINE_CE1 ] = &gk104_ce1_oclass; + device->oclass[NVDEV_ENGINE_CE2 ] = &gk104_ce2_oclass; + device->oclass[NVDEV_ENGINE_MSVLD ] = &gk104_msvld_oclass; + device->oclass[NVDEV_ENGINE_MSPDEC ] = &gk104_mspdec_oclass; + device->oclass[NVDEV_ENGINE_MSPPP ] = &gf100_msppp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = &gk110_pm_oclass; + break; + case 0x106: + device->cname = "GK208B"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = gk104_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = gk104_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &gk104_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &gf110_therm_oclass; + device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = gf100_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = gk20a_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = gf100_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = gk104_fb_oclass; + device->oclass[NVDEV_SUBDEV_LTC ] = gk104_ltc_oclass; + device->oclass[NVDEV_SUBDEV_IBUS ] = &gk104_ibus_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &gf100_mmu_oclass; + device->oclass[NVDEV_SUBDEV_BAR ] = &gf100_bar_oclass; + device->oclass[NVDEV_SUBDEV_PMU ] = gk208_pmu_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = gf110_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = gk208_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = gf100_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = gk208_gr_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = gk110_disp_oclass; + device->oclass[NVDEV_ENGINE_CE0 ] = &gk104_ce0_oclass; + device->oclass[NVDEV_ENGINE_CE1 ] = &gk104_ce1_oclass; + device->oclass[NVDEV_ENGINE_CE2 ] = &gk104_ce2_oclass; + device->oclass[NVDEV_ENGINE_MSVLD ] = &gk104_msvld_oclass; + device->oclass[NVDEV_ENGINE_MSPDEC ] = &gk104_mspdec_oclass; + device->oclass[NVDEV_ENGINE_MSPPP ] = &gf100_msppp_oclass; + break; + case 0x108: + device->cname = "GK208"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = gk104_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = gk104_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &gk104_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &gf110_therm_oclass; + device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = gf100_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = gk20a_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = gf100_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = gk104_fb_oclass; + device->oclass[NVDEV_SUBDEV_LTC ] = gk104_ltc_oclass; + device->oclass[NVDEV_SUBDEV_IBUS ] = &gk104_ibus_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &gf100_mmu_oclass; + device->oclass[NVDEV_SUBDEV_BAR ] = &gf100_bar_oclass; + device->oclass[NVDEV_SUBDEV_PMU ] = gk208_pmu_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = gf110_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = gk208_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = gf100_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = gk208_gr_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = gk110_disp_oclass; + device->oclass[NVDEV_ENGINE_CE0 ] = &gk104_ce0_oclass; + device->oclass[NVDEV_ENGINE_CE1 ] = &gk104_ce1_oclass; + device->oclass[NVDEV_ENGINE_CE2 ] = &gk104_ce2_oclass; + device->oclass[NVDEV_ENGINE_MSVLD ] = &gk104_msvld_oclass; + device->oclass[NVDEV_ENGINE_MSPDEC ] = &gk104_mspdec_oclass; + device->oclass[NVDEV_ENGINE_MSPPP ] = &gf100_msppp_oclass; + break; + default: + nv_fatal(device, "unknown Kepler chipset\n"); + return -EINVAL; + } + + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/gm100.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/gm100.c new file mode 100644 index 000000000..70abf1ec7 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/gm100.c @@ -0,0 +1,190 @@ +/* + * 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 +#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 +gm100_identify(struct nvkm_device *device) +{ + switch (device->chipset) { + case 0x117: + device->cname = "GM107"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = gk104_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = gf110_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gm107_fuse_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &gk104_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &gm107_therm_oclass; + device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = gm107_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = gk20a_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = gf100_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &gk20a_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = gm107_fb_oclass; + device->oclass[NVDEV_SUBDEV_LTC ] = gm107_ltc_oclass; + device->oclass[NVDEV_SUBDEV_IBUS ] = &gk104_ibus_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &gf100_mmu_oclass; + device->oclass[NVDEV_SUBDEV_BAR ] = &gf100_bar_oclass; + device->oclass[NVDEV_SUBDEV_PMU ] = gk208_pmu_oclass; + +#if 0 + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; +#endif + device->oclass[NVDEV_ENGINE_DMAOBJ ] = gf110_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = gk208_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = gf100_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = gm107_gr_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = gm107_disp_oclass; + device->oclass[NVDEV_ENGINE_CE0 ] = &gk104_ce0_oclass; +#if 0 + device->oclass[NVDEV_ENGINE_CE1 ] = &gk104_ce1_oclass; +#endif + device->oclass[NVDEV_ENGINE_CE2 ] = &gk104_ce2_oclass; +#if 0 + device->oclass[NVDEV_ENGINE_MSVLD ] = &gk104_msvld_oclass; + device->oclass[NVDEV_ENGINE_MSPDEC ] = &gk104_mspdec_oclass; + device->oclass[NVDEV_ENGINE_MSPPP ] = &gf100_msppp_oclass; +#endif + break; + case 0x124: + device->cname = "GM204"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = gk104_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = gm204_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gm107_fuse_oclass; +#if 0 + /* looks to be some non-trivial changes */ + device->oclass[NVDEV_SUBDEV_CLK ] = &gk104_clk_oclass; + /* priv ring says no to 0x10eb14 writes */ + device->oclass[NVDEV_SUBDEV_THERM ] = &gm107_therm_oclass; +#endif + device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = gm204_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = gk20a_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = gf100_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &gk20a_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = gm107_fb_oclass; + device->oclass[NVDEV_SUBDEV_LTC ] = gm107_ltc_oclass; + device->oclass[NVDEV_SUBDEV_IBUS ] = &gk104_ibus_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &gf100_mmu_oclass; + device->oclass[NVDEV_SUBDEV_BAR ] = &gf100_bar_oclass; + device->oclass[NVDEV_SUBDEV_PMU ] = gk208_pmu_oclass; +#if 0 + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; +#endif + device->oclass[NVDEV_ENGINE_DMAOBJ ] = gf110_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = gm204_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = gf100_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = gm204_gr_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = gm204_disp_oclass; + device->oclass[NVDEV_ENGINE_CE0 ] = &gm204_ce0_oclass; + device->oclass[NVDEV_ENGINE_CE1 ] = &gm204_ce1_oclass; + device->oclass[NVDEV_ENGINE_CE2 ] = &gm204_ce2_oclass; +#if 0 + device->oclass[NVDEV_ENGINE_MSVLD ] = &gk104_msvld_oclass; + device->oclass[NVDEV_ENGINE_MSPDEC ] = &gk104_mspdec_oclass; + device->oclass[NVDEV_ENGINE_MSPPP ] = &gf100_msppp_oclass; +#endif + break; + case 0x126: + device->cname = "GM206"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = gk104_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = gm204_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gm107_fuse_oclass; +#if 0 + /* looks to be some non-trivial changes */ + device->oclass[NVDEV_SUBDEV_CLK ] = &gk104_clk_oclass; + /* priv ring says no to 0x10eb14 writes */ + device->oclass[NVDEV_SUBDEV_THERM ] = &gm107_therm_oclass; +#endif + device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = gm204_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = gk20a_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = gf100_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &gk20a_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = gm107_fb_oclass; + device->oclass[NVDEV_SUBDEV_LTC ] = gm107_ltc_oclass; + device->oclass[NVDEV_SUBDEV_IBUS ] = &gk104_ibus_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &gf100_mmu_oclass; + device->oclass[NVDEV_SUBDEV_BAR ] = &gf100_bar_oclass; + device->oclass[NVDEV_SUBDEV_PMU ] = gk208_pmu_oclass; +#if 0 + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; +#endif + device->oclass[NVDEV_ENGINE_DMAOBJ ] = gf110_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = gm204_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = gf100_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = gm206_gr_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = gm204_disp_oclass; + device->oclass[NVDEV_ENGINE_CE0 ] = &gm204_ce0_oclass; + device->oclass[NVDEV_ENGINE_CE1 ] = &gm204_ce1_oclass; + device->oclass[NVDEV_ENGINE_CE2 ] = &gm204_ce2_oclass; +#if 0 + device->oclass[NVDEV_ENGINE_MSVLD ] = &gk104_msvld_oclass; + device->oclass[NVDEV_ENGINE_MSPDEC ] = &gk104_mspdec_oclass; + device->oclass[NVDEV_ENGINE_MSPPP ] = &gf100_msppp_oclass; +#endif + break; + default: + nv_fatal(device, "unknown Maxwell chipset\n"); + return -EINVAL; + } + + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/nv04.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/nv04.c new file mode 100644 index 000000000..5a2ae043b --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/nv04.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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +int +nv04_identify(struct nvkm_device *device) +{ + switch (device->chipset) { + case 0x04: + device->cname = "NV04"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &nv04_clk_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nv04_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv04_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nv04_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv04_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv04_mmu_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = nv04_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv04_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv04_gr_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass; + break; + case 0x05: + device->cname = "NV05"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &nv04_clk_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nv05_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv04_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nv04_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv04_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv04_mmu_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = nv04_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv04_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv04_gr_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass; + break; + default: + nv_fatal(device, "unknown RIVA chipset\n"); + return -EINVAL; + } + + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/nv10.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/nv10.c new file mode 100644 index 000000000..94a1ca45e --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/nv10.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 "priv.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +int +nv10_identify(struct nvkm_device *device) +{ + switch (device->chipset) { + case 0x10: + device->cname = "NV10"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &nv04_clk_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nv10_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv04_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nv10_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv04_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv04_mmu_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv10_gr_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass; + break; + case 0x15: + device->cname = "NV15"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &nv04_clk_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nv10_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv04_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nv10_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv04_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv04_mmu_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = nv10_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv10_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv10_gr_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass; + break; + case 0x16: + device->cname = "NV16"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &nv04_clk_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nv10_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv04_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nv10_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv04_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv04_mmu_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = nv10_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv10_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv10_gr_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass; + break; + case 0x1a: + device->cname = "nForce"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &nv04_clk_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv04_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nv1a_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv04_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv04_mmu_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = nv10_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv10_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv10_gr_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass; + break; + case 0x11: + device->cname = "NV11"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &nv04_clk_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nv10_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv04_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nv10_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv04_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv04_mmu_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = nv10_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv10_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv10_gr_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass; + break; + case 0x17: + device->cname = "NV17"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &nv04_clk_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nv10_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv04_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nv10_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv04_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv04_mmu_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = nv17_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv10_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv10_gr_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass; + break; + case 0x1f: + device->cname = "nForce2"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &nv04_clk_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv04_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nv1a_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv04_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv04_mmu_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = nv17_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv10_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv10_gr_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass; + break; + case 0x18: + device->cname = "NV18"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &nv04_clk_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nv10_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv04_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nv10_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv04_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv04_mmu_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = nv17_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv10_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv10_gr_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass; + break; + default: + nv_fatal(device, "unknown Celsius chipset\n"); + return -EINVAL; + } + + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/nv20.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/nv20.c new file mode 100644 index 000000000..d5ec8937d --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/nv20.c @@ -0,0 +1,131 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +int +nv20_identify(struct nvkm_device *device) +{ + switch (device->chipset) { + case 0x20: + device->cname = "NV20"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &nv04_clk_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nv20_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv04_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nv20_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv04_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv04_mmu_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = nv17_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv10_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv20_gr_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass; + break; + case 0x25: + device->cname = "NV25"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &nv04_clk_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nv20_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv04_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nv25_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv04_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv04_mmu_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = nv17_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv10_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv25_gr_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass; + break; + case 0x28: + device->cname = "NV28"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &nv04_clk_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nv20_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv04_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nv25_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv04_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv04_mmu_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = nv17_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv10_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv25_gr_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass; + break; + case 0x2a: + device->cname = "NV2A"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &nv04_clk_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nv20_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv04_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nv25_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv04_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv04_mmu_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = nv17_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv10_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv2a_gr_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass; + break; + default: + nv_fatal(device, "unknown Kelvin chipset\n"); + return -EINVAL; + } + + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/nv30.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/nv30.c new file mode 100644 index 000000000..dda09621e --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/nv30.c @@ -0,0 +1,153 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +int +nv30_identify(struct nvkm_device *device) +{ + switch (device->chipset) { + case 0x30: + device->cname = "NV30"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &nv04_clk_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nv20_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv04_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nv30_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv04_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv04_mmu_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = nv17_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv10_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv30_gr_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass; + break; + case 0x35: + device->cname = "NV35"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &nv04_clk_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nv20_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv04_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nv35_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv04_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv04_mmu_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = nv17_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv10_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv35_gr_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass; + break; + case 0x31: + device->cname = "NV31"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &nv04_clk_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nv20_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv31_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nv30_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv04_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv04_mmu_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = nv17_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv10_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv30_gr_oclass; + device->oclass[NVDEV_ENGINE_MPEG ] = &nv31_mpeg_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass; + break; + case 0x36: + device->cname = "NV36"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &nv04_clk_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nv20_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv31_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nv36_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv04_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv04_mmu_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = nv17_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv10_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv35_gr_oclass; + device->oclass[NVDEV_ENGINE_MPEG ] = &nv31_mpeg_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass; + break; + case 0x34: + device->cname = "NV34"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &nv04_clk_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nv10_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv31_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nv10_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv04_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv04_mmu_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = nv17_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv10_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv34_gr_oclass; + device->oclass[NVDEV_ENGINE_MPEG ] = &nv31_mpeg_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass; + break; + default: + nv_fatal(device, "unknown Rankine chipset\n"); + return -EINVAL; + } + + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/nv40.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/nv40.c new file mode 100644 index 000000000..c6301361d --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/nv40.c @@ -0,0 +1,427 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +int +nv40_identify(struct nvkm_device *device) +{ + switch (device->chipset) { + case 0x40: + device->cname = "NV40"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &nv40_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv40_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv31_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nv40_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv40_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv04_mmu_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = nv40_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv10_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv40_gr_oclass; + device->oclass[NVDEV_ENGINE_MPEG ] = &nv40_mpeg_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = nv40_pm_oclass; + break; + case 0x41: + device->cname = "NV41"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &nv40_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv40_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv31_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nv41_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv40_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv41_mmu_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = nv40_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv10_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv40_gr_oclass; + device->oclass[NVDEV_ENGINE_MPEG ] = &nv40_mpeg_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = nv40_pm_oclass; + break; + case 0x42: + device->cname = "NV42"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &nv40_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv40_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv31_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nv41_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv40_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv41_mmu_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = nv40_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv10_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv40_gr_oclass; + device->oclass[NVDEV_ENGINE_MPEG ] = &nv40_mpeg_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = nv40_pm_oclass; + break; + case 0x43: + device->cname = "NV43"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &nv40_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv40_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv31_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nv41_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv40_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv41_mmu_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = nv40_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv10_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv40_gr_oclass; + device->oclass[NVDEV_ENGINE_MPEG ] = &nv40_mpeg_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = nv40_pm_oclass; + break; + case 0x45: + device->cname = "NV45"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &nv40_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv40_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv31_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nv40_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv40_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv04_mmu_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = nv40_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv10_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv40_gr_oclass; + device->oclass[NVDEV_ENGINE_MPEG ] = &nv44_mpeg_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = nv40_pm_oclass; + break; + case 0x47: + device->cname = "G70"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &nv40_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv40_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv31_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nv47_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv40_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv41_mmu_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = nv40_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv10_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv40_gr_oclass; + device->oclass[NVDEV_ENGINE_MPEG ] = &nv44_mpeg_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = nv40_pm_oclass; + break; + case 0x49: + device->cname = "G71"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &nv40_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv40_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv31_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nv49_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv40_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv41_mmu_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = nv40_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv10_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv40_gr_oclass; + device->oclass[NVDEV_ENGINE_MPEG ] = &nv44_mpeg_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = nv40_pm_oclass; + break; + case 0x4b: + device->cname = "G73"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &nv40_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv40_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv31_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nv49_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv40_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv41_mmu_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = nv40_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv10_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv40_gr_oclass; + device->oclass[NVDEV_ENGINE_MPEG ] = &nv44_mpeg_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = nv40_pm_oclass; + break; + case 0x44: + device->cname = "NV44"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &nv40_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv44_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv31_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nv44_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv40_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv44_mmu_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = nv40_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv10_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv40_gr_oclass; + device->oclass[NVDEV_ENGINE_MPEG ] = &nv44_mpeg_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = nv40_pm_oclass; + break; + case 0x46: + device->cname = "G72"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &nv40_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv44_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv31_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nv46_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv40_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv44_mmu_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = nv40_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv10_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv40_gr_oclass; + device->oclass[NVDEV_ENGINE_MPEG ] = &nv44_mpeg_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = nv40_pm_oclass; + break; + case 0x4a: + device->cname = "NV44A"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &nv40_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv44_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv31_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nv44_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv40_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv44_mmu_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = nv40_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv10_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv40_gr_oclass; + device->oclass[NVDEV_ENGINE_MPEG ] = &nv44_mpeg_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = nv40_pm_oclass; + break; + case 0x4c: + device->cname = "C61"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &nv40_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv4c_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv31_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nv46_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv40_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv44_mmu_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = nv40_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv10_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv40_gr_oclass; + device->oclass[NVDEV_ENGINE_MPEG ] = &nv44_mpeg_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = nv40_pm_oclass; + break; + case 0x4e: + device->cname = "C51"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv4e_i2c_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &nv40_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv4c_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv31_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nv4e_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv40_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv44_mmu_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = nv40_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv10_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv40_gr_oclass; + device->oclass[NVDEV_ENGINE_MPEG ] = &nv44_mpeg_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = nv40_pm_oclass; + break; + case 0x63: + device->cname = "C73"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &nv40_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv4c_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv31_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nv46_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv40_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv44_mmu_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = nv40_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv10_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv40_gr_oclass; + device->oclass[NVDEV_ENGINE_MPEG ] = &nv44_mpeg_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = nv40_pm_oclass; + break; + case 0x67: + device->cname = "C67"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &nv40_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv4c_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv31_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nv46_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv40_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv44_mmu_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = nv40_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv10_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv40_gr_oclass; + device->oclass[NVDEV_ENGINE_MPEG ] = &nv44_mpeg_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = nv40_pm_oclass; + break; + case 0x68: + device->cname = "C68"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = &nv40_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv4c_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv31_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nv46_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv40_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv44_mmu_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = nv40_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv10_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv40_gr_oclass; + device->oclass[NVDEV_ENGINE_MPEG ] = &nv44_mpeg_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = nv40_pm_oclass; + break; + default: + nv_fatal(device, "unknown Curie chipset\n"); + return -EINVAL; + } + + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/nv50.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/nv50.c new file mode 100644 index 000000000..249b84454 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/nv50.c @@ -0,0 +1,478 @@ +/* + * 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 +#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 +nv50_identify(struct nvkm_device *device) +{ + switch (device->chipset) { + case 0x50: + device->cname = "G80"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv50_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &nv50_fuse_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = nv50_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &nv50_therm_oclass; + device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nv50_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv50_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv50_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nv50_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv50_mmu_oclass; + device->oclass[NVDEV_SUBDEV_BAR ] = &nv50_bar_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv50_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = nv50_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv50_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv50_gr_oclass; + device->oclass[NVDEV_ENGINE_MPEG ] = &nv50_mpeg_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = nv50_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = nv50_pm_oclass; + break; + case 0x84: + device->cname = "G84"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv50_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &nv50_fuse_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = g84_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &g84_therm_oclass; + device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = g84_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv50_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv50_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = g84_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv50_mmu_oclass; + device->oclass[NVDEV_SUBDEV_BAR ] = &nv50_bar_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv50_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = g84_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv50_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv50_gr_oclass; + device->oclass[NVDEV_ENGINE_MPEG ] = &g84_mpeg_oclass; + device->oclass[NVDEV_ENGINE_VP ] = &g84_vp_oclass; + device->oclass[NVDEV_ENGINE_CIPHER ] = &g84_cipher_oclass; + device->oclass[NVDEV_ENGINE_BSP ] = &g84_bsp_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = g84_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = g84_pm_oclass; + break; + case 0x86: + device->cname = "G86"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv50_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &nv50_fuse_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = g84_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &g84_therm_oclass; + device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = g84_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv50_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv50_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = g84_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv50_mmu_oclass; + device->oclass[NVDEV_SUBDEV_BAR ] = &nv50_bar_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv50_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = g84_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv50_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv50_gr_oclass; + device->oclass[NVDEV_ENGINE_MPEG ] = &g84_mpeg_oclass; + device->oclass[NVDEV_ENGINE_VP ] = &g84_vp_oclass; + device->oclass[NVDEV_ENGINE_CIPHER ] = &g84_cipher_oclass; + device->oclass[NVDEV_ENGINE_BSP ] = &g84_bsp_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = g84_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = g84_pm_oclass; + break; + case 0x92: + device->cname = "G92"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv50_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &nv50_fuse_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = g84_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &g84_therm_oclass; + device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = g84_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nv50_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv50_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = g84_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv50_mmu_oclass; + device->oclass[NVDEV_SUBDEV_BAR ] = &nv50_bar_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv50_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = g84_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv50_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv50_gr_oclass; + device->oclass[NVDEV_ENGINE_MPEG ] = &g84_mpeg_oclass; + device->oclass[NVDEV_ENGINE_VP ] = &g84_vp_oclass; + device->oclass[NVDEV_ENGINE_CIPHER ] = &g84_cipher_oclass; + device->oclass[NVDEV_ENGINE_BSP ] = &g84_bsp_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = g84_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = g84_pm_oclass; + break; + case 0x94: + device->cname = "G94"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = g94_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = g94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &nv50_fuse_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = g84_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &g84_therm_oclass; + device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = g84_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = g94_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = g94_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = g84_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv50_mmu_oclass; + device->oclass[NVDEV_SUBDEV_BAR ] = &nv50_bar_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv50_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = g84_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv50_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv50_gr_oclass; + device->oclass[NVDEV_ENGINE_MPEG ] = &g84_mpeg_oclass; + device->oclass[NVDEV_ENGINE_VP ] = &g84_vp_oclass; + device->oclass[NVDEV_ENGINE_CIPHER ] = &g84_cipher_oclass; + device->oclass[NVDEV_ENGINE_BSP ] = &g84_bsp_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = g94_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = g84_pm_oclass; + break; + case 0x96: + device->cname = "G96"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = g94_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = g94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &nv50_fuse_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = g84_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &g84_therm_oclass; + device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = g84_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = g94_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = g94_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = g84_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv50_mmu_oclass; + device->oclass[NVDEV_SUBDEV_BAR ] = &nv50_bar_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv50_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = g84_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv50_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv50_gr_oclass; + device->oclass[NVDEV_ENGINE_MPEG ] = &g84_mpeg_oclass; + device->oclass[NVDEV_ENGINE_VP ] = &g84_vp_oclass; + device->oclass[NVDEV_ENGINE_CIPHER ] = &g84_cipher_oclass; + device->oclass[NVDEV_ENGINE_BSP ] = &g84_bsp_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = g94_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = g84_pm_oclass; + break; + case 0x98: + device->cname = "G98"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = g94_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = g94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &nv50_fuse_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = g84_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &g84_therm_oclass; + device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = g98_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = g98_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = g94_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = g84_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv50_mmu_oclass; + device->oclass[NVDEV_SUBDEV_BAR ] = &nv50_bar_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv50_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = g84_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv50_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv50_gr_oclass; + device->oclass[NVDEV_ENGINE_MSPDEC ] = &g98_mspdec_oclass; + device->oclass[NVDEV_ENGINE_SEC ] = &g98_sec_oclass; + device->oclass[NVDEV_ENGINE_MSVLD ] = &g98_msvld_oclass; + device->oclass[NVDEV_ENGINE_MSPPP ] = &g98_msppp_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = g94_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = g84_pm_oclass; + break; + case 0xa0: + device->cname = "G200"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = g94_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &nv50_fuse_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = g84_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &g84_therm_oclass; + device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = g84_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = g98_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = g94_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = g84_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv50_mmu_oclass; + device->oclass[NVDEV_SUBDEV_BAR ] = &nv50_bar_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv50_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = g84_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv50_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv50_gr_oclass; + device->oclass[NVDEV_ENGINE_MPEG ] = &g84_mpeg_oclass; + device->oclass[NVDEV_ENGINE_VP ] = &g84_vp_oclass; + device->oclass[NVDEV_ENGINE_CIPHER ] = &g84_cipher_oclass; + device->oclass[NVDEV_ENGINE_BSP ] = &g84_bsp_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = gt200_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = g84_pm_oclass; + break; + case 0xaa: + device->cname = "MCP77/MCP78"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = g94_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = g94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &nv50_fuse_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = mcp77_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &g84_therm_oclass; + device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = g98_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = g98_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = g94_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = mcp77_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv50_mmu_oclass; + device->oclass[NVDEV_SUBDEV_BAR ] = &nv50_bar_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv50_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = g84_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv50_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv50_gr_oclass; + device->oclass[NVDEV_ENGINE_MSPDEC ] = &g98_mspdec_oclass; + device->oclass[NVDEV_ENGINE_SEC ] = &g98_sec_oclass; + device->oclass[NVDEV_ENGINE_MSVLD ] = &g98_msvld_oclass; + device->oclass[NVDEV_ENGINE_MSPPP ] = &g98_msppp_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = g94_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = g84_pm_oclass; + break; + case 0xac: + device->cname = "MCP79/MCP7A"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = g94_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = g94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &nv50_fuse_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = mcp77_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &g84_therm_oclass; + device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = g98_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = g98_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = g94_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = mcp77_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv50_mmu_oclass; + device->oclass[NVDEV_SUBDEV_BAR ] = &nv50_bar_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv50_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = g84_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv50_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv50_gr_oclass; + device->oclass[NVDEV_ENGINE_MSPDEC ] = &g98_mspdec_oclass; + device->oclass[NVDEV_ENGINE_SEC ] = &g98_sec_oclass; + device->oclass[NVDEV_ENGINE_MSVLD ] = &g98_msvld_oclass; + device->oclass[NVDEV_ENGINE_MSPPP ] = &g98_msppp_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = g94_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = g84_pm_oclass; + break; + case 0xa3: + device->cname = "GT215"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = g94_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = g94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &nv50_fuse_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = >215_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = >215_therm_oclass; + device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = gt215_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = g98_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = g94_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = gt215_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv50_mmu_oclass; + device->oclass[NVDEV_SUBDEV_BAR ] = &nv50_bar_oclass; + device->oclass[NVDEV_SUBDEV_PMU ] = gt215_pmu_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv50_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = g84_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv50_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv50_gr_oclass; + device->oclass[NVDEV_ENGINE_MPEG ] = &g84_mpeg_oclass; + device->oclass[NVDEV_ENGINE_MSPDEC ] = &g98_mspdec_oclass; + device->oclass[NVDEV_ENGINE_MSVLD ] = &g98_msvld_oclass; + device->oclass[NVDEV_ENGINE_MSPPP ] = &g98_msppp_oclass; + device->oclass[NVDEV_ENGINE_CE0 ] = >215_ce_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = gt215_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = gt215_pm_oclass; + break; + case 0xa5: + device->cname = "GT216"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = g94_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = g94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &nv50_fuse_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = >215_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = >215_therm_oclass; + device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = gt215_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = g98_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = g94_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = gt215_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv50_mmu_oclass; + device->oclass[NVDEV_SUBDEV_BAR ] = &nv50_bar_oclass; + device->oclass[NVDEV_SUBDEV_PMU ] = gt215_pmu_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv50_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = g84_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv50_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv50_gr_oclass; + device->oclass[NVDEV_ENGINE_MSPDEC ] = &g98_mspdec_oclass; + device->oclass[NVDEV_ENGINE_MSVLD ] = &g98_msvld_oclass; + device->oclass[NVDEV_ENGINE_MSPPP ] = &g98_msppp_oclass; + device->oclass[NVDEV_ENGINE_CE0 ] = >215_ce_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = gt215_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = gt215_pm_oclass; + break; + case 0xa8: + device->cname = "GT218"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = g94_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = g94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &nv50_fuse_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = >215_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = >215_therm_oclass; + device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = gt215_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = g98_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = g94_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = gt215_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv50_mmu_oclass; + device->oclass[NVDEV_SUBDEV_BAR ] = &nv50_bar_oclass; + device->oclass[NVDEV_SUBDEV_PMU ] = gt215_pmu_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv50_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = g84_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv50_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv50_gr_oclass; + device->oclass[NVDEV_ENGINE_MSPDEC ] = &g98_mspdec_oclass; + device->oclass[NVDEV_ENGINE_MSVLD ] = &g98_msvld_oclass; + device->oclass[NVDEV_ENGINE_MSPPP ] = &g98_msppp_oclass; + device->oclass[NVDEV_ENGINE_CE0 ] = >215_ce_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = gt215_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = gt215_pm_oclass; + break; + case 0xaf: + device->cname = "MCP89"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = g94_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = g94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &nv50_fuse_oclass; + device->oclass[NVDEV_SUBDEV_CLK ] = >215_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = >215_therm_oclass; + device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = mcp89_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = g98_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = g94_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = mcp89_fb_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &nv50_mmu_oclass; + device->oclass[NVDEV_SUBDEV_BAR ] = &nv50_bar_oclass; + device->oclass[NVDEV_SUBDEV_PMU ] = gt215_pmu_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv50_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = g84_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nv50_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = &nv50_gr_oclass; + device->oclass[NVDEV_ENGINE_MSPDEC ] = &g98_mspdec_oclass; + device->oclass[NVDEV_ENGINE_MSVLD ] = &g98_msvld_oclass; + device->oclass[NVDEV_ENGINE_MSPPP ] = &g98_msppp_oclass; + device->oclass[NVDEV_ENGINE_CE0 ] = >215_ce_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = gt215_disp_oclass; + device->oclass[NVDEV_ENGINE_PM ] = gt215_pm_oclass; + break; + default: + nv_fatal(device, "unknown Tesla chipset\n"); + return -EINVAL; + } + + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/priv.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/priv.h new file mode 100644 index 000000000..8d3590e7b --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/device/priv.h @@ -0,0 +1,16 @@ +#ifndef __NVKM_DEVICE_PRIV_H__ +#define __NVKM_DEVICE_PRIV_H__ +#include + +extern struct nvkm_oclass nvkm_control_oclass[]; + +int nv04_identify(struct nvkm_device *); +int nv10_identify(struct nvkm_device *); +int nv20_identify(struct nvkm_device *); +int nv30_identify(struct nvkm_device *); +int nv40_identify(struct nvkm_device *); +int nv50_identify(struct nvkm_device *); +int gf100_identify(struct nvkm_device *); +int gk104_identify(struct nvkm_device *); +int gm100_identify(struct nvkm_device *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild new file mode 100644 index 000000000..16a4e2a37 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild @@ -0,0 +1,29 @@ +nvkm-y += nvkm/engine/disp/base.o +nvkm-y += nvkm/engine/disp/conn.o +nvkm-y += nvkm/engine/disp/outp.o +nvkm-y += nvkm/engine/disp/outpdp.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/gt215.o +nvkm-y += nvkm/engine/disp/gf110.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/gm204.o +nvkm-y += nvkm/engine/disp/dacnv50.o +nvkm-y += nvkm/engine/disp/dport.o +nvkm-y += nvkm/engine/disp/hdagt215.o +nvkm-y += nvkm/engine/disp/hdagf110.o +nvkm-y += nvkm/engine/disp/hdmig84.o +nvkm-y += nvkm/engine/disp/hdmigt215.o +nvkm-y += nvkm/engine/disp/hdmigf110.o +nvkm-y += nvkm/engine/disp/hdmigk104.o +nvkm-y += nvkm/engine/disp/piornv50.o +nvkm-y += nvkm/engine/disp/sornv50.o +nvkm-y += nvkm/engine/disp/sorg94.o +nvkm-y += nvkm/engine/disp/sorgf110.o +nvkm-y += nvkm/engine/disp/sorgm204.o +nvkm-y += nvkm/engine/disp/vga.o diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c new file mode 100644 index 000000000..23d1b5c0d --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c @@ -0,0 +1,240 @@ +/* + * 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 "outp.h" + +#include +#include +#include + +#include +#include +#include + +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; + + if (nvif_unpack(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; +} + +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_output *outp; + int ret; + + if (nvif_unpack(req->v0, 0, 0, false)) { + notify->size = sizeof(struct nvif_notify_conn_rep_v0); + list_for_each_entry(outp, &disp->outp, 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 = (void *)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; +} + +int +_nvkm_disp_fini(struct nvkm_object *object, bool suspend) +{ + struct nvkm_disp *disp = (void *)object; + struct nvkm_output *outp; + int ret; + + list_for_each_entry(outp, &disp->outp, head) { + ret = nv_ofuncs(outp)->fini(nv_object(outp), suspend); + if (ret && suspend) + goto fail_outp; + } + + return nvkm_engine_fini(&disp->base, suspend); + +fail_outp: + list_for_each_entry_continue_reverse(outp, &disp->outp, head) { + nv_ofuncs(outp)->init(nv_object(outp)); + } + + return ret; +} + +int +_nvkm_disp_init(struct nvkm_object *object) +{ + struct nvkm_disp *disp = (void *)object; + struct nvkm_output *outp; + int ret; + + ret = nvkm_engine_init(&disp->base); + if (ret) + return ret; + + list_for_each_entry(outp, &disp->outp, head) { + ret = nv_ofuncs(outp)->init(nv_object(outp)); + if (ret) + goto fail_outp; + } + + return ret; + +fail_outp: + list_for_each_entry_continue_reverse(outp, &disp->outp, head) { + nv_ofuncs(outp)->fini(nv_object(outp), false); + } + + return ret; +} + +void +_nvkm_disp_dtor(struct nvkm_object *object) +{ + struct nvkm_disp *disp = (void *)object; + struct nvkm_output *outp, *outt; + + nvkm_event_fini(&disp->vblank); + nvkm_event_fini(&disp->hpd); + + if (disp->outp.next) { + list_for_each_entry_safe(outp, outt, &disp->outp, head) { + nvkm_object_ref(NULL, (struct nvkm_object **)&outp); + } + } + + nvkm_engine_destroy(&disp->base); +} + +int +nvkm_disp_create_(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, int heads, const char *intname, + const char *extname, int length, void **pobject) +{ + struct nvkm_disp_impl *impl = (void *)oclass; + struct nvkm_bios *bios = nvkm_bios(parent); + struct nvkm_disp *disp; + struct nvkm_oclass **sclass; + struct nvkm_object *object; + struct dcb_output dcbE; + u8 hpd = 0, ver, hdr; + u32 data; + int ret, i; + + ret = nvkm_engine_create_(parent, engine, oclass, true, intname, + extname, length, pobject); + disp = *pobject; + if (ret) + return ret; + + INIT_LIST_HEAD(&disp->outp); + + /* create output objects for each display path in the vbios */ + i = -1; + while ((data = dcb_outp_parse(bios, ++i, &ver, &hdr, &dcbE))) { + if (dcbE.type == DCB_OUTPUT_UNUSED) + continue; + if (dcbE.type == DCB_OUTPUT_EOL) + break; + data = dcbE.location << 4 | dcbE.type; + + oclass = nvkm_output_oclass; + sclass = impl->outp; + while (sclass && sclass[0]) { + if (sclass[0]->handle == data) { + oclass = sclass[0]; + break; + } + sclass++; + } + + nvkm_object_ctor(*pobject, NULL, oclass, &dcbE, i, &object); + hpd = max(hpd, (u8)(dcbE.connector + 1)); + } + + ret = nvkm_event_init(&nvkm_disp_hpd_func, 3, hpd, &disp->hpd); + if (ret) + return ret; + + ret = nvkm_event_init(impl->vblank, 1, heads, &disp->vblank); + if (ret) + return ret; + + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c new file mode 100644 index 000000000..cf03e0240 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c @@ -0,0 +1,174 @@ +/* + * 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_connector_hpd(struct nvkm_notify *notify) +{ + struct nvkm_connector *conn = container_of(notify, typeof(*conn), hpd); + struct nvkm_disp *disp = nvkm_disp(conn); + struct nvkm_gpio *gpio = nvkm_gpio(conn); + const struct nvkm_gpio_ntfy_rep *line = notify->data; + struct nvif_notify_conn_rep_v0 rep; + int index = conn->index; + + DBG("HPD: %d\n", line->mask); + + if (!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; +} + +int +_nvkm_connector_fini(struct nvkm_object *object, bool suspend) +{ + struct nvkm_connector *conn = (void *)object; + nvkm_notify_put(&conn->hpd); + return nvkm_object_fini(&conn->base, suspend); +} + +int +_nvkm_connector_init(struct nvkm_object *object) +{ + struct nvkm_connector *conn = (void *)object; + int ret = nvkm_object_init(&conn->base); + if (ret == 0) + nvkm_notify_get(&conn->hpd); + return ret; +} + +void +_nvkm_connector_dtor(struct nvkm_object *object) +{ + struct nvkm_connector *conn = (void *)object; + nvkm_notify_fini(&conn->hpd); + nvkm_object_destroy(&conn->base); +} + +int +nvkm_connector_create_(struct nvkm_object *parent, + struct nvkm_object *engine, + struct nvkm_oclass *oclass, + struct nvbios_connE *info, int index, + int length, void **pobject) +{ + static const u8 hpd[] = { 0x07, 0x08, 0x51, 0x52, 0x5e, 0x5f, 0x60 }; + struct nvkm_disp *disp = nvkm_disp(parent); + struct nvkm_gpio *gpio = nvkm_gpio(parent); + struct nvkm_connector *conn; + struct nvkm_output *outp; + struct dcb_gpio_func func; + int ret; + + list_for_each_entry(outp, &disp->outp, head) { + if (outp->conn && outp->conn->index == index) { + atomic_inc(&nv_object(outp->conn)->refcount); + *pobject = outp->conn; + return 1; + } + } + + ret = nvkm_object_create_(parent, engine, oclass, 0, length, pobject); + conn = *pobject; + if (ret) + return ret; + + conn->info = *info; + conn->index = index; + + DBG("type %02x loc %d hpd %02x dp %x di %x sr %x lcdid %x\n", + 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)) { + ERR("hpd %02x unknown\n", info->hpd); + return 0; + } + info->hpd = hpd[info->hpd]; + + ret = gpio->find(gpio, 0, info->hpd, DCB_GPIO_UNUSED, &func); + if (ret) { + ERR("func %02x lookup failed, %d\n", info->hpd, ret); + return 0; + } + + ret = nvkm_notify_init(NULL, &gpio->event, nvkm_connector_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) { + ERR("func %02x failed, %d\n", info->hpd, ret); + } else { + DBG("func %02x (HPD)\n", info->hpd); + } + } + + return 0; +} + +int +_nvkm_connector_ctor(struct nvkm_object *parent, + struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *info, u32 index, + struct nvkm_object **pobject) +{ + struct nvkm_connector *conn; + int ret; + + ret = nvkm_connector_create(parent, engine, oclass, info, index, &conn); + *pobject = nv_object(conn); + if (ret) + return ret; + + return 0; +} + +struct nvkm_oclass * +nvkm_connector_oclass = &(struct nvkm_connector_impl) { + .base = { + .handle = 0, + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_connector_ctor, + .dtor = _nvkm_connector_dtor, + .init = _nvkm_connector_init, + .fini = _nvkm_connector_fini, + }, + }, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.h new file mode 100644 index 000000000..c87a061f7 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.h @@ -0,0 +1,58 @@ +#ifndef __NVKM_DISP_CONN_H__ +#define __NVKM_DISP_CONN_H__ +#include +#include + +#include +#include + +struct nvkm_connector { + struct nvkm_object base; + struct list_head head; + + struct nvbios_connE info; + int index; + + struct nvkm_notify hpd; +}; + +#define nvkm_connector_create(p,e,c,b,i,d) \ + nvkm_connector_create_((p), (e), (c), (b), (i), sizeof(**d), (void **)d) +#define nvkm_connector_destroy(d) ({ \ + struct nvkm_connector *disp = (d); \ + _nvkm_connector_dtor(nv_object(disp)); \ +}) +#define nvkm_connector_init(d) ({ \ + struct nvkm_connector *disp = (d); \ + _nvkm_connector_init(nv_object(disp)); \ +}) +#define nvkm_connector_fini(d,s) ({ \ + struct nvkm_connector *disp = (d); \ + _nvkm_connector_fini(nv_object(disp), (s)); \ +}) + +int nvkm_connector_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, struct nvbios_connE *, + int, int, void **); + +int _nvkm_connector_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); +void _nvkm_connector_dtor(struct nvkm_object *); +int _nvkm_connector_init(struct nvkm_object *); +int _nvkm_connector_fini(struct nvkm_object *, bool); + +struct nvkm_connector_impl { + struct nvkm_oclass base; +}; + +#ifndef MSG +#define MSG(l,f,a...) do { \ + struct nvkm_connector *_conn = (void *)conn; \ + nv_##l(_conn, "%02x:%02x%02x: "f, _conn->index, \ + _conn->info.location, _conn->info.type, ##a); \ +} while(0) +#define DBG(f,a...) MSG(debug, f, ##a) +#define ERR(f,a...) MSG(error, f, ##a) +#endif +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/dacnv50.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/dacnv50.c new file mode 100644 index 000000000..0f7d1ec4d --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/dacnv50.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 "nv50.h" +#include "outp.h" + +#include +#include + +#include +#include + +int +nv50_dac_power(NV50_DISP_MTHD_V1) +{ + const u32 doff = outp->or * 0x800; + union { + struct nv50_disp_dac_pwr_v0 v0; + } *args = data; + u32 stat; + int ret; + + nv_ioctl(object, "disp dac pwr size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(object, "disp dac pwr vers %d state %d data %d " + "vsync %d hsync %d\n", + args->v0.version, args->v0.state, args->v0.data, + args->v0.vsync, args->v0.hsync); + stat = 0x00000040 * !args->v0.state; + stat |= 0x00000010 * !args->v0.data; + stat |= 0x00000004 * !args->v0.vsync; + stat |= 0x00000001 * !args->v0.hsync; + } else + return ret; + + nv_wait(priv, 0x61a004 + doff, 0x80000000, 0x00000000); + nv_mask(priv, 0x61a004 + doff, 0xc000007f, 0x80000000 | stat); + nv_wait(priv, 0x61a004 + doff, 0x80000000, 0x00000000); + return 0; +} + +int +nv50_dac_sense(NV50_DISP_MTHD_V1) +{ + union { + struct nv50_disp_dac_load_v0 v0; + } *args = data; + const u32 doff = outp->or * 0x800; + u32 loadval; + int ret; + + nv_ioctl(object, "disp dac load size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(object, "disp dac load vers %d data %08x\n", + args->v0.version, args->v0.data); + if (args->v0.data & 0xfff00000) + return -EINVAL; + loadval = args->v0.data; + } else + return ret; + + nv_mask(priv, 0x61a004 + doff, 0x807f0000, 0x80150000); + nv_wait(priv, 0x61a004 + doff, 0x80000000, 0x00000000); + + nv_wr32(priv, 0x61a00c + doff, 0x00100000 | loadval); + mdelay(9); + udelay(500); + loadval = nv_mask(priv, 0x61a00c + doff, 0xffffffff, 0x00000000); + + nv_mask(priv, 0x61a004 + doff, 0x807f0000, 0x80550000); + nv_wait(priv, 0x61a004 + doff, 0x80000000, 0x00000000); + + nv_debug(priv, "DAC%d sense: 0x%08x\n", outp->or, loadval); + if (!(loadval & 0x80000000)) + return -ETIMEDOUT; + + args->v0.load = (loadval & 0x38000000) >> 27; + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/dport.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/dport.c new file mode 100644 index 000000000..68347661a --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/dport.c @@ -0,0 +1,398 @@ +/* + * 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 "dport.h" +#include "outpdp.h" +#include "nv50.h" + +#include +#include +#include + +#include + +/****************************************************************************** + * link training + *****************************************************************************/ +struct dp_state { + struct nvkm_output_dp *outp; + int link_nr; + u32 link_bw; + u8 stat[6]; + u8 conf[4]; + bool pc2; + u8 pc2stat; + u8 pc2conf[2]; +}; + +static int +dp_set_link_config(struct dp_state *dp) +{ + struct nvkm_output_dp_impl *impl = (void *)nv_oclass(dp->outp); + struct nvkm_output_dp *outp = dp->outp; + struct nvkm_disp *disp = nvkm_disp(outp); + struct nvkm_bios *bios = nvkm_bios(disp); + struct nvbios_init init = { + .subdev = nv_subdev(disp), + .bios = bios, + .offset = 0x0000, + .outp = &outp->base.info, + .crtc = -1, + .execute = 1, + }; + u32 lnkcmp; + u8 sink[2]; + int ret; + + DBG("%d lanes at %d KB/s\n", dp->link_nr, dp->link_bw); + + /* set desired link configuration on the source */ + if ((lnkcmp = dp->outp->info.lnkcmp)) { + if (outp->version < 0x30) { + while ((dp->link_bw / 10) < nv_ro16(bios, lnkcmp)) + lnkcmp += 4; + init.offset = nv_ro16(bios, lnkcmp + 2); + } else { + while ((dp->link_bw / 27000) < nv_ro08(bios, lnkcmp)) + lnkcmp += 3; + init.offset = nv_ro16(bios, lnkcmp + 1); + } + + nvbios_exec(&init); + } + + ret = impl->lnk_ctl(outp, dp->link_nr, dp->link_bw / 27000, + outp->dpcd[DPCD_RC02] & + DPCD_RC02_ENHANCED_FRAME_CAP); + if (ret) { + if (ret < 0) + ERR("lnk_ctl failed with %d\n", ret); + return ret; + } + + impl->lnk_pwr(outp, dp->link_nr); + + /* set desired link configuration on the sink */ + sink[0] = dp->link_bw / 27000; + sink[1] = dp->link_nr; + if (outp->dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP) + sink[1] |= DPCD_LC01_ENHANCED_FRAME_EN; + + return nv_wraux(outp->base.edid, DPCD_LC00_LINK_BW_SET, sink, 2); +} + +static void +dp_set_training_pattern(struct dp_state *dp, u8 pattern) +{ + struct nvkm_output_dp_impl *impl = (void *)nv_oclass(dp->outp); + struct nvkm_output_dp *outp = dp->outp; + u8 sink_tp; + + DBG("training pattern %d\n", pattern); + impl->pattern(outp, pattern); + + nv_rdaux(outp->base.edid, DPCD_LC02, &sink_tp, 1); + sink_tp &= ~DPCD_LC02_TRAINING_PATTERN_SET; + sink_tp |= pattern; + nv_wraux(outp->base.edid, DPCD_LC02, &sink_tp, 1); +} + +static int +dp_link_train_commit(struct dp_state *dp, bool pc) +{ + struct nvkm_output_dp_impl *impl = (void *)nv_oclass(dp->outp); + struct nvkm_output_dp *outp = dp->outp; + int ret, i; + + for (i = 0; i < dp->link_nr; i++) { + u8 lane = (dp->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf; + u8 lpc2 = (dp->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; + } + + dp->conf[i] = (lpre << 3) | lvsw; + dp->pc2conf[i >> 1] |= lpc2 << ((i & 1) * 4); + + DBG("config lane %d %02x %02x\n", i, dp->conf[i], lpc2); + impl->drv_ctl(outp, i, lvsw & 3, lpre & 3, lpc2 & 3); + } + + ret = nv_wraux(outp->base.edid, DPCD_LC03(0), dp->conf, 4); + if (ret) + return ret; + + if (pc) { + ret = nv_wraux(outp->base.edid, DPCD_LC0F, dp->pc2conf, 2); + if (ret) + return ret; + } + + return 0; +} + +static int +dp_link_train_update(struct dp_state *dp, bool pc, u32 delay) +{ + struct nvkm_output_dp *outp = dp->outp; + int ret; + + if (outp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL]) + mdelay(outp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL] * 4); + else + udelay(delay); + + ret = nv_rdaux(outp->base.edid, DPCD_LS02, dp->stat, 6); + if (ret) + return ret; + + if (pc) { + ret = nv_rdaux(outp->base.edid, DPCD_LS0C, &dp->pc2stat, 1); + if (ret) + dp->pc2stat = 0x00; + DBG("status %6ph pc2 %02x\n", dp->stat, dp->pc2stat); + } else { + DBG("status %6ph\n", dp->stat); + } + + return 0; +} + +static int +dp_link_train_cr(struct dp_state *dp) +{ + bool cr_done = false, abort = false; + int voltage = dp->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET; + int tries = 0, i; + + dp_set_training_pattern(dp, 1); + + do { + if (dp_link_train_commit(dp, false) || + dp_link_train_update(dp, false, 100)) + break; + + cr_done = true; + for (i = 0; i < dp->link_nr; i++) { + u8 lane = (dp->stat[i >> 1] >> ((i & 1) * 4)) & 0xf; + if (!(lane & DPCD_LS02_LANE0_CR_DONE)) { + cr_done = false; + if (dp->conf[i] & DPCD_LC03_MAX_SWING_REACHED) + abort = true; + break; + } + } + + if ((dp->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET) != voltage) { + voltage = dp->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET; + tries = 0; + } + } while (!cr_done && !abort && ++tries < 5); + + return cr_done ? 0 : -1; +} + +static int +dp_link_train_eq(struct dp_state *dp) +{ + struct nvkm_output_dp *outp = dp->outp; + bool eq_done = false, cr_done = true; + int tries = 0, i; + + if (outp->dpcd[2] & DPCD_RC02_TPS3_SUPPORTED) + dp_set_training_pattern(dp, 3); + else + dp_set_training_pattern(dp, 2); + + do { + if ((tries && + dp_link_train_commit(dp, dp->pc2)) || + dp_link_train_update(dp, dp->pc2, 400)) + break; + + eq_done = !!(dp->stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE); + for (i = 0; i < dp->link_nr && eq_done; i++) { + u8 lane = (dp->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 void +dp_link_train_init(struct dp_state *dp, bool spread) +{ + struct nvkm_output_dp *outp = dp->outp; + struct nvkm_disp *disp = nvkm_disp(outp); + struct nvkm_bios *bios = nvkm_bios(disp); + struct nvbios_init init = { + .subdev = nv_subdev(disp), + .bios = bios, + .outp = &outp->base.info, + .crtc = -1, + .execute = 1, + }; + + /* set desired spread */ + if (spread) + init.offset = outp->info.script[2]; + else + init.offset = outp->info.script[3]; + nvbios_exec(&init); + + /* pre-train script */ + init.offset = outp->info.script[0]; + nvbios_exec(&init); +} + +static void +dp_link_train_fini(struct dp_state *dp) +{ + struct nvkm_output_dp *outp = dp->outp; + struct nvkm_disp *disp = nvkm_disp(outp); + struct nvkm_bios *bios = nvkm_bios(disp); + struct nvbios_init init = { + .subdev = nv_subdev(disp), + .bios = bios, + .outp = &outp->base.info, + .crtc = -1, + .execute = 1, + }; + + /* post-train script */ + init.offset = outp->info.script[1], + nvbios_exec(&init); +} + +static const struct dp_rates { + u32 rate; + u8 bw; + u8 nr; +} nvkm_dp_rates[] = { + { 2160000, 0x14, 4 }, + { 1080000, 0x0a, 4 }, + { 1080000, 0x14, 2 }, + { 648000, 0x06, 4 }, + { 540000, 0x0a, 2 }, + { 540000, 0x14, 1 }, + { 324000, 0x06, 2 }, + { 270000, 0x0a, 1 }, + { 162000, 0x06, 1 }, + {} +}; + +void +nvkm_dp_train(struct work_struct *w) +{ + struct nvkm_output_dp *outp = container_of(w, typeof(*outp), lt.work); + struct nv50_disp_priv *priv = (void *)nvkm_disp(outp); + const struct dp_rates *cfg = nvkm_dp_rates; + struct dp_state _dp = { + .outp = outp, + }, *dp = &_dp; + u32 datarate = 0; + int ret; + + if (!outp->base.info.location && priv->sor.magic) + priv->sor.magic(&outp->base); + + /* bring capabilities within encoder limits */ + if (nv_mclass(priv) < GF110_DISP) + outp->dpcd[2] &= ~DPCD_RC02_TPS3_SUPPORTED; + if ((outp->dpcd[2] & 0x1f) > outp->base.info.dpconf.link_nr) { + outp->dpcd[2] &= ~DPCD_RC02_MAX_LANE_COUNT; + outp->dpcd[2] |= outp->base.info.dpconf.link_nr; + } + if (outp->dpcd[1] > outp->base.info.dpconf.link_bw) + outp->dpcd[1] = outp->base.info.dpconf.link_bw; + dp->pc2 = outp->dpcd[2] & DPCD_RC02_TPS3_SUPPORTED; + + /* restrict link config to the lowest required rate, if requested */ + if (datarate) { + datarate = (datarate / 8) * 10; /* 8B/10B coding overhead */ + while (cfg[1].rate >= datarate) + cfg++; + } + cfg--; + + /* disable link interrupt handling during link training */ + nvkm_notify_put(&outp->irq); + + /* enable down-spreading and execute pre-train script from vbios */ + dp_link_train_init(dp, outp->dpcd[3] & 0x01); + + while (ret = -EIO, (++cfg)->rate) { + /* select next configuration supported by encoder and sink */ + while (cfg->nr > (outp->dpcd[2] & DPCD_RC02_MAX_LANE_COUNT) || + cfg->bw > (outp->dpcd[DPCD_RC01_MAX_LINK_RATE])) + cfg++; + dp->link_bw = cfg->bw * 27000; + dp->link_nr = cfg->nr; + + /* program selected link configuration */ + ret = dp_set_link_config(dp); + if (ret == 0) { + /* attempt to train the link at this configuration */ + memset(dp->stat, 0x00, sizeof(dp->stat)); + if (!dp_link_train_cr(dp) && + !dp_link_train_eq(dp)) + break; + } else + if (ret) { + /* dp_set_link_config() handled training, or + * we failed to communicate with the sink. + */ + break; + } + } + + /* finish link training and execute post-train script from vbios */ + dp_set_training_pattern(dp, 0); + if (ret < 0) + ERR("link training failed\n"); + + dp_link_train_fini(dp); + + /* signal completion and enable link interrupt handling */ + DBG("training complete\n"); + atomic_set(&outp->lt.done, 1); + wake_up(&outp->lt.wait); + nvkm_notify_get(&outp->irq); +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/dport.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/dport.h new file mode 100644 index 000000000..959629032 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/dport.h @@ -0,0 +1,75 @@ +#ifndef __NVKM_DISP_DPORT_H__ +#define __NVKM_DISP_DPORT_H__ +#include + +/* 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_MAX_DOWNSPREAD 0x01 +#define DPCD_RC0E_AUX_RD_INTERVAL 0x0000e + +/* 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 0x03 +#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 + +/* 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 + +void nvkm_dp_train(struct work_struct *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/g84.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/g84.c new file mode 100644 index 000000000..a0dcf534c --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/g84.c @@ -0,0 +1,272 @@ +/* + * 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 + +/******************************************************************************* + * EVO master channel object + ******************************************************************************/ + +const struct nv50_disp_mthd_list +g84_disp_core_mthd_dac = { + .mthd = 0x0080, + .addr = 0x000008, + .data = { + { 0x0400, 0x610b58 }, + { 0x0404, 0x610bdc }, + { 0x0420, 0x610bc4 }, + {} + } +}; + +const struct nv50_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 nv50_disp_mthd_chan +g84_disp_core_mthd_chan = { + .name = "Core", + .addr = 0x000000, + .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 }, + {} + } +}; + +/******************************************************************************* + * EVO sync channel objects + ******************************************************************************/ + +static const struct nv50_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 }, + {} + } +}; + +const struct nv50_disp_mthd_chan +g84_disp_base_mthd_chan = { + .name = "Base", + .addr = 0x000540, + .data = { + { "Global", 1, &g84_disp_base_mthd_base }, + { "Image", 2, &nv50_disp_base_mthd_image }, + {} + } +}; + +/******************************************************************************* + * EVO overlay channel objects + ******************************************************************************/ + +static const struct nv50_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 }, + {} + } +}; + +const struct nv50_disp_mthd_chan +g84_disp_ovly_mthd_chan = { + .name = "Overlay", + .addr = 0x000540, + .data = { + { "Global", 1, &g84_disp_ovly_mthd_base }, + {} + } +}; + +/******************************************************************************* + * Base display object + ******************************************************************************/ + +static struct nvkm_oclass +g84_disp_sclass[] = { + { G82_DISP_CORE_CHANNEL_DMA, &nv50_disp_core_ofuncs.base }, + { G82_DISP_BASE_CHANNEL_DMA, &nv50_disp_base_ofuncs.base }, + { G82_DISP_OVERLAY_CHANNEL_DMA, &nv50_disp_ovly_ofuncs.base }, + { G82_DISP_OVERLAY, &nv50_disp_oimm_ofuncs.base }, + { G82_DISP_CURSOR, &nv50_disp_curs_ofuncs.base }, + {} +}; + +static struct nvkm_oclass +g84_disp_main_oclass[] = { + { G82_DISP, &nv50_disp_main_ofuncs }, + {} +}; + +/******************************************************************************* + * Display engine implementation + ******************************************************************************/ + +static int +g84_disp_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv50_disp_priv *priv; + int ret; + + ret = nvkm_disp_create(parent, engine, oclass, 2, "PDISP", + "display", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + ret = nvkm_event_init(&nv50_disp_chan_uevent, 1, 9, &priv->uevent); + if (ret) + return ret; + + nv_engine(priv)->sclass = g84_disp_main_oclass; + nv_engine(priv)->cclass = &nv50_disp_cclass; + nv_subdev(priv)->intr = nv50_disp_intr; + INIT_WORK(&priv->supervisor, nv50_disp_intr_supervisor); + priv->sclass = g84_disp_sclass; + priv->head.nr = 2; + priv->dac.nr = 3; + priv->sor.nr = 2; + priv->pior.nr = 3; + priv->dac.power = nv50_dac_power; + priv->dac.sense = nv50_dac_sense; + priv->sor.power = nv50_sor_power; + priv->sor.hdmi = g84_hdmi_ctrl; + priv->pior.power = nv50_pior_power; + return 0; +} + +struct nvkm_oclass * +g84_disp_oclass = &(struct nv50_disp_impl) { + .base.base.handle = NV_ENGINE(DISP, 0x82), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = g84_disp_ctor, + .dtor = _nvkm_disp_dtor, + .init = _nvkm_disp_init, + .fini = _nvkm_disp_fini, + }, + .base.vblank = &nv50_disp_vblank_func, + .base.outp = nv50_disp_outp_sclass, + .mthd.core = &g84_disp_core_mthd_chan, + .mthd.base = &g84_disp_base_mthd_chan, + .mthd.ovly = &g84_disp_ovly_mthd_chan, + .mthd.prev = 0x000004, + .head.scanoutpos = nv50_disp_main_scanoutpos, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/g94.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/g94.c new file mode 100644 index 000000000..1ab0d0ae3 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/g94.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 "nv50.h" +#include "outpdp.h" + +#include + +/******************************************************************************* + * EVO master channel object + ******************************************************************************/ + +const struct nv50_disp_mthd_list +g94_disp_core_mthd_sor = { + .mthd = 0x0040, + .addr = 0x000008, + .data = { + { 0x0600, 0x610794 }, + {} + } +}; + +const struct nv50_disp_mthd_chan +g94_disp_core_mthd_chan = { + .name = "Core", + .addr = 0x000000, + .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 }, + {} + } +}; + +/******************************************************************************* + * Base display object + ******************************************************************************/ + +static struct nvkm_oclass +g94_disp_sclass[] = { + { GT206_DISP_CORE_CHANNEL_DMA, &nv50_disp_core_ofuncs.base }, + { GT200_DISP_BASE_CHANNEL_DMA, &nv50_disp_base_ofuncs.base }, + { GT200_DISP_OVERLAY_CHANNEL_DMA, &nv50_disp_ovly_ofuncs.base }, + { G82_DISP_OVERLAY, &nv50_disp_oimm_ofuncs.base }, + { G82_DISP_CURSOR, &nv50_disp_curs_ofuncs.base }, + {} +}; + +static struct nvkm_oclass +g94_disp_main_oclass[] = { + { GT206_DISP, &nv50_disp_main_ofuncs }, + {} +}; + +/******************************************************************************* + * Display engine implementation + ******************************************************************************/ + +static int +g94_disp_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv50_disp_priv *priv; + int ret; + + ret = nvkm_disp_create(parent, engine, oclass, 2, "PDISP", + "display", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + ret = nvkm_event_init(&nv50_disp_chan_uevent, 1, 9, &priv->uevent); + if (ret) + return ret; + + nv_engine(priv)->sclass = g94_disp_main_oclass; + nv_engine(priv)->cclass = &nv50_disp_cclass; + nv_subdev(priv)->intr = nv50_disp_intr; + INIT_WORK(&priv->supervisor, nv50_disp_intr_supervisor); + priv->sclass = g94_disp_sclass; + priv->head.nr = 2; + priv->dac.nr = 3; + priv->sor.nr = 4; + priv->pior.nr = 3; + priv->dac.power = nv50_dac_power; + priv->dac.sense = nv50_dac_sense; + priv->sor.power = nv50_sor_power; + priv->sor.hdmi = g84_hdmi_ctrl; + priv->pior.power = nv50_pior_power; + return 0; +} + +struct nvkm_oclass * +g94_disp_outp_sclass[] = { + &nv50_pior_dp_impl.base.base, + &g94_sor_dp_impl.base.base, + NULL +}; + +struct nvkm_oclass * +g94_disp_oclass = &(struct nv50_disp_impl) { + .base.base.handle = NV_ENGINE(DISP, 0x88), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = g94_disp_ctor, + .dtor = _nvkm_disp_dtor, + .init = _nvkm_disp_init, + .fini = _nvkm_disp_fini, + }, + .base.vblank = &nv50_disp_vblank_func, + .base.outp = g94_disp_outp_sclass, + .mthd.core = &g94_disp_core_mthd_chan, + .mthd.base = &g84_disp_base_mthd_chan, + .mthd.ovly = &g84_disp_ovly_mthd_chan, + .mthd.prev = 0x000004, + .head.scanoutpos = nv50_disp_main_scanoutpos, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf110.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf110.c new file mode 100644 index 000000000..9ef6728c5 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf110.c @@ -0,0 +1,1310 @@ +/* + * 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 "outp.h" +#include "outpdp.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/******************************************************************************* + * EVO channel base class + ******************************************************************************/ + +static void +gf110_disp_chan_uevent_fini(struct nvkm_event *event, int type, int index) +{ + struct nv50_disp_priv *priv = container_of(event, typeof(*priv), uevent); + nv_mask(priv, 0x610090, 0x00000001 << index, 0x00000000 << index); + nv_wr32(priv, 0x61008c, 0x00000001 << index); +} + +static void +gf110_disp_chan_uevent_init(struct nvkm_event *event, int types, int index) +{ + struct nv50_disp_priv *priv = container_of(event, typeof(*priv), uevent); + nv_wr32(priv, 0x61008c, 0x00000001 << index); + nv_mask(priv, 0x610090, 0x00000001 << index, 0x00000001 << index); +} + +const struct nvkm_event_func +gf110_disp_chan_uevent = { + .ctor = nv50_disp_chan_uevent_ctor, + .init = gf110_disp_chan_uevent_init, + .fini = gf110_disp_chan_uevent_fini, +}; + +/******************************************************************************* + * EVO DMA channel base class + ******************************************************************************/ + +static int +gf110_disp_dmac_object_attach(struct nvkm_object *parent, + struct nvkm_object *object, u32 name) +{ + struct nv50_disp_base *base = (void *)parent->parent; + struct nv50_disp_chan *chan = (void *)parent; + u32 addr = nv_gpuobj(object)->node->offset; + u32 data = (chan->chid << 27) | (addr << 9) | 0x00000001; + return nvkm_ramht_insert(base->ramht, chan->chid, name, data); +} + +static void +gf110_disp_dmac_object_detach(struct nvkm_object *parent, int cookie) +{ + struct nv50_disp_base *base = (void *)parent->parent; + nvkm_ramht_remove(base->ramht, cookie); +} + +static int +gf110_disp_dmac_init(struct nvkm_object *object) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nv50_disp_dmac *dmac = (void *)object; + int chid = dmac->base.chid; + int ret; + + ret = nv50_disp_chan_init(&dmac->base); + if (ret) + return ret; + + /* enable error reporting */ + nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000001 << chid); + + /* initialise channel for dma command submission */ + nv_wr32(priv, 0x610494 + (chid * 0x0010), dmac->push); + nv_wr32(priv, 0x610498 + (chid * 0x0010), 0x00010000); + nv_wr32(priv, 0x61049c + (chid * 0x0010), 0x00000001); + nv_mask(priv, 0x610490 + (chid * 0x0010), 0x00000010, 0x00000010); + nv_wr32(priv, 0x640000 + (chid * 0x1000), 0x00000000); + nv_wr32(priv, 0x610490 + (chid * 0x0010), 0x00000013); + + /* wait for it to go inactive */ + if (!nv_wait(priv, 0x610490 + (chid * 0x10), 0x80000000, 0x00000000)) { + nv_error(dmac, "init: 0x%08x\n", + nv_rd32(priv, 0x610490 + (chid * 0x10))); + return -EBUSY; + } + + return 0; +} + +static int +gf110_disp_dmac_fini(struct nvkm_object *object, bool suspend) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nv50_disp_dmac *dmac = (void *)object; + int chid = dmac->base.chid; + + /* deactivate channel */ + nv_mask(priv, 0x610490 + (chid * 0x0010), 0x00001010, 0x00001000); + nv_mask(priv, 0x610490 + (chid * 0x0010), 0x00000003, 0x00000000); + if (!nv_wait(priv, 0x610490 + (chid * 0x10), 0x001e0000, 0x00000000)) { + nv_error(dmac, "fini: 0x%08x\n", + nv_rd32(priv, 0x610490 + (chid * 0x10))); + if (suspend) + return -EBUSY; + } + + /* disable error reporting and completion notification */ + nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000000); + nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000000); + + return nv50_disp_chan_fini(&dmac->base, suspend); +} + +/******************************************************************************* + * EVO master channel object + ******************************************************************************/ + +const struct nv50_disp_mthd_list +gf110_disp_core_mthd_base = { + .mthd = 0x0000, + .addr = 0x000000, + .data = { + { 0x0080, 0x660080 }, + { 0x0084, 0x660084 }, + { 0x0088, 0x660088 }, + { 0x008c, 0x000000 }, + {} + } +}; + +const struct nv50_disp_mthd_list +gf110_disp_core_mthd_dac = { + .mthd = 0x0020, + .addr = 0x000020, + .data = { + { 0x0180, 0x660180 }, + { 0x0184, 0x660184 }, + { 0x0188, 0x660188 }, + { 0x0190, 0x660190 }, + {} + } +}; + +const struct nv50_disp_mthd_list +gf110_disp_core_mthd_sor = { + .mthd = 0x0020, + .addr = 0x000020, + .data = { + { 0x0200, 0x660200 }, + { 0x0204, 0x660204 }, + { 0x0208, 0x660208 }, + { 0x0210, 0x660210 }, + {} + } +}; + +const struct nv50_disp_mthd_list +gf110_disp_core_mthd_pior = { + .mthd = 0x0020, + .addr = 0x000020, + .data = { + { 0x0300, 0x660300 }, + { 0x0304, 0x660304 }, + { 0x0308, 0x660308 }, + { 0x0310, 0x660310 }, + {} + } +}; + +static const struct nv50_disp_mthd_list +gf110_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 nv50_disp_mthd_chan +gf110_disp_core_mthd_chan = { + .name = "Core", + .addr = 0x000000, + .data = { + { "Global", 1, &gf110_disp_core_mthd_base }, + { "DAC", 3, &gf110_disp_core_mthd_dac }, + { "SOR", 8, &gf110_disp_core_mthd_sor }, + { "PIOR", 4, &gf110_disp_core_mthd_pior }, + { "HEAD", 4, &gf110_disp_core_mthd_head }, + {} + } +}; + +static int +gf110_disp_core_init(struct nvkm_object *object) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nv50_disp_dmac *mast = (void *)object; + int ret; + + ret = nv50_disp_chan_init(&mast->base); + if (ret) + return ret; + + /* enable error reporting */ + nv_mask(priv, 0x6100a0, 0x00000001, 0x00000001); + + /* initialise channel for dma command submission */ + nv_wr32(priv, 0x610494, mast->push); + nv_wr32(priv, 0x610498, 0x00010000); + nv_wr32(priv, 0x61049c, 0x00000001); + nv_mask(priv, 0x610490, 0x00000010, 0x00000010); + nv_wr32(priv, 0x640000, 0x00000000); + nv_wr32(priv, 0x610490, 0x01000013); + + /* wait for it to go inactive */ + if (!nv_wait(priv, 0x610490, 0x80000000, 0x00000000)) { + nv_error(mast, "init: 0x%08x\n", nv_rd32(priv, 0x610490)); + return -EBUSY; + } + + return 0; +} + +static int +gf110_disp_core_fini(struct nvkm_object *object, bool suspend) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nv50_disp_dmac *mast = (void *)object; + + /* deactivate channel */ + nv_mask(priv, 0x610490, 0x00000010, 0x00000000); + nv_mask(priv, 0x610490, 0x00000003, 0x00000000); + if (!nv_wait(priv, 0x610490, 0x001e0000, 0x00000000)) { + nv_error(mast, "fini: 0x%08x\n", nv_rd32(priv, 0x610490)); + if (suspend) + return -EBUSY; + } + + /* disable error reporting and completion notification */ + nv_mask(priv, 0x610090, 0x00000001, 0x00000000); + nv_mask(priv, 0x6100a0, 0x00000001, 0x00000000); + + return nv50_disp_chan_fini(&mast->base, suspend); +} + +struct nv50_disp_chan_impl +gf110_disp_core_ofuncs = { + .base.ctor = nv50_disp_core_ctor, + .base.dtor = nv50_disp_dmac_dtor, + .base.init = gf110_disp_core_init, + .base.fini = gf110_disp_core_fini, + .base.ntfy = nv50_disp_chan_ntfy, + .base.map = nv50_disp_chan_map, + .base.rd32 = nv50_disp_chan_rd32, + .base.wr32 = nv50_disp_chan_wr32, + .chid = 0, + .attach = gf110_disp_dmac_object_attach, + .detach = gf110_disp_dmac_object_detach, +}; + +/******************************************************************************* + * EVO sync channel objects + ******************************************************************************/ + +static const struct nv50_disp_mthd_list +gf110_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 nv50_disp_mthd_list +gf110_disp_base_mthd_image = { + .mthd = 0x0020, + .addr = 0x000020, + .data = { + { 0x0400, 0x661400 }, + { 0x0404, 0x661404 }, + { 0x0408, 0x661408 }, + { 0x040c, 0x66140c }, + { 0x0410, 0x661410 }, + {} + } +}; + +const struct nv50_disp_mthd_chan +gf110_disp_base_mthd_chan = { + .name = "Base", + .addr = 0x001000, + .data = { + { "Global", 1, &gf110_disp_base_mthd_base }, + { "Image", 2, &gf110_disp_base_mthd_image }, + {} + } +}; + +struct nv50_disp_chan_impl +gf110_disp_base_ofuncs = { + .base.ctor = nv50_disp_base_ctor, + .base.dtor = nv50_disp_dmac_dtor, + .base.init = gf110_disp_dmac_init, + .base.fini = gf110_disp_dmac_fini, + .base.ntfy = nv50_disp_chan_ntfy, + .base.map = nv50_disp_chan_map, + .base.rd32 = nv50_disp_chan_rd32, + .base.wr32 = nv50_disp_chan_wr32, + .chid = 1, + .attach = gf110_disp_dmac_object_attach, + .detach = gf110_disp_dmac_object_detach, +}; + +/******************************************************************************* + * EVO overlay channel objects + ******************************************************************************/ + +static const struct nv50_disp_mthd_list +gf110_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 nv50_disp_mthd_chan +gf110_disp_ovly_mthd_chan = { + .name = "Overlay", + .addr = 0x001000, + .data = { + { "Global", 1, &gf110_disp_ovly_mthd_base }, + {} + } +}; + +struct nv50_disp_chan_impl +gf110_disp_ovly_ofuncs = { + .base.ctor = nv50_disp_ovly_ctor, + .base.dtor = nv50_disp_dmac_dtor, + .base.init = gf110_disp_dmac_init, + .base.fini = gf110_disp_dmac_fini, + .base.ntfy = nv50_disp_chan_ntfy, + .base.map = nv50_disp_chan_map, + .base.rd32 = nv50_disp_chan_rd32, + .base.wr32 = nv50_disp_chan_wr32, + .chid = 5, + .attach = gf110_disp_dmac_object_attach, + .detach = gf110_disp_dmac_object_detach, +}; + +/******************************************************************************* + * EVO PIO channel base class + ******************************************************************************/ + +static int +gf110_disp_pioc_init(struct nvkm_object *object) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nv50_disp_pioc *pioc = (void *)object; + int chid = pioc->base.chid; + int ret; + + ret = nv50_disp_chan_init(&pioc->base); + if (ret) + return ret; + + /* enable error reporting */ + nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000001 << chid); + + /* activate channel */ + nv_wr32(priv, 0x610490 + (chid * 0x10), 0x00000001); + if (!nv_wait(priv, 0x610490 + (chid * 0x10), 0x00030000, 0x00010000)) { + nv_error(pioc, "init: 0x%08x\n", + nv_rd32(priv, 0x610490 + (chid * 0x10))); + return -EBUSY; + } + + return 0; +} + +static int +gf110_disp_pioc_fini(struct nvkm_object *object, bool suspend) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nv50_disp_pioc *pioc = (void *)object; + int chid = pioc->base.chid; + + nv_mask(priv, 0x610490 + (chid * 0x10), 0x00000001, 0x00000000); + if (!nv_wait(priv, 0x610490 + (chid * 0x10), 0x00030000, 0x00000000)) { + nv_error(pioc, "timeout: 0x%08x\n", + nv_rd32(priv, 0x610490 + (chid * 0x10))); + if (suspend) + return -EBUSY; + } + + /* disable error reporting and completion notification */ + nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000000); + nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000000); + + return nv50_disp_chan_fini(&pioc->base, suspend); +} + +/******************************************************************************* + * EVO immediate overlay channel objects + ******************************************************************************/ + +struct nv50_disp_chan_impl +gf110_disp_oimm_ofuncs = { + .base.ctor = nv50_disp_oimm_ctor, + .base.dtor = nv50_disp_pioc_dtor, + .base.init = gf110_disp_pioc_init, + .base.fini = gf110_disp_pioc_fini, + .base.ntfy = nv50_disp_chan_ntfy, + .base.map = nv50_disp_chan_map, + .base.rd32 = nv50_disp_chan_rd32, + .base.wr32 = nv50_disp_chan_wr32, + .chid = 9, +}; + +/******************************************************************************* + * EVO cursor channel objects + ******************************************************************************/ + +struct nv50_disp_chan_impl +gf110_disp_curs_ofuncs = { + .base.ctor = nv50_disp_curs_ctor, + .base.dtor = nv50_disp_pioc_dtor, + .base.init = gf110_disp_pioc_init, + .base.fini = gf110_disp_pioc_fini, + .base.ntfy = nv50_disp_chan_ntfy, + .base.map = nv50_disp_chan_map, + .base.rd32 = nv50_disp_chan_rd32, + .base.wr32 = nv50_disp_chan_wr32, + .chid = 13, +}; + +/******************************************************************************* + * Base display object + ******************************************************************************/ + +int +gf110_disp_main_scanoutpos(NV50_DISP_MTHD_V0) +{ + const u32 total = nv_rd32(priv, 0x640414 + (head * 0x300)); + const u32 blanke = nv_rd32(priv, 0x64041c + (head * 0x300)); + const u32 blanks = nv_rd32(priv, 0x640420 + (head * 0x300)); + union { + struct nv04_disp_scanoutpos_v0 v0; + } *args = data; + int ret; + + nv_ioctl(object, "disp scanoutpos size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(object, "disp scanoutpos vers %d\n", args->v0.version); + args->v0.vblanke = (blanke & 0xffff0000) >> 16; + args->v0.hblanke = (blanke & 0x0000ffff); + args->v0.vblanks = (blanks & 0xffff0000) >> 16; + args->v0.hblanks = (blanks & 0x0000ffff); + args->v0.vtotal = ( total & 0xffff0000) >> 16; + args->v0.htotal = ( total & 0x0000ffff); + args->v0.time[0] = ktime_to_ns(ktime_get()); + args->v0.vline = /* vline read locks hline */ + nv_rd32(priv, 0x616340 + (head * 0x800)) & 0xffff; + args->v0.time[1] = ktime_to_ns(ktime_get()); + args->v0.hline = + nv_rd32(priv, 0x616344 + (head * 0x800)) & 0xffff; + } else + return ret; + + return 0; +} + +static int +gf110_disp_main_init(struct nvkm_object *object) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nv50_disp_base *base = (void *)object; + int ret, i; + u32 tmp; + + ret = nvkm_parent_init(&base->base); + if (ret) + return ret; + + /* 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 */ + for (i = 0; i < priv->head.nr; i++) { + tmp = nv_rd32(priv, 0x616104 + (i * 0x800)); + nv_wr32(priv, 0x6101b4 + (i * 0x800), tmp); + tmp = nv_rd32(priv, 0x616108 + (i * 0x800)); + nv_wr32(priv, 0x6101b8 + (i * 0x800), tmp); + tmp = nv_rd32(priv, 0x61610c + (i * 0x800)); + nv_wr32(priv, 0x6101bc + (i * 0x800), tmp); + } + + /* ... DAC caps */ + for (i = 0; i < priv->dac.nr; i++) { + tmp = nv_rd32(priv, 0x61a000 + (i * 0x800)); + nv_wr32(priv, 0x6101c0 + (i * 0x800), tmp); + } + + /* ... SOR caps */ + for (i = 0; i < priv->sor.nr; i++) { + tmp = nv_rd32(priv, 0x61c000 + (i * 0x800)); + nv_wr32(priv, 0x6301c4 + (i * 0x800), tmp); + } + + /* steal display away from vbios, or something like that */ + if (nv_rd32(priv, 0x6100ac) & 0x00000100) { + nv_wr32(priv, 0x6100ac, 0x00000100); + nv_mask(priv, 0x6194e8, 0x00000001, 0x00000000); + if (!nv_wait(priv, 0x6194e8, 0x00000002, 0x00000000)) { + nv_error(priv, "timeout acquiring display\n"); + return -EBUSY; + } + } + + /* point at display engine memory area (hash table, objects) */ + nv_wr32(priv, 0x610010, (nv_gpuobj(object->parent)->addr >> 8) | 9); + + /* enable supervisor interrupts, disable everything else */ + nv_wr32(priv, 0x610090, 0x00000000); + nv_wr32(priv, 0x6100a0, 0x00000000); + nv_wr32(priv, 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 + */ + for (i = 0; i < priv->head.nr; i++) + nv_mask(priv, 0x616308 + (i * 0x800), 0x00000111, 0x00000010); + + return 0; +} + +static int +gf110_disp_main_fini(struct nvkm_object *object, bool suspend) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nv50_disp_base *base = (void *)object; + + /* disable all interrupts */ + nv_wr32(priv, 0x6100b0, 0x00000000); + + return nvkm_parent_fini(&base->base, suspend); +} + +struct nvkm_ofuncs +gf110_disp_main_ofuncs = { + .ctor = nv50_disp_main_ctor, + .dtor = nv50_disp_main_dtor, + .init = gf110_disp_main_init, + .fini = gf110_disp_main_fini, + .mthd = nv50_disp_main_mthd, + .ntfy = nvkm_disp_ntfy, +}; + +static struct nvkm_oclass +gf110_disp_main_oclass[] = { + { GF110_DISP, &gf110_disp_main_ofuncs }, + {} +}; + +static struct nvkm_oclass +gf110_disp_sclass[] = { + { GF110_DISP_CORE_CHANNEL_DMA, &gf110_disp_core_ofuncs.base }, + { GF110_DISP_BASE_CHANNEL_DMA, &gf110_disp_base_ofuncs.base }, + { GF110_DISP_OVERLAY_CONTROL_DMA, &gf110_disp_ovly_ofuncs.base }, + { GF110_DISP_OVERLAY, &gf110_disp_oimm_ofuncs.base }, + { GF110_DISP_CURSOR, &gf110_disp_curs_ofuncs.base }, + {} +}; + +/******************************************************************************* + * Display engine implementation + ******************************************************************************/ + +static void +gf110_disp_vblank_init(struct nvkm_event *event, int type, int head) +{ + struct nvkm_disp *disp = container_of(event, typeof(*disp), vblank); + nv_mask(disp, 0x6100c0 + (head * 0x800), 0x00000001, 0x00000001); +} + +static void +gf110_disp_vblank_fini(struct nvkm_event *event, int type, int head) +{ + struct nvkm_disp *disp = container_of(event, typeof(*disp), vblank); + nv_mask(disp, 0x6100c0 + (head * 0x800), 0x00000001, 0x00000000); +} + +const struct nvkm_event_func +gf110_disp_vblank_func = { + .ctor = nvkm_disp_vblank_ctor, + .init = gf110_disp_vblank_init, + .fini = gf110_disp_vblank_fini, +}; + +static struct nvkm_output * +exec_lookup(struct nv50_disp_priv *priv, int head, int or, u32 ctrl, + u32 *data, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_outp *info) +{ + struct nvkm_bios *bios = nvkm_bios(priv); + struct nvkm_output *outp; + u16 mask, type; + + if (or < 4) { + type = DCB_OUTPUT_ANALOG; + mask = 0; + } else { + or -= 4; + switch (ctrl & 0x00000f00) { + case 0x00000000: type = DCB_OUTPUT_LVDS; mask = 1; break; + case 0x00000100: type = DCB_OUTPUT_TMDS; mask = 1; break; + case 0x00000200: type = DCB_OUTPUT_TMDS; mask = 2; break; + case 0x00000500: type = DCB_OUTPUT_TMDS; mask = 3; break; + case 0x00000800: type = DCB_OUTPUT_DP; mask = 1; break; + case 0x00000900: type = DCB_OUTPUT_DP; mask = 2; break; + default: + nv_error(priv, "unknown SOR mc 0x%08x\n", ctrl); + return 0x0000; + } + } + + mask = 0x00c0 & (mask << 6); + mask |= 0x0001 << or; + mask |= 0x0100 << head; + + list_for_each_entry(outp, &priv->base.outp, head) { + if ((outp->info.hasht & 0xff) == type && + (outp->info.hashm & mask) == mask) { + *data = nvbios_outp_match(bios, outp->info.hasht, + outp->info.hashm, + ver, hdr, cnt, len, info); + if (!*data) + return NULL; + return outp; + } + } + + return NULL; +} + +static struct nvkm_output * +exec_script(struct nv50_disp_priv *priv, int head, int id) +{ + struct nvkm_bios *bios = nvkm_bios(priv); + struct nvkm_output *outp; + struct nvbios_outp info; + u8 ver, hdr, cnt, len; + u32 data, ctrl = 0; + int or; + + for (or = 0; !(ctrl & (1 << head)) && or < 8; or++) { + ctrl = nv_rd32(priv, 0x640180 + (or * 0x20)); + if (ctrl & (1 << head)) + break; + } + + if (or == 8) + return NULL; + + outp = exec_lookup(priv, head, or, ctrl, &data, &ver, &hdr, &cnt, &len, &info); + if (outp) { + struct nvbios_init init = { + .subdev = nv_subdev(priv), + .bios = bios, + .offset = info.script[id], + .outp = &outp->info, + .crtc = head, + .execute = 1, + }; + + nvbios_exec(&init); + } + + return outp; +} + +static struct nvkm_output * +exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk, u32 *conf) +{ + struct nvkm_bios *bios = nvkm_bios(priv); + struct nvkm_output *outp; + struct nvbios_outp info1; + struct nvbios_ocfg info2; + u8 ver, hdr, cnt, len; + u32 data, ctrl = 0; + int or; + + for (or = 0; !(ctrl & (1 << head)) && or < 8; or++) { + ctrl = nv_rd32(priv, 0x660180 + (or * 0x20)); + if (ctrl & (1 << head)) + break; + } + + if (or == 8) + return NULL; + + outp = exec_lookup(priv, head, or, ctrl, &data, &ver, &hdr, &cnt, &len, &info1); + if (!outp) + return NULL; + + switch (outp->info.type) { + case DCB_OUTPUT_TMDS: + *conf = (ctrl & 0x00000f00) >> 8; + if (pclk >= 165000) + *conf |= 0x0100; + break; + case DCB_OUTPUT_LVDS: + *conf = priv->sor.lvdsconf; + break; + case DCB_OUTPUT_DP: + *conf = (ctrl & 0x00000f00) >> 8; + break; + case DCB_OUTPUT_ANALOG: + default: + *conf = 0x00ff; + break; + } + + data = nvbios_ocfg_match(bios, data, *conf, &ver, &hdr, &cnt, &len, &info2); + if (data && id < 0xff) { + data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk); + if (data) { + struct nvbios_init init = { + .subdev = nv_subdev(priv), + .bios = bios, + .offset = data, + .outp = &outp->info, + .crtc = head, + .execute = 1, + }; + + nvbios_exec(&init); + } + } + + return outp; +} + +static void +gf110_disp_intr_unk1_0(struct nv50_disp_priv *priv, int head) +{ + exec_script(priv, head, 1); +} + +static void +gf110_disp_intr_unk2_0(struct nv50_disp_priv *priv, int head) +{ + struct nvkm_output *outp = exec_script(priv, head, 2); + + /* see note in nv50_disp_intr_unk20_0() */ + if (outp && outp->info.type == DCB_OUTPUT_DP) { + struct nvkm_output_dp *outpdp = (void *)outp; + struct nvbios_init init = { + .subdev = nv_subdev(priv), + .bios = nvkm_bios(priv), + .outp = &outp->info, + .crtc = head, + .offset = outpdp->info.script[4], + .execute = 1, + }; + + nvbios_exec(&init); + atomic_set(&outpdp->lt.done, 0); + } +} + +static void +gf110_disp_intr_unk2_1(struct nv50_disp_priv *priv, int head) +{ + struct nvkm_devinit *devinit = nvkm_devinit(priv); + u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000; + if (pclk) + devinit->pll_set(devinit, PLL_VPLL0 + head, pclk); + nv_wr32(priv, 0x612200 + (head * 0x800), 0x00000000); +} + +static void +gf110_disp_intr_unk2_2_tu(struct nv50_disp_priv *priv, int head, + struct dcb_output *outp) +{ + const int or = ffs(outp->or) - 1; + const u32 ctrl = nv_rd32(priv, 0x660200 + (or * 0x020)); + const u32 conf = nv_rd32(priv, 0x660404 + (head * 0x300)); + const s32 vactive = nv_rd32(priv, 0x660414 + (head * 0x300)) & 0xffff; + const s32 vblanke = nv_rd32(priv, 0x66041c + (head * 0x300)) & 0xffff; + const s32 vblanks = nv_rd32(priv, 0x660420 + (head * 0x300)) & 0xffff; + const u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000; + const u32 link = ((ctrl & 0xf00) == 0x800) ? 0 : 1; + const u32 hoff = (head * 0x800); + const u32 soff = ( or * 0x800); + const u32 loff = (link * 0x080) + soff; + const u32 symbol = 100000; + const u32 TU = 64; + u32 dpctrl = nv_rd32(priv, 0x61c10c + loff); + u32 clksor = nv_rd32(priv, 0x612300 + soff); + u32 datarate, link_nr, link_bw, bits; + u64 ratio, value; + + link_nr = hweight32(dpctrl & 0x000f0000); + link_bw = (clksor & 0x007c0000) >> 18; + link_bw *= 27000; + + /* symbols/hblank - algorithm taken from comments in tegra driver */ + value = vblanke + vactive - vblanks - 7; + value = value * link_bw; + do_div(value, pclk); + value = value - (3 * !!(dpctrl & 0x00004000)) - (12 / link_nr); + nv_mask(priv, 0x616620 + hoff, 0x0000ffff, value); + + /* symbols/vblank - algorithm taken from comments in tegra driver */ + value = vblanks - vblanke - 25; + value = value * link_bw; + do_div(value, pclk); + value = value - ((36 / link_nr) + 3) - 1; + nv_mask(priv, 0x616624 + hoff, 0x00ffffff, value); + + /* watermark */ + if ((conf & 0x3c0) == 0x180) bits = 30; + else if ((conf & 0x3c0) == 0x140) bits = 24; + else bits = 18; + datarate = (pclk * bits) / 8; + + ratio = datarate; + ratio *= symbol; + do_div(ratio, link_nr * link_bw); + + value = (symbol - ratio) * TU; + value *= ratio; + do_div(value, symbol); + do_div(value, symbol); + + value += 5; + value |= 0x08000000; + + nv_wr32(priv, 0x616610 + hoff, value); +} + +static void +gf110_disp_intr_unk2_2(struct nv50_disp_priv *priv, int head) +{ + struct nvkm_output *outp; + u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000; + u32 conf, addr, data; + + outp = exec_clkcmp(priv, head, 0xff, pclk, &conf); + if (!outp) + return; + + /* see note in nv50_disp_intr_unk20_2() */ + if (outp->info.type == DCB_OUTPUT_DP) { + u32 sync = nv_rd32(priv, 0x660404 + (head * 0x300)); + switch ((sync & 0x000003c0) >> 6) { + case 6: pclk = pclk * 30; break; + case 5: pclk = pclk * 24; break; + case 2: + default: + pclk = pclk * 18; + break; + } + + if (nvkm_output_dp_train(outp, pclk, true)) + ERR("link not trained before attach\n"); + } else { + if (priv->sor.magic) + priv->sor.magic(outp); + } + + exec_clkcmp(priv, head, 0, pclk, &conf); + + if (outp->info.type == DCB_OUTPUT_ANALOG) { + addr = 0x612280 + (ffs(outp->info.or) - 1) * 0x800; + data = 0x00000000; + } else { + addr = 0x612300 + (ffs(outp->info.or) - 1) * 0x800; + data = (conf & 0x0100) ? 0x00000101 : 0x00000000; + switch (outp->info.type) { + case DCB_OUTPUT_TMDS: + nv_mask(priv, addr, 0x007c0000, 0x00280000); + break; + case DCB_OUTPUT_DP: + gf110_disp_intr_unk2_2_tu(priv, head, &outp->info); + break; + default: + break; + } + } + + nv_mask(priv, addr, 0x00000707, data); +} + +static void +gf110_disp_intr_unk4_0(struct nv50_disp_priv *priv, int head) +{ + u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000; + u32 conf; + + exec_clkcmp(priv, head, 1, pclk, &conf); +} + +void +gf110_disp_intr_supervisor(struct work_struct *work) +{ + struct nv50_disp_priv *priv = + container_of(work, struct nv50_disp_priv, supervisor); + struct nv50_disp_impl *impl = (void *)nv_object(priv)->oclass; + u32 mask[4]; + int head; + + nv_debug(priv, "supervisor %d\n", ffs(priv->super)); + for (head = 0; head < priv->head.nr; head++) { + mask[head] = nv_rd32(priv, 0x6101d4 + (head * 0x800)); + nv_debug(priv, "head %d: 0x%08x\n", head, mask[head]); + } + + if (priv->super & 0x00000001) { + nv50_disp_mthd_chan(priv, NV_DBG_DEBUG, 0, impl->mthd.core); + for (head = 0; head < priv->head.nr; head++) { + if (!(mask[head] & 0x00001000)) + continue; + nv_debug(priv, "supervisor 1.0 - head %d\n", head); + gf110_disp_intr_unk1_0(priv, head); + } + } else + if (priv->super & 0x00000002) { + for (head = 0; head < priv->head.nr; head++) { + if (!(mask[head] & 0x00001000)) + continue; + nv_debug(priv, "supervisor 2.0 - head %d\n", head); + gf110_disp_intr_unk2_0(priv, head); + } + for (head = 0; head < priv->head.nr; head++) { + if (!(mask[head] & 0x00010000)) + continue; + nv_debug(priv, "supervisor 2.1 - head %d\n", head); + gf110_disp_intr_unk2_1(priv, head); + } + for (head = 0; head < priv->head.nr; head++) { + if (!(mask[head] & 0x00001000)) + continue; + nv_debug(priv, "supervisor 2.2 - head %d\n", head); + gf110_disp_intr_unk2_2(priv, head); + } + } else + if (priv->super & 0x00000004) { + for (head = 0; head < priv->head.nr; head++) { + if (!(mask[head] & 0x00001000)) + continue; + nv_debug(priv, "supervisor 3.0 - head %d\n", head); + gf110_disp_intr_unk4_0(priv, head); + } + } + + for (head = 0; head < priv->head.nr; head++) + nv_wr32(priv, 0x6101d4 + (head * 0x800), 0x00000000); + nv_wr32(priv, 0x6101d0, 0x80000000); +} + +static void +gf110_disp_intr_error(struct nv50_disp_priv *priv, int chid) +{ + const struct nv50_disp_impl *impl = (void *)nv_object(priv)->oclass; + u32 mthd = nv_rd32(priv, 0x6101f0 + (chid * 12)); + u32 data = nv_rd32(priv, 0x6101f4 + (chid * 12)); + u32 unkn = nv_rd32(priv, 0x6101f8 + (chid * 12)); + + nv_error(priv, "chid %d mthd 0x%04x data 0x%08x " + "0x%08x 0x%08x\n", + chid, (mthd & 0x0000ffc), data, mthd, unkn); + + if (chid == 0) { + switch (mthd & 0xffc) { + case 0x0080: + nv50_disp_mthd_chan(priv, NV_DBG_ERROR, chid - 0, + impl->mthd.core); + break; + default: + break; + } + } else + if (chid <= 4) { + switch (mthd & 0xffc) { + case 0x0080: + nv50_disp_mthd_chan(priv, NV_DBG_ERROR, chid - 1, + impl->mthd.base); + break; + default: + break; + } + } else + if (chid <= 8) { + switch (mthd & 0xffc) { + case 0x0080: + nv50_disp_mthd_chan(priv, NV_DBG_ERROR, chid - 5, + impl->mthd.ovly); + break; + default: + break; + } + } + + nv_wr32(priv, 0x61009c, (1 << chid)); + nv_wr32(priv, 0x6101f0 + (chid * 12), 0x90000000); +} + +void +gf110_disp_intr(struct nvkm_subdev *subdev) +{ + struct nv50_disp_priv *priv = (void *)subdev; + u32 intr = nv_rd32(priv, 0x610088); + int i; + + if (intr & 0x00000001) { + u32 stat = nv_rd32(priv, 0x61008c); + while (stat) { + int chid = __ffs(stat); stat &= ~(1 << chid); + nv50_disp_chan_uevent_send(priv, chid); + nv_wr32(priv, 0x61008c, 1 << chid); + } + intr &= ~0x00000001; + } + + if (intr & 0x00000002) { + u32 stat = nv_rd32(priv, 0x61009c); + int chid = ffs(stat) - 1; + if (chid >= 0) + gf110_disp_intr_error(priv, chid); + intr &= ~0x00000002; + } + + if (intr & 0x00100000) { + u32 stat = nv_rd32(priv, 0x6100ac); + if (stat & 0x00000007) { + priv->super = (stat & 0x00000007); + schedule_work(&priv->supervisor); + nv_wr32(priv, 0x6100ac, priv->super); + stat &= ~0x00000007; + } + + if (stat) { + nv_info(priv, "unknown intr24 0x%08x\n", stat); + nv_wr32(priv, 0x6100ac, stat); + } + + intr &= ~0x00100000; + } + + for (i = 0; i < priv->head.nr; i++) { + u32 mask = 0x01000000 << i; + if (mask & intr) { + u32 stat = nv_rd32(priv, 0x6100bc + (i * 0x800)); + if (stat & 0x00000001) + nvkm_disp_vblank(&priv->base, i); + nv_mask(priv, 0x6100bc + (i * 0x800), 0, 0); + nv_rd32(priv, 0x6100c0 + (i * 0x800)); + } + } +} + +static int +gf110_disp_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv50_disp_priv *priv; + int heads = nv_rd32(parent, 0x022448); + int ret; + + ret = nvkm_disp_create(parent, engine, oclass, heads, + "PDISP", "display", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + ret = nvkm_event_init(&gf110_disp_chan_uevent, 1, 17, &priv->uevent); + if (ret) + return ret; + + nv_engine(priv)->sclass = gf110_disp_main_oclass; + nv_engine(priv)->cclass = &nv50_disp_cclass; + nv_subdev(priv)->intr = gf110_disp_intr; + INIT_WORK(&priv->supervisor, gf110_disp_intr_supervisor); + priv->sclass = gf110_disp_sclass; + priv->head.nr = heads; + priv->dac.nr = 3; + priv->sor.nr = 4; + priv->dac.power = nv50_dac_power; + priv->dac.sense = nv50_dac_sense; + priv->sor.power = nv50_sor_power; + priv->sor.hda_eld = gf110_hda_eld; + priv->sor.hdmi = gf110_hdmi_ctrl; + return 0; +} + +struct nvkm_oclass * +gf110_disp_outp_sclass[] = { + &gf110_sor_dp_impl.base.base, + NULL +}; + +struct nvkm_oclass * +gf110_disp_oclass = &(struct nv50_disp_impl) { + .base.base.handle = NV_ENGINE(DISP, 0x90), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf110_disp_ctor, + .dtor = _nvkm_disp_dtor, + .init = _nvkm_disp_init, + .fini = _nvkm_disp_fini, + }, + .base.vblank = &gf110_disp_vblank_func, + .base.outp = gf110_disp_outp_sclass, + .mthd.core = &gf110_disp_core_mthd_chan, + .mthd.base = &gf110_disp_base_mthd_chan, + .mthd.ovly = &gf110_disp_ovly_mthd_chan, + .mthd.prev = -0x020000, + .head.scanoutpos = gf110_disp_main_scanoutpos, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk104.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk104.c new file mode 100644 index 000000000..6f4019ab4 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk104.c @@ -0,0 +1,268 @@ +/* + * 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 + +/******************************************************************************* + * EVO master channel object + ******************************************************************************/ + +static const struct nv50_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 nv50_disp_mthd_chan +gk104_disp_core_mthd_chan = { + .name = "Core", + .addr = 0x000000, + .data = { + { "Global", 1, &gf110_disp_core_mthd_base }, + { "DAC", 3, &gf110_disp_core_mthd_dac }, + { "SOR", 8, &gf110_disp_core_mthd_sor }, + { "PIOR", 4, &gf110_disp_core_mthd_pior }, + { "HEAD", 4, &gk104_disp_core_mthd_head }, + {} + } +}; + +/******************************************************************************* + * EVO overlay channel objects + ******************************************************************************/ + +static const struct nv50_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 nv50_disp_mthd_chan +gk104_disp_ovly_mthd_chan = { + .name = "Overlay", + .addr = 0x001000, + .data = { + { "Global", 1, &gk104_disp_ovly_mthd_base }, + {} + } +}; + +/******************************************************************************* + * Base display object + ******************************************************************************/ + +static struct nvkm_oclass +gk104_disp_sclass[] = { + { GK104_DISP_CORE_CHANNEL_DMA, &gf110_disp_core_ofuncs.base }, + { GK104_DISP_BASE_CHANNEL_DMA, &gf110_disp_base_ofuncs.base }, + { GK104_DISP_OVERLAY_CONTROL_DMA, &gf110_disp_ovly_ofuncs.base }, + { GK104_DISP_OVERLAY, &gf110_disp_oimm_ofuncs.base }, + { GK104_DISP_CURSOR, &gf110_disp_curs_ofuncs.base }, + {} +}; + +static struct nvkm_oclass +gk104_disp_main_oclass[] = { + { GK104_DISP, &gf110_disp_main_ofuncs }, + {} +}; + +/******************************************************************************* + * Display engine implementation + ******************************************************************************/ + +static int +gk104_disp_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv50_disp_priv *priv; + int heads = nv_rd32(parent, 0x022448); + int ret; + + ret = nvkm_disp_create(parent, engine, oclass, heads, + "PDISP", "display", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + ret = nvkm_event_init(&gf110_disp_chan_uevent, 1, 17, &priv->uevent); + if (ret) + return ret; + + nv_engine(priv)->sclass = gk104_disp_main_oclass; + nv_engine(priv)->cclass = &nv50_disp_cclass; + nv_subdev(priv)->intr = gf110_disp_intr; + INIT_WORK(&priv->supervisor, gf110_disp_intr_supervisor); + priv->sclass = gk104_disp_sclass; + priv->head.nr = heads; + priv->dac.nr = 3; + priv->sor.nr = 4; + priv->dac.power = nv50_dac_power; + priv->dac.sense = nv50_dac_sense; + priv->sor.power = nv50_sor_power; + priv->sor.hda_eld = gf110_hda_eld; + priv->sor.hdmi = gk104_hdmi_ctrl; + return 0; +} + +struct nvkm_oclass * +gk104_disp_oclass = &(struct nv50_disp_impl) { + .base.base.handle = NV_ENGINE(DISP, 0x91), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gk104_disp_ctor, + .dtor = _nvkm_disp_dtor, + .init = _nvkm_disp_init, + .fini = _nvkm_disp_fini, + }, + .base.vblank = &gf110_disp_vblank_func, + .base.outp = gf110_disp_outp_sclass, + .mthd.core = &gk104_disp_core_mthd_chan, + .mthd.base = &gf110_disp_base_mthd_chan, + .mthd.ovly = &gk104_disp_ovly_mthd_chan, + .mthd.prev = -0x020000, + .head.scanoutpos = gf110_disp_main_scanoutpos, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk110.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk110.c new file mode 100644 index 000000000..daa4b460a --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk110.c @@ -0,0 +1,103 @@ +/* + * 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 + +/******************************************************************************* + * Base display object + ******************************************************************************/ + +static struct nvkm_oclass +gk110_disp_sclass[] = { + { GK110_DISP_CORE_CHANNEL_DMA, &gf110_disp_core_ofuncs.base }, + { GK110_DISP_BASE_CHANNEL_DMA, &gf110_disp_base_ofuncs.base }, + { GK104_DISP_OVERLAY_CONTROL_DMA, &gf110_disp_ovly_ofuncs.base }, + { GK104_DISP_OVERLAY, &gf110_disp_oimm_ofuncs.base }, + { GK104_DISP_CURSOR, &gf110_disp_curs_ofuncs.base }, + {} +}; + +static struct nvkm_oclass +gk110_disp_main_oclass[] = { + { GK110_DISP, &gf110_disp_main_ofuncs }, + {} +}; + +/******************************************************************************* + * Display engine implementation + ******************************************************************************/ + +static int +gk110_disp_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv50_disp_priv *priv; + int heads = nv_rd32(parent, 0x022448); + int ret; + + ret = nvkm_disp_create(parent, engine, oclass, heads, + "PDISP", "display", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + ret = nvkm_event_init(&gf110_disp_chan_uevent, 1, 17, &priv->uevent); + if (ret) + return ret; + + nv_engine(priv)->sclass = gk110_disp_main_oclass; + nv_engine(priv)->cclass = &nv50_disp_cclass; + nv_subdev(priv)->intr = gf110_disp_intr; + INIT_WORK(&priv->supervisor, gf110_disp_intr_supervisor); + priv->sclass = gk110_disp_sclass; + priv->head.nr = heads; + priv->dac.nr = 3; + priv->sor.nr = 4; + priv->dac.power = nv50_dac_power; + priv->dac.sense = nv50_dac_sense; + priv->sor.power = nv50_sor_power; + priv->sor.hda_eld = gf110_hda_eld; + priv->sor.hdmi = gk104_hdmi_ctrl; + return 0; +} + +struct nvkm_oclass * +gk110_disp_oclass = &(struct nv50_disp_impl) { + .base.base.handle = NV_ENGINE(DISP, 0x92), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gk110_disp_ctor, + .dtor = _nvkm_disp_dtor, + .init = _nvkm_disp_init, + .fini = _nvkm_disp_fini, + }, + .base.vblank = &gf110_disp_vblank_func, + .base.outp = gf110_disp_outp_sclass, + .mthd.core = &gk104_disp_core_mthd_chan, + .mthd.base = &gf110_disp_base_mthd_chan, + .mthd.ovly = &gk104_disp_ovly_mthd_chan, + .mthd.prev = -0x020000, + .head.scanoutpos = gf110_disp_main_scanoutpos, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm107.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm107.c new file mode 100644 index 000000000..881cc9438 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm107.c @@ -0,0 +1,103 @@ +/* + * 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 + +/******************************************************************************* + * Base display object + ******************************************************************************/ + +static struct nvkm_oclass +gm107_disp_sclass[] = { + { GM107_DISP_CORE_CHANNEL_DMA, &gf110_disp_core_ofuncs.base }, + { GK110_DISP_BASE_CHANNEL_DMA, &gf110_disp_base_ofuncs.base }, + { GK104_DISP_OVERLAY_CONTROL_DMA, &gf110_disp_ovly_ofuncs.base }, + { GK104_DISP_OVERLAY, &gf110_disp_oimm_ofuncs.base }, + { GK104_DISP_CURSOR, &gf110_disp_curs_ofuncs.base }, + {} +}; + +static struct nvkm_oclass +gm107_disp_main_oclass[] = { + { GM107_DISP, &gf110_disp_main_ofuncs }, + {} +}; + +/******************************************************************************* + * Display engine implementation + ******************************************************************************/ + +static int +gm107_disp_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv50_disp_priv *priv; + int heads = nv_rd32(parent, 0x022448); + int ret; + + ret = nvkm_disp_create(parent, engine, oclass, heads, + "PDISP", "display", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + ret = nvkm_event_init(&gf110_disp_chan_uevent, 1, 17, &priv->uevent); + if (ret) + return ret; + + nv_engine(priv)->sclass = gm107_disp_main_oclass; + nv_engine(priv)->cclass = &nv50_disp_cclass; + nv_subdev(priv)->intr = gf110_disp_intr; + INIT_WORK(&priv->supervisor, gf110_disp_intr_supervisor); + priv->sclass = gm107_disp_sclass; + priv->head.nr = heads; + priv->dac.nr = 3; + priv->sor.nr = 4; + priv->dac.power = nv50_dac_power; + priv->dac.sense = nv50_dac_sense; + priv->sor.power = nv50_sor_power; + priv->sor.hda_eld = gf110_hda_eld; + priv->sor.hdmi = gk104_hdmi_ctrl; + return 0; +} + +struct nvkm_oclass * +gm107_disp_oclass = &(struct nv50_disp_impl) { + .base.base.handle = NV_ENGINE(DISP, 0x07), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gm107_disp_ctor, + .dtor = _nvkm_disp_dtor, + .init = _nvkm_disp_init, + .fini = _nvkm_disp_fini, + }, + .base.vblank = &gf110_disp_vblank_func, + .base.outp = gf110_disp_outp_sclass, + .mthd.core = &gk104_disp_core_mthd_chan, + .mthd.base = &gf110_disp_base_mthd_chan, + .mthd.ovly = &gk104_disp_ovly_mthd_chan, + .mthd.prev = -0x020000, + .head.scanoutpos = gf110_disp_main_scanoutpos, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm204.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm204.c new file mode 100644 index 000000000..67004f830 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm204.c @@ -0,0 +1,111 @@ +/* + * 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 "outpdp.h" + +#include + +/******************************************************************************* + * Base display object + ******************************************************************************/ + +static struct nvkm_oclass +gm204_disp_sclass[] = { + { GM204_DISP_CORE_CHANNEL_DMA, &gf110_disp_core_ofuncs.base }, + { GK110_DISP_BASE_CHANNEL_DMA, &gf110_disp_base_ofuncs.base }, + { GK104_DISP_OVERLAY_CONTROL_DMA, &gf110_disp_ovly_ofuncs.base }, + { GK104_DISP_OVERLAY, &gf110_disp_oimm_ofuncs.base }, + { GK104_DISP_CURSOR, &gf110_disp_curs_ofuncs.base }, + {} +}; + +static struct nvkm_oclass +gm204_disp_main_oclass[] = { + { GM204_DISP, &gf110_disp_main_ofuncs }, + {} +}; + +/******************************************************************************* + * Display engine implementation + ******************************************************************************/ + +static int +gm204_disp_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv50_disp_priv *priv; + int heads = nv_rd32(parent, 0x022448); + int ret; + + ret = nvkm_disp_create(parent, engine, oclass, heads, + "PDISP", "display", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + ret = nvkm_event_init(&gf110_disp_chan_uevent, 1, 17, &priv->uevent); + if (ret) + return ret; + + nv_engine(priv)->sclass = gm204_disp_main_oclass; + nv_engine(priv)->cclass = &nv50_disp_cclass; + nv_subdev(priv)->intr = gf110_disp_intr; + INIT_WORK(&priv->supervisor, gf110_disp_intr_supervisor); + priv->sclass = gm204_disp_sclass; + priv->head.nr = heads; + priv->dac.nr = 3; + priv->sor.nr = 4; + priv->dac.power = nv50_dac_power; + priv->dac.sense = nv50_dac_sense; + priv->sor.power = nv50_sor_power; + priv->sor.hda_eld = gf110_hda_eld; + priv->sor.hdmi = gf110_hdmi_ctrl; + priv->sor.magic = gm204_sor_magic; + return 0; +} + +struct nvkm_oclass * +gm204_disp_outp_sclass[] = { + &gm204_sor_dp_impl.base.base, + NULL +}; + +struct nvkm_oclass * +gm204_disp_oclass = &(struct nv50_disp_impl) { + .base.base.handle = NV_ENGINE(DISP, 0x07), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gm204_disp_ctor, + .dtor = _nvkm_disp_dtor, + .init = _nvkm_disp_init, + .fini = _nvkm_disp_fini, + }, + .base.vblank = &gf110_disp_vblank_func, + .base.outp = gm204_disp_outp_sclass, + .mthd.core = &gk104_disp_core_mthd_chan, + .mthd.base = &gf110_disp_base_mthd_chan, + .mthd.ovly = &gk104_disp_ovly_mthd_chan, + .mthd.prev = -0x020000, + .head.scanoutpos = gf110_disp_main_scanoutpos, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt200.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt200.c new file mode 100644 index 000000000..a45307213 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt200.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 + +/******************************************************************************* + * EVO overlay channel objects + ******************************************************************************/ + +static const struct nv50_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 nv50_disp_mthd_chan +gt200_disp_ovly_mthd_chan = { + .name = "Overlay", + .addr = 0x000540, + .data = { + { "Global", 1, >200_disp_ovly_mthd_base }, + {} + } +}; + +/******************************************************************************* + * Base display object + ******************************************************************************/ + +static struct nvkm_oclass +gt200_disp_sclass[] = { + { GT200_DISP_CORE_CHANNEL_DMA, &nv50_disp_core_ofuncs.base }, + { GT200_DISP_BASE_CHANNEL_DMA, &nv50_disp_base_ofuncs.base }, + { GT200_DISP_OVERLAY_CHANNEL_DMA, &nv50_disp_ovly_ofuncs.base }, + { G82_DISP_OVERLAY, &nv50_disp_oimm_ofuncs.base }, + { G82_DISP_CURSOR, &nv50_disp_curs_ofuncs.base }, + {} +}; + +static struct nvkm_oclass +gt200_disp_main_oclass[] = { + { GT200_DISP, &nv50_disp_main_ofuncs }, + {} +}; + +/******************************************************************************* + * Display engine implementation + ******************************************************************************/ + +static int +gt200_disp_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv50_disp_priv *priv; + int ret; + + ret = nvkm_disp_create(parent, engine, oclass, 2, "PDISP", + "display", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + ret = nvkm_event_init(&nv50_disp_chan_uevent, 1, 9, &priv->uevent); + if (ret) + return ret; + + nv_engine(priv)->sclass = gt200_disp_main_oclass; + nv_engine(priv)->cclass = &nv50_disp_cclass; + nv_subdev(priv)->intr = nv50_disp_intr; + INIT_WORK(&priv->supervisor, nv50_disp_intr_supervisor); + priv->sclass = gt200_disp_sclass; + priv->head.nr = 2; + priv->dac.nr = 3; + priv->sor.nr = 2; + priv->pior.nr = 3; + priv->dac.power = nv50_dac_power; + priv->dac.sense = nv50_dac_sense; + priv->sor.power = nv50_sor_power; + priv->sor.hdmi = g84_hdmi_ctrl; + priv->pior.power = nv50_pior_power; + return 0; +} + +struct nvkm_oclass * +gt200_disp_oclass = &(struct nv50_disp_impl) { + .base.base.handle = NV_ENGINE(DISP, 0x83), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gt200_disp_ctor, + .dtor = _nvkm_disp_dtor, + .init = _nvkm_disp_init, + .fini = _nvkm_disp_fini, + }, + .base.vblank = &nv50_disp_vblank_func, + .base.outp = nv50_disp_outp_sclass, + .mthd.core = &g84_disp_core_mthd_chan, + .mthd.base = &g84_disp_base_mthd_chan, + .mthd.ovly = >200_disp_ovly_mthd_chan, + .mthd.prev = 0x000004, + .head.scanoutpos = nv50_disp_main_scanoutpos, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt215.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt215.c new file mode 100644 index 000000000..55f0d3ac5 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt215.c @@ -0,0 +1,104 @@ +/* + * 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 + +/******************************************************************************* + * Base display object + ******************************************************************************/ + +static struct nvkm_oclass +gt215_disp_sclass[] = { + { GT214_DISP_CORE_CHANNEL_DMA, &nv50_disp_core_ofuncs.base }, + { GT214_DISP_BASE_CHANNEL_DMA, &nv50_disp_base_ofuncs.base }, + { GT214_DISP_OVERLAY_CHANNEL_DMA, &nv50_disp_ovly_ofuncs.base }, + { GT214_DISP_OVERLAY, &nv50_disp_oimm_ofuncs.base }, + { GT214_DISP_CURSOR, &nv50_disp_curs_ofuncs.base }, + {} +}; + +static struct nvkm_oclass +gt215_disp_main_oclass[] = { + { GT214_DISP, &nv50_disp_main_ofuncs }, + {} +}; + +/******************************************************************************* + * Display engine implementation + ******************************************************************************/ + +static int +gt215_disp_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv50_disp_priv *priv; + int ret; + + ret = nvkm_disp_create(parent, engine, oclass, 2, "PDISP", + "display", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + ret = nvkm_event_init(&nv50_disp_chan_uevent, 1, 9, &priv->uevent); + if (ret) + return ret; + + nv_engine(priv)->sclass = gt215_disp_main_oclass; + nv_engine(priv)->cclass = &nv50_disp_cclass; + nv_subdev(priv)->intr = nv50_disp_intr; + INIT_WORK(&priv->supervisor, nv50_disp_intr_supervisor); + priv->sclass = gt215_disp_sclass; + priv->head.nr = 2; + priv->dac.nr = 3; + priv->sor.nr = 4; + priv->pior.nr = 3; + priv->dac.power = nv50_dac_power; + priv->dac.sense = nv50_dac_sense; + priv->sor.power = nv50_sor_power; + priv->sor.hda_eld = gt215_hda_eld; + priv->sor.hdmi = gt215_hdmi_ctrl; + priv->pior.power = nv50_pior_power; + return 0; +} + +struct nvkm_oclass * +gt215_disp_oclass = &(struct nv50_disp_impl) { + .base.base.handle = NV_ENGINE(DISP, 0x85), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gt215_disp_ctor, + .dtor = _nvkm_disp_dtor, + .init = _nvkm_disp_init, + .fini = _nvkm_disp_fini, + }, + .base.vblank = &nv50_disp_vblank_func, + .base.outp = g94_disp_outp_sclass, + .mthd.core = &g94_disp_core_mthd_chan, + .mthd.base = &g84_disp_base_mthd_chan, + .mthd.ovly = &g84_disp_ovly_mthd_chan, + .mthd.prev = 0x000004, + .head.scanoutpos = nv50_disp_main_scanoutpos, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagf110.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagf110.c new file mode 100644 index 000000000..b9813d246 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagf110.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 "nv50.h" +#include "outp.h" + +#include +#include +#include +#include + +#include +#include + +int +gf110_hda_eld(NV50_DISP_MTHD_V1) +{ + union { + struct nv50_disp_sor_hda_eld_v0 v0; + } *args = data; + const u32 soff = outp->or * 0x030; + const u32 hoff = head * 0x800; + int ret, i; + + nv_ioctl(object, "disp sor hda eld size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, true)) { + nv_ioctl(object, "disp sor hda eld vers %d\n", args->v0.version); + if (size > 0x60) + return -E2BIG; + } else + return ret; + + if (size && args->v0.data[0]) { + if (outp->info.type == DCB_OUTPUT_DP) { + nv_mask(priv, 0x616618 + hoff, 0x8000000c, 0x80000001); + nv_wait(priv, 0x616618 + hoff, 0x80000000, 0x00000000); + } + nv_mask(priv, 0x616548 + hoff, 0x00000070, 0x00000000); + for (i = 0; i < size; i++) + nv_wr32(priv, 0x10ec00 + soff, (i << 8) | args->v0.data[i]); + for (; i < 0x60; i++) + nv_wr32(priv, 0x10ec00 + soff, (i << 8)); + nv_mask(priv, 0x10ec10 + soff, 0x80000003, 0x80000003); + } else { + if (outp->info.type == DCB_OUTPUT_DP) { + nv_mask(priv, 0x616618 + hoff, 0x80000001, 0x80000000); + nv_wait(priv, 0x616618 + hoff, 0x80000000, 0x00000000); + } + nv_mask(priv, 0x10ec10 + soff, 0x80000003, 0x80000000 | !!size); + } + + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c new file mode 100644 index 000000000..891d1e7bf --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.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 "nv50.h" +#include "outp.h" + +#include +#include + +#include +#include + +int +gt215_hda_eld(NV50_DISP_MTHD_V1) +{ + union { + struct nv50_disp_sor_hda_eld_v0 v0; + } *args = data; + const u32 soff = outp->or * 0x800; + int ret, i; + + nv_ioctl(object, "disp sor hda eld size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, true)) { + nv_ioctl(object, "disp sor hda eld vers %d\n", args->v0.version); + if (size > 0x60) + return -E2BIG; + } else + return ret; + + if (size && args->v0.data[0]) { + if (outp->info.type == DCB_OUTPUT_DP) { + nv_mask(priv, 0x61c1e0 + soff, 0x8000000d, 0x80000001); + nv_wait(priv, 0x61c1e0 + soff, 0x80000000, 0x00000000); + } + for (i = 0; i < size; i++) + nv_wr32(priv, 0x61c440 + soff, (i << 8) | args->v0.data[0]); + for (; i < 0x60; i++) + nv_wr32(priv, 0x61c440 + soff, (i << 8)); + nv_mask(priv, 0x61c448 + soff, 0x80000003, 0x80000003); + } else { + if (outp->info.type == DCB_OUTPUT_DP) { + nv_mask(priv, 0x61c1e0 + soff, 0x80000001, 0x80000000); + nv_wait(priv, 0x61c1e0 + soff, 0x80000000, 0x00000000); + } + nv_mask(priv, 0x61c448 + soff, 0x80000003, 0x80000000 | !!size); + } + + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c new file mode 100644 index 000000000..621cb0b7f --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c @@ -0,0 +1,91 @@ +/* + * 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 + +int +g84_hdmi_ctrl(NV50_DISP_MTHD_V1) +{ + const u32 hoff = (head * 0x800); + union { + struct nv50_disp_sor_hdmi_pwr_v0 v0; + } *args = data; + u32 ctrl; + int ret; + + nv_ioctl(object, "disp sor hdmi ctrl size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(object, "disp sor hdmi ctrl vers %d state %d " + "max_ac_packet %d rekey %d\n", + args->v0.version, args->v0.state, + args->v0.max_ac_packet, args->v0.rekey); + if (args->v0.max_ac_packet > 0x1f || args->v0.rekey > 0x7f) + return -EINVAL; + ctrl = 0x40000000 * !!args->v0.state; + ctrl |= args->v0.max_ac_packet << 16; + ctrl |= args->v0.rekey; + ctrl |= 0x1f000000; /* ??? */ + } else + return ret; + + if (!(ctrl & 0x40000000)) { + nv_mask(priv, 0x6165a4 + hoff, 0x40000000, 0x00000000); + nv_mask(priv, 0x616520 + hoff, 0x00000001, 0x00000000); + nv_mask(priv, 0x616500 + hoff, 0x00000001, 0x00000000); + return 0; + } + + /* AVI InfoFrame */ + nv_mask(priv, 0x616520 + hoff, 0x00000001, 0x00000000); + nv_wr32(priv, 0x616528 + hoff, 0x000d0282); + nv_wr32(priv, 0x61652c + hoff, 0x0000006f); + nv_wr32(priv, 0x616530 + hoff, 0x00000000); + nv_wr32(priv, 0x616534 + hoff, 0x00000000); + nv_wr32(priv, 0x616538 + hoff, 0x00000000); + nv_mask(priv, 0x616520 + hoff, 0x00000001, 0x00000001); + + /* Audio InfoFrame */ + nv_mask(priv, 0x616500 + hoff, 0x00000001, 0x00000000); + nv_wr32(priv, 0x616508 + hoff, 0x000a0184); + nv_wr32(priv, 0x61650c + hoff, 0x00000071); + nv_wr32(priv, 0x616510 + hoff, 0x00000000); + nv_mask(priv, 0x616500 + hoff, 0x00000001, 0x00000001); + + nv_mask(priv, 0x6165d0 + hoff, 0x00070001, 0x00010001); /* SPARE, HW_CTS */ + nv_mask(priv, 0x616568 + hoff, 0x00010101, 0x00000000); /* ACR_CTRL, ?? */ + nv_mask(priv, 0x616578 + hoff, 0x80000000, 0x80000000); /* ACR_0441_ENABLE */ + + /* ??? */ + nv_mask(priv, 0x61733c, 0x00100000, 0x00100000); /* RESETF */ + nv_mask(priv, 0x61733c, 0x10000000, 0x10000000); /* LOOKUP_EN */ + nv_mask(priv, 0x61733c, 0x00100000, 0x00000000); /* !RESETF */ + + /* HDMI_CTRL */ + nv_mask(priv, 0x6165a4 + hoff, 0x5f1f007f, ctrl); + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf110.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf110.c new file mode 100644 index 000000000..c28449061 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf110.c @@ -0,0 +1,79 @@ +/* + * 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 + +int +gf110_hdmi_ctrl(NV50_DISP_MTHD_V1) +{ + const u32 hoff = (head * 0x800); + union { + struct nv50_disp_sor_hdmi_pwr_v0 v0; + } *args = data; + u32 ctrl; + int ret; + + nv_ioctl(object, "disp sor hdmi ctrl size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(object, "disp sor hdmi ctrl vers %d state %d " + "max_ac_packet %d rekey %d\n", + args->v0.version, args->v0.state, + args->v0.max_ac_packet, args->v0.rekey); + if (args->v0.max_ac_packet > 0x1f || args->v0.rekey > 0x7f) + return -EINVAL; + ctrl = 0x40000000 * !!args->v0.state; + ctrl |= args->v0.max_ac_packet << 16; + ctrl |= args->v0.rekey; + } else + return ret; + + if (!(ctrl & 0x40000000)) { + nv_mask(priv, 0x616798 + hoff, 0x40000000, 0x00000000); + nv_mask(priv, 0x6167a4 + hoff, 0x00000001, 0x00000000); + nv_mask(priv, 0x616714 + hoff, 0x00000001, 0x00000000); + return 0; + } + + /* AVI InfoFrame */ + nv_mask(priv, 0x616714 + hoff, 0x00000001, 0x00000000); + nv_wr32(priv, 0x61671c + hoff, 0x000d0282); + nv_wr32(priv, 0x616720 + hoff, 0x0000006f); + nv_wr32(priv, 0x616724 + hoff, 0x00000000); + nv_wr32(priv, 0x616728 + hoff, 0x00000000); + nv_wr32(priv, 0x61672c + hoff, 0x00000000); + nv_mask(priv, 0x616714 + hoff, 0x00000001, 0x00000001); + + /* ??? InfoFrame? */ + nv_mask(priv, 0x6167a4 + hoff, 0x00000001, 0x00000000); + nv_wr32(priv, 0x6167ac + hoff, 0x00000010); + nv_mask(priv, 0x6167a4 + hoff, 0x00000001, 0x00000001); + + /* HDMI_CTRL */ + nv_mask(priv, 0x616798 + hoff, 0x401f007f, ctrl); + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c new file mode 100644 index 000000000..ca34ff81a --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.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 "nv50.h" + +#include + +#include +#include + +int +gk104_hdmi_ctrl(NV50_DISP_MTHD_V1) +{ + const u32 hoff = (head * 0x800); + const u32 hdmi = (head * 0x400); + union { + struct nv50_disp_sor_hdmi_pwr_v0 v0; + } *args = data; + u32 ctrl; + int ret; + + nv_ioctl(object, "disp sor hdmi ctrl size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(object, "disp sor hdmi ctrl vers %d state %d " + "max_ac_packet %d rekey %d\n", + args->v0.version, args->v0.state, + args->v0.max_ac_packet, args->v0.rekey); + if (args->v0.max_ac_packet > 0x1f || args->v0.rekey > 0x7f) + return -EINVAL; + ctrl = 0x40000000 * !!args->v0.state; + ctrl |= args->v0.max_ac_packet << 16; + ctrl |= args->v0.rekey; + } else + return ret; + + if (!(ctrl & 0x40000000)) { + nv_mask(priv, 0x616798 + hoff, 0x40000000, 0x00000000); + nv_mask(priv, 0x6900c0 + hdmi, 0x00000001, 0x00000000); + nv_mask(priv, 0x690000 + hdmi, 0x00000001, 0x00000000); + return 0; + } + + /* AVI InfoFrame */ + nv_mask(priv, 0x690000 + hdmi, 0x00000001, 0x00000000); + nv_wr32(priv, 0x690008 + hdmi, 0x000d0282); + nv_wr32(priv, 0x69000c + hdmi, 0x0000006f); + nv_wr32(priv, 0x690010 + hdmi, 0x00000000); + nv_wr32(priv, 0x690014 + hdmi, 0x00000000); + nv_wr32(priv, 0x690018 + hdmi, 0x00000000); + nv_mask(priv, 0x690000 + hdmi, 0x00000001, 0x00000001); + + /* ??? InfoFrame? */ + nv_mask(priv, 0x6900c0 + hdmi, 0x00000001, 0x00000000); + nv_wr32(priv, 0x6900cc + hdmi, 0x00000010); + nv_mask(priv, 0x6900c0 + hdmi, 0x00000001, 0x00000001); + + /* ??? */ + nv_wr32(priv, 0x690080 + hdmi, 0x82000000); + + /* HDMI_CTRL */ + nv_mask(priv, 0x616798 + hoff, 0x401f007f, ctrl); + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c new file mode 100644 index 000000000..b641c167d --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c @@ -0,0 +1,92 @@ +/* + * 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 "outp.h" + +#include + +#include +#include + +int +gt215_hdmi_ctrl(NV50_DISP_MTHD_V1) +{ + const u32 soff = outp->or * 0x800; + union { + struct nv50_disp_sor_hdmi_pwr_v0 v0; + } *args = data; + u32 ctrl; + int ret; + + nv_ioctl(object, "disp sor hdmi ctrl size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(object, "disp sor hdmi ctrl vers %d state %d " + "max_ac_packet %d rekey %d\n", + args->v0.version, args->v0.state, + args->v0.max_ac_packet, args->v0.rekey); + if (args->v0.max_ac_packet > 0x1f || args->v0.rekey > 0x7f) + return -EINVAL; + ctrl = 0x40000000 * !!args->v0.state; + ctrl |= args->v0.max_ac_packet << 16; + ctrl |= args->v0.rekey; + ctrl |= 0x1f000000; /* ??? */ + } else + return ret; + + if (!(ctrl & 0x40000000)) { + nv_mask(priv, 0x61c5a4 + soff, 0x40000000, 0x00000000); + nv_mask(priv, 0x61c520 + soff, 0x00000001, 0x00000000); + nv_mask(priv, 0x61c500 + soff, 0x00000001, 0x00000000); + return 0; + } + + /* AVI InfoFrame */ + nv_mask(priv, 0x61c520 + soff, 0x00000001, 0x00000000); + nv_wr32(priv, 0x61c528 + soff, 0x000d0282); + nv_wr32(priv, 0x61c52c + soff, 0x0000006f); + nv_wr32(priv, 0x61c530 + soff, 0x00000000); + nv_wr32(priv, 0x61c534 + soff, 0x00000000); + nv_wr32(priv, 0x61c538 + soff, 0x00000000); + nv_mask(priv, 0x61c520 + soff, 0x00000001, 0x00000001); + + /* Audio InfoFrame */ + nv_mask(priv, 0x61c500 + soff, 0x00000001, 0x00000000); + nv_wr32(priv, 0x61c508 + soff, 0x000a0184); + nv_wr32(priv, 0x61c50c + soff, 0x00000071); + nv_wr32(priv, 0x61c510 + soff, 0x00000000); + nv_mask(priv, 0x61c500 + soff, 0x00000001, 0x00000001); + + nv_mask(priv, 0x61c5d0 + soff, 0x00070001, 0x00010001); /* SPARE, HW_CTS */ + nv_mask(priv, 0x61c568 + soff, 0x00010101, 0x00000000); /* ACR_CTRL, ?? */ + nv_mask(priv, 0x61c578 + soff, 0x80000000, 0x80000000); /* ACR_0441_ENABLE */ + + /* ??? */ + nv_mask(priv, 0x61733c, 0x00100000, 0x00100000); /* RESETF */ + nv_mask(priv, 0x61733c, 0x10000000, 0x10000000); /* LOOKUP_EN */ + nv_mask(priv, 0x61733c, 0x00100000, 0x00000000); /* !RESETF */ + + /* HDMI_CTRL */ + nv_mask(priv, 0x61c5a4 + soff, 0x5f1f007f, ctrl); + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv04.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv04.c new file mode 100644 index 000000000..ff09b2659 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv04.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 + +struct nv04_disp_priv { + struct nvkm_disp base; +}; + +static int +nv04_disp_scanoutpos(struct nvkm_object *object, struct nv04_disp_priv *priv, + void *data, u32 size, int head) +{ + const u32 hoff = head * 0x2000; + union { + struct nv04_disp_scanoutpos_v0 v0; + } *args = data; + u32 line; + int ret; + + nv_ioctl(object, "disp scanoutpos size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(object, "disp scanoutpos vers %d\n", args->v0.version); + args->v0.vblanks = nv_rd32(priv, 0x680800 + hoff) & 0xffff; + args->v0.vtotal = nv_rd32(priv, 0x680804 + hoff) & 0xffff; + args->v0.vblanke = args->v0.vtotal - 1; + + args->v0.hblanks = nv_rd32(priv, 0x680820 + hoff) & 0xffff; + args->v0.htotal = nv_rd32(priv, 0x680824 + hoff) & 0xffff; + args->v0.hblanke = args->v0.htotal - 1; + + /* + * If output is vga instead of digital then vtotal/htotal is + * invalid 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()); + line = nv_rd32(priv, 0x600868 + hoff); + args->v0.time[1] = ktime_to_ns(ktime_get()); + args->v0.hline = (line & 0xffff0000) >> 16; + args->v0.vline = (line & 0x0000ffff); + } else + return ret; + + return 0; +} + +static int +nv04_disp_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size) +{ + union { + struct nv04_disp_mthd_v0 v0; + } *args = data; + struct nv04_disp_priv *priv = (void *)object->engine; + int head, ret; + + nv_ioctl(object, "disp mthd size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, true)) { + nv_ioctl(object, "disp mthd vers %d mthd %02x head %d\n", + args->v0.version, args->v0.method, args->v0.head); + mthd = args->v0.method; + head = args->v0.head; + } else + return ret; + + if (head < 0 || head >= 2) + return -ENXIO; + + switch (mthd) { + case NV04_DISP_SCANOUTPOS: + return nv04_disp_scanoutpos(object, priv, data, size, head); + default: + break; + } + + return -EINVAL; +} + +static struct nvkm_ofuncs +nv04_disp_ofuncs = { + .ctor = _nvkm_object_ctor, + .dtor = nvkm_object_destroy, + .init = nvkm_object_init, + .fini = nvkm_object_fini, + .mthd = nv04_disp_mthd, + .ntfy = nvkm_disp_ntfy, +}; + +static struct nvkm_oclass +nv04_disp_sclass[] = { + { NV04_DISP, &nv04_disp_ofuncs }, + {}, +}; + +/******************************************************************************* + * Display engine implementation + ******************************************************************************/ + +static void +nv04_disp_vblank_init(struct nvkm_event *event, int type, int head) +{ + struct nvkm_disp *disp = container_of(event, typeof(*disp), vblank); + nv_wr32(disp, 0x600140 + (head * 0x2000) , 0x00000001); +} + +static void +nv04_disp_vblank_fini(struct nvkm_event *event, int type, int head) +{ + struct nvkm_disp *disp = container_of(event, typeof(*disp), vblank); + nv_wr32(disp, 0x600140 + (head * 0x2000) , 0x00000000); +} + +static const struct nvkm_event_func +nv04_disp_vblank_func = { + .ctor = nvkm_disp_vblank_ctor, + .init = nv04_disp_vblank_init, + .fini = nv04_disp_vblank_fini, +}; + +static void +nv04_disp_intr(struct nvkm_subdev *subdev) +{ + struct nv04_disp_priv *priv = (void *)subdev; + u32 crtc0 = nv_rd32(priv, 0x600100); + u32 crtc1 = nv_rd32(priv, 0x602100); + u32 pvideo; + + if (crtc0 & 0x00000001) { + nvkm_disp_vblank(&priv->base, 0); + nv_wr32(priv, 0x600100, 0x00000001); + } + + if (crtc1 & 0x00000001) { + nvkm_disp_vblank(&priv->base, 1); + nv_wr32(priv, 0x602100, 0x00000001); + } + + if (nv_device(priv)->chipset >= 0x10 && + nv_device(priv)->chipset <= 0x40) { + pvideo = nv_rd32(priv, 0x8100); + if (pvideo & ~0x11) + nv_info(priv, "PVIDEO intr: %08x\n", pvideo); + nv_wr32(priv, 0x8100, pvideo); + } +} + +static int +nv04_disp_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv04_disp_priv *priv; + int ret; + + ret = nvkm_disp_create(parent, engine, oclass, 2, "DISPLAY", + "display", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_engine(priv)->sclass = nv04_disp_sclass; + nv_subdev(priv)->intr = nv04_disp_intr; + return 0; +} + +struct nvkm_oclass * +nv04_disp_oclass = &(struct nvkm_disp_impl) { + .base.handle = NV_ENGINE(DISP, 0x04), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_disp_ctor, + .dtor = _nvkm_disp_dtor, + .init = _nvkm_disp_init, + .fini = _nvkm_disp_fini, + }, + .vblank = &nv04_disp_vblank_func, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c new file mode 100644 index 000000000..8ba808df2 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c @@ -0,0 +1,2019 @@ +/* + * 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 "outp.h" +#include "outpdp.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/******************************************************************************* + * EVO channel base class + ******************************************************************************/ + +static int +nv50_disp_chan_create_(struct nvkm_object *parent, + struct nvkm_object *engine, + struct nvkm_oclass *oclass, int head, + int length, void **pobject) +{ + const struct nv50_disp_chan_impl *impl = (void *)oclass->ofuncs; + struct nv50_disp_base *base = (void *)parent; + struct nv50_disp_chan *chan; + int chid = impl->chid + head; + int ret; + + if (base->chan & (1 << chid)) + return -EBUSY; + base->chan |= (1 << chid); + + ret = nvkm_namedb_create_(parent, engine, oclass, 0, NULL, + (1ULL << NVDEV_ENGINE_DMAOBJ), + length, pobject); + chan = *pobject; + if (ret) + return ret; + chan->chid = chid; + + nv_parent(chan)->object_attach = impl->attach; + nv_parent(chan)->object_detach = impl->detach; + return 0; +} + +static void +nv50_disp_chan_destroy(struct nv50_disp_chan *chan) +{ + struct nv50_disp_base *base = (void *)nv_object(chan)->parent; + base->chan &= ~(1 << chan->chid); + nvkm_namedb_destroy(&chan->base); +} + +static void +nv50_disp_chan_uevent_fini(struct nvkm_event *event, int type, int index) +{ + struct nv50_disp_priv *priv = container_of(event, typeof(*priv), uevent); + nv_mask(priv, 0x610028, 0x00000001 << index, 0x00000000 << index); + nv_wr32(priv, 0x610020, 0x00000001 << index); +} + +static void +nv50_disp_chan_uevent_init(struct nvkm_event *event, int types, int index) +{ + struct nv50_disp_priv *priv = container_of(event, typeof(*priv), uevent); + nv_wr32(priv, 0x610020, 0x00000001 << index); + nv_mask(priv, 0x610028, 0x00000001 << index, 0x00000001 << index); +} + +void +nv50_disp_chan_uevent_send(struct nv50_disp_priv *priv, int chid) +{ + struct nvif_notify_uevent_rep { + } rep; + + nvkm_event_send(&priv->uevent, 1, chid, &rep, sizeof(rep)); +} + +int +nv50_disp_chan_uevent_ctor(struct nvkm_object *object, void *data, u32 size, + struct nvkm_notify *notify) +{ + struct nv50_disp_dmac *dmac = (void *)object; + union { + struct nvif_notify_uevent_req none; + } *args = data; + int ret; + + if (nvif_unvers(args->none)) { + notify->size = sizeof(struct nvif_notify_uevent_rep); + notify->types = 1; + notify->index = dmac->base.chid; + return 0; + } + + return ret; +} + +const struct nvkm_event_func +nv50_disp_chan_uevent = { + .ctor = nv50_disp_chan_uevent_ctor, + .init = nv50_disp_chan_uevent_init, + .fini = nv50_disp_chan_uevent_fini, +}; + +int +nv50_disp_chan_ntfy(struct nvkm_object *object, u32 type, + struct nvkm_event **pevent) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + switch (type) { + case NV50_DISP_CORE_CHANNEL_DMA_V0_NTFY_UEVENT: + *pevent = &priv->uevent; + return 0; + default: + break; + } + return -EINVAL; +} + +int +nv50_disp_chan_map(struct nvkm_object *object, u64 *addr, u32 *size) +{ + struct nv50_disp_chan *chan = (void *)object; + *addr = nv_device_resource_start(nv_device(object), 0) + + 0x640000 + (chan->chid * 0x1000); + *size = 0x001000; + return 0; +} + +u32 +nv50_disp_chan_rd32(struct nvkm_object *object, u64 addr) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nv50_disp_chan *chan = (void *)object; + return nv_rd32(priv, 0x640000 + (chan->chid * 0x1000) + addr); +} + +void +nv50_disp_chan_wr32(struct nvkm_object *object, u64 addr, u32 data) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nv50_disp_chan *chan = (void *)object; + nv_wr32(priv, 0x640000 + (chan->chid * 0x1000) + addr, data); +} + +/******************************************************************************* + * EVO DMA channel base class + ******************************************************************************/ + +static int +nv50_disp_dmac_object_attach(struct nvkm_object *parent, + struct nvkm_object *object, u32 name) +{ + struct nv50_disp_base *base = (void *)parent->parent; + struct nv50_disp_chan *chan = (void *)parent; + u32 addr = nv_gpuobj(object)->node->offset; + u32 chid = chan->chid; + u32 data = (chid << 28) | (addr << 10) | chid; + return nvkm_ramht_insert(base->ramht, chid, name, data); +} + +static void +nv50_disp_dmac_object_detach(struct nvkm_object *parent, int cookie) +{ + struct nv50_disp_base *base = (void *)parent->parent; + nvkm_ramht_remove(base->ramht, cookie); +} + +static int +nv50_disp_dmac_create_(struct nvkm_object *parent, + struct nvkm_object *engine, + struct nvkm_oclass *oclass, u32 pushbuf, int head, + int length, void **pobject) +{ + struct nv50_disp_dmac *dmac; + int ret; + + ret = nv50_disp_chan_create_(parent, engine, oclass, head, + length, pobject); + dmac = *pobject; + if (ret) + return ret; + + dmac->pushdma = (void *)nvkm_handle_ref(parent, pushbuf); + if (!dmac->pushdma) + return -ENOENT; + + switch (nv_mclass(dmac->pushdma)) { + case 0x0002: + case 0x003d: + if (dmac->pushdma->limit - dmac->pushdma->start != 0xfff) + return -EINVAL; + + switch (dmac->pushdma->target) { + case NV_MEM_TARGET_VRAM: + dmac->push = 0x00000001 | dmac->pushdma->start >> 8; + break; + case NV_MEM_TARGET_PCI_NOSNOOP: + dmac->push = 0x00000003 | dmac->pushdma->start >> 8; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + return 0; +} + +void +nv50_disp_dmac_dtor(struct nvkm_object *object) +{ + struct nv50_disp_dmac *dmac = (void *)object; + nvkm_object_ref(NULL, (struct nvkm_object **)&dmac->pushdma); + nv50_disp_chan_destroy(&dmac->base); +} + +static int +nv50_disp_dmac_init(struct nvkm_object *object) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nv50_disp_dmac *dmac = (void *)object; + int chid = dmac->base.chid; + int ret; + + ret = nv50_disp_chan_init(&dmac->base); + if (ret) + return ret; + + /* enable error reporting */ + nv_mask(priv, 0x610028, 0x00010000 << chid, 0x00010000 << chid); + + /* initialise channel for dma command submission */ + nv_wr32(priv, 0x610204 + (chid * 0x0010), dmac->push); + nv_wr32(priv, 0x610208 + (chid * 0x0010), 0x00010000); + nv_wr32(priv, 0x61020c + (chid * 0x0010), chid); + nv_mask(priv, 0x610200 + (chid * 0x0010), 0x00000010, 0x00000010); + nv_wr32(priv, 0x640000 + (chid * 0x1000), 0x00000000); + nv_wr32(priv, 0x610200 + (chid * 0x0010), 0x00000013); + + /* wait for it to go inactive */ + if (!nv_wait(priv, 0x610200 + (chid * 0x10), 0x80000000, 0x00000000)) { + nv_error(dmac, "init timeout, 0x%08x\n", + nv_rd32(priv, 0x610200 + (chid * 0x10))); + return -EBUSY; + } + + return 0; +} + +static int +nv50_disp_dmac_fini(struct nvkm_object *object, bool suspend) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nv50_disp_dmac *dmac = (void *)object; + int chid = dmac->base.chid; + + /* deactivate channel */ + nv_mask(priv, 0x610200 + (chid * 0x0010), 0x00001010, 0x00001000); + nv_mask(priv, 0x610200 + (chid * 0x0010), 0x00000003, 0x00000000); + if (!nv_wait(priv, 0x610200 + (chid * 0x10), 0x001e0000, 0x00000000)) { + nv_error(dmac, "fini timeout, 0x%08x\n", + nv_rd32(priv, 0x610200 + (chid * 0x10))); + if (suspend) + return -EBUSY; + } + + /* disable error reporting and completion notifications */ + nv_mask(priv, 0x610028, 0x00010001 << chid, 0x00000000 << chid); + + return nv50_disp_chan_fini(&dmac->base, suspend); +} + +/******************************************************************************* + * EVO master channel object + ******************************************************************************/ + +static void +nv50_disp_mthd_list(struct nv50_disp_priv *priv, int debug, u32 base, int c, + const struct nv50_disp_mthd_list *list, int inst) +{ + struct nvkm_object *disp = nv_object(priv); + int i; + + for (i = 0; list->data[i].mthd; i++) { + if (list->data[i].addr) { + u32 next = nv_rd32(priv, list->data[i].addr + base + 0); + u32 prev = nv_rd32(priv, 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), "-> 0x%08x", next); + else + snprintf(mods, sizeof(mods), "%13c", ' '); + + nv_printk_(disp, debug, "\t0x%04x: 0x%08x %s%s%s\n", + mthd, prev, mods, name ? " // " : "", + name ? name : ""); + } + } +} + +void +nv50_disp_mthd_chan(struct nv50_disp_priv *priv, int debug, int head, + const struct nv50_disp_mthd_chan *chan) +{ + struct nvkm_object *disp = nv_object(priv); + const struct nv50_disp_impl *impl = (void *)disp->oclass; + const struct nv50_disp_mthd_list *list; + int i, j; + + if (debug > nv_subdev(priv)->debug) + return; + + for (i = 0; (list = chan->data[i].mthd) != NULL; i++) { + u32 base = head * chan->addr; + for (j = 0; j < chan->data[i].nr; j++, base += list->addr) { + const char *cname = chan->name; + const char *sname = ""; + char cname_[16], sname_[16]; + + if (chan->addr) { + snprintf(cname_, sizeof(cname_), "%s %d", + chan->name, head); + cname = cname_; + } + + if (chan->data[i].nr > 1) { + snprintf(sname_, sizeof(sname_), " - %s %d", + chan->data[i].name, j); + sname = sname_; + } + + nv_printk_(disp, debug, "%s%s:\n", cname, sname); + nv50_disp_mthd_list(priv, debug, base, impl->mthd.prev, + list, j); + } + } +} + +const struct nv50_disp_mthd_list +nv50_disp_core_mthd_base = { + .mthd = 0x0000, + .addr = 0x000000, + .data = { + { 0x0080, 0x000000 }, + { 0x0084, 0x610bb8 }, + { 0x0088, 0x610b9c }, + { 0x008c, 0x000000 }, + {} + } +}; + +static const struct nv50_disp_mthd_list +nv50_disp_core_mthd_dac = { + .mthd = 0x0080, + .addr = 0x000008, + .data = { + { 0x0400, 0x610b58 }, + { 0x0404, 0x610bdc }, + { 0x0420, 0x610828 }, + {} + } +}; + +const struct nv50_disp_mthd_list +nv50_disp_core_mthd_sor = { + .mthd = 0x0040, + .addr = 0x000008, + .data = { + { 0x0600, 0x610b70 }, + {} + } +}; + +const struct nv50_disp_mthd_list +nv50_disp_core_mthd_pior = { + .mthd = 0x0040, + .addr = 0x000008, + .data = { + { 0x0700, 0x610b80 }, + {} + } +}; + +static const struct nv50_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 nv50_disp_mthd_chan +nv50_disp_core_mthd_chan = { + .name = "Core", + .addr = 0x000000, + .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 }, + {} + } +}; + +int +nv50_disp_core_ctor(struct nvkm_object *parent, + struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + union { + struct nv50_disp_core_channel_dma_v0 v0; + } *args = data; + struct nv50_disp_dmac *mast; + int ret; + + nv_ioctl(parent, "create disp core channel dma size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(parent, "create disp core channel dma vers %d " + "pushbuf %08x\n", + args->v0.version, args->v0.pushbuf); + } else + return ret; + + ret = nv50_disp_dmac_create_(parent, engine, oclass, args->v0.pushbuf, + 0, sizeof(*mast), (void **)&mast); + *pobject = nv_object(mast); + if (ret) + return ret; + + return 0; +} + +static int +nv50_disp_core_init(struct nvkm_object *object) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nv50_disp_dmac *mast = (void *)object; + int ret; + + ret = nv50_disp_chan_init(&mast->base); + if (ret) + return ret; + + /* enable error reporting */ + nv_mask(priv, 0x610028, 0x00010000, 0x00010000); + + /* attempt to unstick channel from some unknown state */ + if ((nv_rd32(priv, 0x610200) & 0x009f0000) == 0x00020000) + nv_mask(priv, 0x610200, 0x00800000, 0x00800000); + if ((nv_rd32(priv, 0x610200) & 0x003f0000) == 0x00030000) + nv_mask(priv, 0x610200, 0x00600000, 0x00600000); + + /* initialise channel for dma command submission */ + nv_wr32(priv, 0x610204, mast->push); + nv_wr32(priv, 0x610208, 0x00010000); + nv_wr32(priv, 0x61020c, 0x00000000); + nv_mask(priv, 0x610200, 0x00000010, 0x00000010); + nv_wr32(priv, 0x640000, 0x00000000); + nv_wr32(priv, 0x610200, 0x01000013); + + /* wait for it to go inactive */ + if (!nv_wait(priv, 0x610200, 0x80000000, 0x00000000)) { + nv_error(mast, "init: 0x%08x\n", nv_rd32(priv, 0x610200)); + return -EBUSY; + } + + return 0; +} + +static int +nv50_disp_core_fini(struct nvkm_object *object, bool suspend) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nv50_disp_dmac *mast = (void *)object; + + /* deactivate channel */ + nv_mask(priv, 0x610200, 0x00000010, 0x00000000); + nv_mask(priv, 0x610200, 0x00000003, 0x00000000); + if (!nv_wait(priv, 0x610200, 0x001e0000, 0x00000000)) { + nv_error(mast, "fini: 0x%08x\n", nv_rd32(priv, 0x610200)); + if (suspend) + return -EBUSY; + } + + /* disable error reporting and completion notifications */ + nv_mask(priv, 0x610028, 0x00010001, 0x00000000); + + return nv50_disp_chan_fini(&mast->base, suspend); +} + +struct nv50_disp_chan_impl +nv50_disp_core_ofuncs = { + .base.ctor = nv50_disp_core_ctor, + .base.dtor = nv50_disp_dmac_dtor, + .base.init = nv50_disp_core_init, + .base.fini = nv50_disp_core_fini, + .base.map = nv50_disp_chan_map, + .base.ntfy = nv50_disp_chan_ntfy, + .base.rd32 = nv50_disp_chan_rd32, + .base.wr32 = nv50_disp_chan_wr32, + .chid = 0, + .attach = nv50_disp_dmac_object_attach, + .detach = nv50_disp_dmac_object_detach, +}; + +/******************************************************************************* + * EVO sync channel objects + ******************************************************************************/ + +static const struct nv50_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 nv50_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 nv50_disp_mthd_chan +nv50_disp_base_mthd_chan = { + .name = "Base", + .addr = 0x000540, + .data = { + { "Global", 1, &nv50_disp_base_mthd_base }, + { "Image", 2, &nv50_disp_base_mthd_image }, + {} + } +}; + +int +nv50_disp_base_ctor(struct nvkm_object *parent, + struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + union { + struct nv50_disp_base_channel_dma_v0 v0; + } *args = data; + struct nv50_disp_priv *priv = (void *)engine; + struct nv50_disp_dmac *dmac; + int ret; + + nv_ioctl(parent, "create disp base channel dma size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(parent, "create disp base channel dma vers %d " + "pushbuf %08x head %d\n", + args->v0.version, args->v0.pushbuf, args->v0.head); + if (args->v0.head > priv->head.nr) + return -EINVAL; + } else + return ret; + + ret = nv50_disp_dmac_create_(parent, engine, oclass, args->v0.pushbuf, + args->v0.head, sizeof(*dmac), + (void **)&dmac); + *pobject = nv_object(dmac); + if (ret) + return ret; + + return 0; +} + +struct nv50_disp_chan_impl +nv50_disp_base_ofuncs = { + .base.ctor = nv50_disp_base_ctor, + .base.dtor = nv50_disp_dmac_dtor, + .base.init = nv50_disp_dmac_init, + .base.fini = nv50_disp_dmac_fini, + .base.ntfy = nv50_disp_chan_ntfy, + .base.map = nv50_disp_chan_map, + .base.rd32 = nv50_disp_chan_rd32, + .base.wr32 = nv50_disp_chan_wr32, + .chid = 1, + .attach = nv50_disp_dmac_object_attach, + .detach = nv50_disp_dmac_object_detach, +}; + +/******************************************************************************* + * EVO overlay channel objects + ******************************************************************************/ + +const struct nv50_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 nv50_disp_mthd_chan +nv50_disp_ovly_mthd_chan = { + .name = "Overlay", + .addr = 0x000540, + .data = { + { "Global", 1, &nv50_disp_ovly_mthd_base }, + {} + } +}; + +int +nv50_disp_ovly_ctor(struct nvkm_object *parent, + struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + union { + struct nv50_disp_overlay_channel_dma_v0 v0; + } *args = data; + struct nv50_disp_priv *priv = (void *)engine; + struct nv50_disp_dmac *dmac; + int ret; + + nv_ioctl(parent, "create disp overlay channel dma size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(parent, "create disp overlay channel dma vers %d " + "pushbuf %08x head %d\n", + args->v0.version, args->v0.pushbuf, args->v0.head); + if (args->v0.head > priv->head.nr) + return -EINVAL; + } else + return ret; + + ret = nv50_disp_dmac_create_(parent, engine, oclass, args->v0.pushbuf, + args->v0.head, sizeof(*dmac), + (void **)&dmac); + *pobject = nv_object(dmac); + if (ret) + return ret; + + return 0; +} + +struct nv50_disp_chan_impl +nv50_disp_ovly_ofuncs = { + .base.ctor = nv50_disp_ovly_ctor, + .base.dtor = nv50_disp_dmac_dtor, + .base.init = nv50_disp_dmac_init, + .base.fini = nv50_disp_dmac_fini, + .base.ntfy = nv50_disp_chan_ntfy, + .base.map = nv50_disp_chan_map, + .base.rd32 = nv50_disp_chan_rd32, + .base.wr32 = nv50_disp_chan_wr32, + .chid = 3, + .attach = nv50_disp_dmac_object_attach, + .detach = nv50_disp_dmac_object_detach, +}; + +/******************************************************************************* + * EVO PIO channel base class + ******************************************************************************/ + +static int +nv50_disp_pioc_create_(struct nvkm_object *parent, + struct nvkm_object *engine, + struct nvkm_oclass *oclass, int head, + int length, void **pobject) +{ + return nv50_disp_chan_create_(parent, engine, oclass, head, + length, pobject); +} + +void +nv50_disp_pioc_dtor(struct nvkm_object *object) +{ + struct nv50_disp_pioc *pioc = (void *)object; + nv50_disp_chan_destroy(&pioc->base); +} + +static int +nv50_disp_pioc_init(struct nvkm_object *object) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nv50_disp_pioc *pioc = (void *)object; + int chid = pioc->base.chid; + int ret; + + ret = nv50_disp_chan_init(&pioc->base); + if (ret) + return ret; + + nv_wr32(priv, 0x610200 + (chid * 0x10), 0x00002000); + if (!nv_wait(priv, 0x610200 + (chid * 0x10), 0x00000000, 0x00000000)) { + nv_error(pioc, "timeout0: 0x%08x\n", + nv_rd32(priv, 0x610200 + (chid * 0x10))); + return -EBUSY; + } + + nv_wr32(priv, 0x610200 + (chid * 0x10), 0x00000001); + if (!nv_wait(priv, 0x610200 + (chid * 0x10), 0x00030000, 0x00010000)) { + nv_error(pioc, "timeout1: 0x%08x\n", + nv_rd32(priv, 0x610200 + (chid * 0x10))); + return -EBUSY; + } + + return 0; +} + +static int +nv50_disp_pioc_fini(struct nvkm_object *object, bool suspend) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nv50_disp_pioc *pioc = (void *)object; + int chid = pioc->base.chid; + + nv_mask(priv, 0x610200 + (chid * 0x10), 0x00000001, 0x00000000); + if (!nv_wait(priv, 0x610200 + (chid * 0x10), 0x00030000, 0x00000000)) { + nv_error(pioc, "timeout: 0x%08x\n", + nv_rd32(priv, 0x610200 + (chid * 0x10))); + if (suspend) + return -EBUSY; + } + + return nv50_disp_chan_fini(&pioc->base, suspend); +} + +/******************************************************************************* + * EVO immediate overlay channel objects + ******************************************************************************/ + +int +nv50_disp_oimm_ctor(struct nvkm_object *parent, + struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + union { + struct nv50_disp_overlay_v0 v0; + } *args = data; + struct nv50_disp_priv *priv = (void *)engine; + struct nv50_disp_pioc *pioc; + int ret; + + nv_ioctl(parent, "create disp overlay size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(parent, "create disp overlay vers %d head %d\n", + args->v0.version, args->v0.head); + if (args->v0.head > priv->head.nr) + return -EINVAL; + } else + return ret; + + ret = nv50_disp_pioc_create_(parent, engine, oclass, args->v0.head, + sizeof(*pioc), (void **)&pioc); + *pobject = nv_object(pioc); + if (ret) + return ret; + + return 0; +} + +struct nv50_disp_chan_impl +nv50_disp_oimm_ofuncs = { + .base.ctor = nv50_disp_oimm_ctor, + .base.dtor = nv50_disp_pioc_dtor, + .base.init = nv50_disp_pioc_init, + .base.fini = nv50_disp_pioc_fini, + .base.ntfy = nv50_disp_chan_ntfy, + .base.map = nv50_disp_chan_map, + .base.rd32 = nv50_disp_chan_rd32, + .base.wr32 = nv50_disp_chan_wr32, + .chid = 5, +}; + +/******************************************************************************* + * EVO cursor channel objects + ******************************************************************************/ + +int +nv50_disp_curs_ctor(struct nvkm_object *parent, + struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + union { + struct nv50_disp_cursor_v0 v0; + } *args = data; + struct nv50_disp_priv *priv = (void *)engine; + struct nv50_disp_pioc *pioc; + int ret; + + nv_ioctl(parent, "create disp cursor size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(parent, "create disp cursor vers %d head %d\n", + args->v0.version, args->v0.head); + if (args->v0.head > priv->head.nr) + return -EINVAL; + } else + return ret; + + ret = nv50_disp_pioc_create_(parent, engine, oclass, args->v0.head, + sizeof(*pioc), (void **)&pioc); + *pobject = nv_object(pioc); + if (ret) + return ret; + + return 0; +} + +struct nv50_disp_chan_impl +nv50_disp_curs_ofuncs = { + .base.ctor = nv50_disp_curs_ctor, + .base.dtor = nv50_disp_pioc_dtor, + .base.init = nv50_disp_pioc_init, + .base.fini = nv50_disp_pioc_fini, + .base.ntfy = nv50_disp_chan_ntfy, + .base.map = nv50_disp_chan_map, + .base.rd32 = nv50_disp_chan_rd32, + .base.wr32 = nv50_disp_chan_wr32, + .chid = 7, +}; + +/******************************************************************************* + * Base display object + ******************************************************************************/ + +int +nv50_disp_main_scanoutpos(NV50_DISP_MTHD_V0) +{ + const u32 blanke = nv_rd32(priv, 0x610aec + (head * 0x540)); + const u32 blanks = nv_rd32(priv, 0x610af4 + (head * 0x540)); + const u32 total = nv_rd32(priv, 0x610afc + (head * 0x540)); + union { + struct nv04_disp_scanoutpos_v0 v0; + } *args = data; + int ret; + + nv_ioctl(object, "disp scanoutpos size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(object, "disp scanoutpos vers %d\n", args->v0.version); + args->v0.vblanke = (blanke & 0xffff0000) >> 16; + args->v0.hblanke = (blanke & 0x0000ffff); + args->v0.vblanks = (blanks & 0xffff0000) >> 16; + args->v0.hblanks = (blanks & 0x0000ffff); + args->v0.vtotal = ( total & 0xffff0000) >> 16; + args->v0.htotal = ( total & 0x0000ffff); + args->v0.time[0] = ktime_to_ns(ktime_get()); + args->v0.vline = /* vline read locks hline */ + nv_rd32(priv, 0x616340 + (head * 0x800)) & 0xffff; + args->v0.time[1] = ktime_to_ns(ktime_get()); + args->v0.hline = + nv_rd32(priv, 0x616344 + (head * 0x800)) & 0xffff; + } else + return ret; + + return 0; +} + +int +nv50_disp_main_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size) +{ + const struct nv50_disp_impl *impl = (void *)nv_oclass(object->engine); + union { + struct nv50_disp_mthd_v0 v0; + struct nv50_disp_mthd_v1 v1; + } *args = data; + struct nv50_disp_priv *priv = (void *)object->engine; + struct nvkm_output *outp = NULL; + struct nvkm_output *temp; + u16 type, mask = 0; + int head, ret; + + if (mthd != NV50_DISP_MTHD) + return -EINVAL; + + nv_ioctl(object, "disp mthd size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, true)) { + nv_ioctl(object, "disp mthd vers %d mthd %02x head %d\n", + args->v0.version, args->v0.method, args->v0.head); + mthd = args->v0.method; + head = args->v0.head; + } else + if (nvif_unpack(args->v1, 1, 1, true)) { + nv_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; + head = ffs((mask >> 8) & 0x0f) - 1; + } else + return ret; + + if (head < 0 || head >= priv->head.nr) + return -ENXIO; + + if (mask) { + list_for_each_entry(temp, &priv->base.outp, 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 impl->head.scanoutpos(object, priv, data, size, head); + default: + break; + } + + switch (mthd * !!outp) { + case NV50_DISP_MTHD_V1_DAC_PWR: + return priv->dac.power(object, priv, data, size, head, outp); + case NV50_DISP_MTHD_V1_DAC_LOAD: + return priv->dac.sense(object, priv, data, size, head, outp); + case NV50_DISP_MTHD_V1_SOR_PWR: + return priv->sor.power(object, priv, data, size, head, outp); + case NV50_DISP_MTHD_V1_SOR_HDA_ELD: + if (!priv->sor.hda_eld) + return -ENODEV; + return priv->sor.hda_eld(object, priv, data, size, head, outp); + case NV50_DISP_MTHD_V1_SOR_HDMI_PWR: + if (!priv->sor.hdmi) + return -ENODEV; + return priv->sor.hdmi(object, priv, data, size, head, outp); + case NV50_DISP_MTHD_V1_SOR_LVDS_SCRIPT: { + union { + struct nv50_disp_sor_lvds_script_v0 v0; + } *args = data; + nv_ioctl(object, "disp sor lvds script size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(object, "disp sor lvds script " + "vers %d name %04x\n", + args->v0.version, args->v0.script); + priv->sor.lvdsconf = args->v0.script; + return 0; + } else + return ret; + } + break; + case NV50_DISP_MTHD_V1_SOR_DP_PWR: { + struct nvkm_output_dp *outpdp = (void *)outp; + union { + struct nv50_disp_sor_dp_pwr_v0 v0; + } *args = data; + nv_ioctl(object, "disp sor dp pwr size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(object, "disp sor dp pwr vers %d state %d\n", + args->v0.version, args->v0.state); + if (args->v0.state == 0) { + nvkm_notify_put(&outpdp->irq); + ((struct nvkm_output_dp_impl *)nv_oclass(outp)) + ->lnk_pwr(outpdp, 0); + atomic_set(&outpdp->lt.done, 0); + return 0; + } else + if (args->v0.state != 0) { + nvkm_output_dp_train(&outpdp->base, 0, true); + return 0; + } + } else + return ret; + } + break; + case NV50_DISP_MTHD_V1_PIOR_PWR: + if (!priv->pior.power) + return -ENODEV; + return priv->pior.power(object, priv, data, size, head, outp); + default: + break; + } + + return -EINVAL; +} + +int +nv50_disp_main_ctor(struct nvkm_object *parent, + struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv50_disp_priv *priv = (void *)engine; + struct nv50_disp_base *base; + int ret; + + ret = nvkm_parent_create(parent, engine, oclass, 0, + priv->sclass, 0, &base); + *pobject = nv_object(base); + if (ret) + return ret; + + return nvkm_ramht_new(nv_object(base), nv_object(base), 0x1000, 0, + &base->ramht); +} + +void +nv50_disp_main_dtor(struct nvkm_object *object) +{ + struct nv50_disp_base *base = (void *)object; + nvkm_ramht_ref(NULL, &base->ramht); + nvkm_parent_destroy(&base->base); +} + +static int +nv50_disp_main_init(struct nvkm_object *object) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nv50_disp_base *base = (void *)object; + int ret, i; + u32 tmp; + + ret = nvkm_parent_init(&base->base); + if (ret) + return ret; + + /* 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 = nv_rd32(priv, 0x614004); + nv_wr32(priv, 0x610184, tmp); + + /* ... CRTC caps */ + for (i = 0; i < priv->head.nr; i++) { + tmp = nv_rd32(priv, 0x616100 + (i * 0x800)); + nv_wr32(priv, 0x610190 + (i * 0x10), tmp); + tmp = nv_rd32(priv, 0x616104 + (i * 0x800)); + nv_wr32(priv, 0x610194 + (i * 0x10), tmp); + tmp = nv_rd32(priv, 0x616108 + (i * 0x800)); + nv_wr32(priv, 0x610198 + (i * 0x10), tmp); + tmp = nv_rd32(priv, 0x61610c + (i * 0x800)); + nv_wr32(priv, 0x61019c + (i * 0x10), tmp); + } + + /* ... DAC caps */ + for (i = 0; i < priv->dac.nr; i++) { + tmp = nv_rd32(priv, 0x61a000 + (i * 0x800)); + nv_wr32(priv, 0x6101d0 + (i * 0x04), tmp); + } + + /* ... SOR caps */ + for (i = 0; i < priv->sor.nr; i++) { + tmp = nv_rd32(priv, 0x61c000 + (i * 0x800)); + nv_wr32(priv, 0x6101e0 + (i * 0x04), tmp); + } + + /* ... PIOR caps */ + for (i = 0; i < priv->pior.nr; i++) { + tmp = nv_rd32(priv, 0x61e000 + (i * 0x800)); + nv_wr32(priv, 0x6101f0 + (i * 0x04), tmp); + } + + /* steal display away from vbios, or something like that */ + if (nv_rd32(priv, 0x610024) & 0x00000100) { + nv_wr32(priv, 0x610024, 0x00000100); + nv_mask(priv, 0x6194e8, 0x00000001, 0x00000000); + if (!nv_wait(priv, 0x6194e8, 0x00000002, 0x00000000)) { + nv_error(priv, "timeout acquiring display\n"); + return -EBUSY; + } + } + + /* point at display engine memory area (hash table, objects) */ + nv_wr32(priv, 0x610010, (nv_gpuobj(base->ramht)->addr >> 8) | 9); + + /* enable supervisor interrupts, disable everything else */ + nv_wr32(priv, 0x61002c, 0x00000370); + nv_wr32(priv, 0x610028, 0x00000000); + return 0; +} + +static int +nv50_disp_main_fini(struct nvkm_object *object, bool suspend) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nv50_disp_base *base = (void *)object; + + /* disable all interrupts */ + nv_wr32(priv, 0x610024, 0x00000000); + nv_wr32(priv, 0x610020, 0x00000000); + + return nvkm_parent_fini(&base->base, suspend); +} + +struct nvkm_ofuncs +nv50_disp_main_ofuncs = { + .ctor = nv50_disp_main_ctor, + .dtor = nv50_disp_main_dtor, + .init = nv50_disp_main_init, + .fini = nv50_disp_main_fini, + .mthd = nv50_disp_main_mthd, + .ntfy = nvkm_disp_ntfy, +}; + +static struct nvkm_oclass +nv50_disp_main_oclass[] = { + { NV50_DISP, &nv50_disp_main_ofuncs }, + {} +}; + +static struct nvkm_oclass +nv50_disp_sclass[] = { + { NV50_DISP_CORE_CHANNEL_DMA, &nv50_disp_core_ofuncs.base }, + { NV50_DISP_BASE_CHANNEL_DMA, &nv50_disp_base_ofuncs.base }, + { NV50_DISP_OVERLAY_CHANNEL_DMA, &nv50_disp_ovly_ofuncs.base }, + { NV50_DISP_OVERLAY, &nv50_disp_oimm_ofuncs.base }, + { NV50_DISP_CURSOR, &nv50_disp_curs_ofuncs.base }, + {} +}; + +/******************************************************************************* + * Display context, tracks instmem allocation and prevents more than one + * client using the display hardware at any time. + ******************************************************************************/ + +static int +nv50_disp_data_ctor(struct nvkm_object *parent, + struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv50_disp_priv *priv = (void *)engine; + struct nvkm_engctx *ectx; + int ret = -EBUSY; + + /* no context needed for channel objects... */ + if (nv_mclass(parent) != NV_DEVICE) { + atomic_inc(&parent->refcount); + *pobject = parent; + return 1; + } + + /* allocate display hardware to client */ + mutex_lock(&nv_subdev(priv)->mutex); + if (list_empty(&nv_engine(priv)->contexts)) { + ret = nvkm_engctx_create(parent, engine, oclass, NULL, 0x10000, + 0x10000, NVOBJ_FLAG_HEAP, &ectx); + *pobject = nv_object(ectx); + } + mutex_unlock(&nv_subdev(priv)->mutex); + return ret; +} + +struct nvkm_oclass +nv50_disp_cclass = { + .handle = NV_ENGCTX(DISP, 0x50), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_disp_data_ctor, + .dtor = _nvkm_engctx_dtor, + .init = _nvkm_engctx_init, + .fini = _nvkm_engctx_fini, + .rd32 = _nvkm_engctx_rd32, + .wr32 = _nvkm_engctx_wr32, + }, +}; + +/******************************************************************************* + * Display engine implementation + ******************************************************************************/ + +static void +nv50_disp_vblank_fini(struct nvkm_event *event, int type, int head) +{ + struct nvkm_disp *disp = container_of(event, typeof(*disp), vblank); + nv_mask(disp, 0x61002c, (4 << head), 0); +} + +static void +nv50_disp_vblank_init(struct nvkm_event *event, int type, int head) +{ + struct nvkm_disp *disp = container_of(event, typeof(*disp), vblank); + nv_mask(disp, 0x61002c, (4 << head), (4 << head)); +} + +const struct nvkm_event_func +nv50_disp_vblank_func = { + .ctor = nvkm_disp_vblank_ctor, + .init = nv50_disp_vblank_init, + .fini = nv50_disp_vblank_fini, +}; + +static const struct nvkm_enum +nv50_disp_intr_error_type[] = { + { 3, "ILLEGAL_MTHD" }, + { 4, "INVALID_VALUE" }, + { 5, "INVALID_STATE" }, + { 7, "INVALID_HANDLE" }, + {} +}; + +static const struct nvkm_enum +nv50_disp_intr_error_code[] = { + { 0x00, "" }, + {} +}; + +static void +nv50_disp_intr_error(struct nv50_disp_priv *priv, int chid) +{ + struct nv50_disp_impl *impl = (void *)nv_object(priv)->oclass; + u32 data = nv_rd32(priv, 0x610084 + (chid * 0x08)); + u32 addr = nv_rd32(priv, 0x610080 + (chid * 0x08)); + u32 code = (addr & 0x00ff0000) >> 16; + u32 type = (addr & 0x00007000) >> 12; + u32 mthd = (addr & 0x00000ffc); + const struct nvkm_enum *ec, *et; + char ecunk[6], etunk[6]; + + et = nvkm_enum_find(nv50_disp_intr_error_type, type); + if (!et) + snprintf(etunk, sizeof(etunk), "UNK%02X", type); + + ec = nvkm_enum_find(nv50_disp_intr_error_code, code); + if (!ec) + snprintf(ecunk, sizeof(ecunk), "UNK%02X", code); + + nv_error(priv, "%s [%s] chid %d mthd 0x%04x data 0x%08x\n", + et ? et->name : etunk, ec ? ec->name : ecunk, + chid, mthd, data); + + if (chid == 0) { + switch (mthd) { + case 0x0080: + nv50_disp_mthd_chan(priv, NV_DBG_ERROR, chid - 0, + impl->mthd.core); + break; + default: + break; + } + } else + if (chid <= 2) { + switch (mthd) { + case 0x0080: + nv50_disp_mthd_chan(priv, NV_DBG_ERROR, chid - 1, + impl->mthd.base); + break; + default: + break; + } + } else + if (chid <= 4) { + switch (mthd) { + case 0x0080: + nv50_disp_mthd_chan(priv, NV_DBG_ERROR, chid - 3, + impl->mthd.ovly); + break; + default: + break; + } + } + + nv_wr32(priv, 0x610020, 0x00010000 << chid); + nv_wr32(priv, 0x610080 + (chid * 0x08), 0x90000000); +} + +static struct nvkm_output * +exec_lookup(struct nv50_disp_priv *priv, int head, int or, u32 ctrl, + u32 *data, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_outp *info) +{ + struct nvkm_bios *bios = nvkm_bios(priv); + struct nvkm_output *outp; + u16 mask, type; + + if (or < 4) { + type = DCB_OUTPUT_ANALOG; + mask = 0; + } else + if (or < 8) { + switch (ctrl & 0x00000f00) { + case 0x00000000: type = DCB_OUTPUT_LVDS; mask = 1; break; + case 0x00000100: type = DCB_OUTPUT_TMDS; mask = 1; break; + case 0x00000200: type = DCB_OUTPUT_TMDS; mask = 2; break; + case 0x00000500: type = DCB_OUTPUT_TMDS; mask = 3; break; + case 0x00000800: type = DCB_OUTPUT_DP; mask = 1; break; + case 0x00000900: type = DCB_OUTPUT_DP; mask = 2; break; + default: + nv_error(priv, "unknown SOR mc 0x%08x\n", ctrl); + return NULL; + } + or -= 4; + } else { + or = or - 8; + type = 0x0010; + mask = 0; + switch (ctrl & 0x00000f00) { + case 0x00000000: type |= priv->pior.type[or]; break; + default: + nv_error(priv, "unknown PIOR mc 0x%08x\n", ctrl); + return NULL; + } + } + + mask = 0x00c0 & (mask << 6); + mask |= 0x0001 << or; + mask |= 0x0100 << head; + + list_for_each_entry(outp, &priv->base.outp, head) { + if ((outp->info.hasht & 0xff) == type && + (outp->info.hashm & mask) == mask) { + *data = nvbios_outp_match(bios, outp->info.hasht, + outp->info.hashm, + ver, hdr, cnt, len, info); + if (!*data) + return NULL; + return outp; + } + } + + return NULL; +} + +static struct nvkm_output * +exec_script(struct nv50_disp_priv *priv, int head, int id) +{ + struct nvkm_bios *bios = nvkm_bios(priv); + struct nvkm_output *outp; + struct nvbios_outp info; + u8 ver, hdr, cnt, len; + u32 data, ctrl = 0; + u32 reg; + int i; + + /* DAC */ + for (i = 0; !(ctrl & (1 << head)) && i < priv->dac.nr; i++) + ctrl = nv_rd32(priv, 0x610b5c + (i * 8)); + + /* SOR */ + if (!(ctrl & (1 << head))) { + if (nv_device(priv)->chipset < 0x90 || + nv_device(priv)->chipset == 0x92 || + nv_device(priv)->chipset == 0xa0) { + reg = 0x610b74; + } else { + reg = 0x610798; + } + for (i = 0; !(ctrl & (1 << head)) && i < priv->sor.nr; i++) + ctrl = nv_rd32(priv, reg + (i * 8)); + i += 4; + } + + /* PIOR */ + if (!(ctrl & (1 << head))) { + for (i = 0; !(ctrl & (1 << head)) && i < priv->pior.nr; i++) + ctrl = nv_rd32(priv, 0x610b84 + (i * 8)); + i += 8; + } + + if (!(ctrl & (1 << head))) + return NULL; + i--; + + outp = exec_lookup(priv, head, i, ctrl, &data, &ver, &hdr, &cnt, &len, &info); + if (outp) { + struct nvbios_init init = { + .subdev = nv_subdev(priv), + .bios = bios, + .offset = info.script[id], + .outp = &outp->info, + .crtc = head, + .execute = 1, + }; + + nvbios_exec(&init); + } + + return outp; +} + +static struct nvkm_output * +exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk, u32 *conf) +{ + struct nvkm_bios *bios = nvkm_bios(priv); + struct nvkm_output *outp; + struct nvbios_outp info1; + struct nvbios_ocfg info2; + u8 ver, hdr, cnt, len; + u32 data, ctrl = 0; + u32 reg; + int i; + + /* DAC */ + for (i = 0; !(ctrl & (1 << head)) && i < priv->dac.nr; i++) + ctrl = nv_rd32(priv, 0x610b58 + (i * 8)); + + /* SOR */ + if (!(ctrl & (1 << head))) { + if (nv_device(priv)->chipset < 0x90 || + nv_device(priv)->chipset == 0x92 || + nv_device(priv)->chipset == 0xa0) { + reg = 0x610b70; + } else { + reg = 0x610794; + } + for (i = 0; !(ctrl & (1 << head)) && i < priv->sor.nr; i++) + ctrl = nv_rd32(priv, reg + (i * 8)); + i += 4; + } + + /* PIOR */ + if (!(ctrl & (1 << head))) { + for (i = 0; !(ctrl & (1 << head)) && i < priv->pior.nr; i++) + ctrl = nv_rd32(priv, 0x610b80 + (i * 8)); + i += 8; + } + + if (!(ctrl & (1 << head))) + return NULL; + i--; + + outp = exec_lookup(priv, head, i, ctrl, &data, &ver, &hdr, &cnt, &len, &info1); + if (!outp) + return NULL; + + if (outp->info.location == 0) { + switch (outp->info.type) { + case DCB_OUTPUT_TMDS: + *conf = (ctrl & 0x00000f00) >> 8; + if (pclk >= 165000) + *conf |= 0x0100; + break; + case DCB_OUTPUT_LVDS: + *conf = priv->sor.lvdsconf; + break; + case DCB_OUTPUT_DP: + *conf = (ctrl & 0x00000f00) >> 8; + break; + case DCB_OUTPUT_ANALOG: + default: + *conf = 0x00ff; + break; + } + } else { + *conf = (ctrl & 0x00000f00) >> 8; + pclk = pclk / 2; + } + + data = nvbios_ocfg_match(bios, data, *conf, &ver, &hdr, &cnt, &len, &info2); + if (data && id < 0xff) { + data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk); + if (data) { + struct nvbios_init init = { + .subdev = nv_subdev(priv), + .bios = bios, + .offset = data, + .outp = &outp->info, + .crtc = head, + .execute = 1, + }; + + nvbios_exec(&init); + } + } + + return outp; +} + +static void +nv50_disp_intr_unk10_0(struct nv50_disp_priv *priv, int head) +{ + exec_script(priv, head, 1); +} + +static void +nv50_disp_intr_unk20_0(struct nv50_disp_priv *priv, int head) +{ + struct nvkm_output *outp = exec_script(priv, head, 2); + + /* the binary driver does this outside of the supervisor handling + * (after the third supervisor from a detach). we (currently?) + * allow both detach/attach to happen in the same set of + * supervisor interrupts, so it would make sense to execute this + * (full power down?) script after all the detach phases of the + * supervisor handling. like with training if needed from the + * second supervisor, nvidia doesn't do this, so who knows if it's + * entirely safe, but it does appear to work.. + * + * without this script being run, on some configurations i've + * seen, switching from DP to TMDS on a DP connector may result + * in a blank screen (SOR_PWR off/on can restore it) + */ + if (outp && outp->info.type == DCB_OUTPUT_DP) { + struct nvkm_output_dp *outpdp = (void *)outp; + struct nvbios_init init = { + .subdev = nv_subdev(priv), + .bios = nvkm_bios(priv), + .outp = &outp->info, + .crtc = head, + .offset = outpdp->info.script[4], + .execute = 1, + }; + + nvbios_exec(&init); + atomic_set(&outpdp->lt.done, 0); + } +} + +static void +nv50_disp_intr_unk20_1(struct nv50_disp_priv *priv, int head) +{ + struct nvkm_devinit *devinit = nvkm_devinit(priv); + u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff; + if (pclk) + devinit->pll_set(devinit, PLL_VPLL0 + head, pclk); +} + +static void +nv50_disp_intr_unk20_2_dp(struct nv50_disp_priv *priv, int head, + struct dcb_output *outp, u32 pclk) +{ + const int link = !(outp->sorconf.link & 1); + const int or = ffs(outp->or) - 1; + const u32 soff = ( or * 0x800); + const u32 loff = (link * 0x080) + soff; + const u32 ctrl = nv_rd32(priv, 0x610794 + (or * 8)); + const u32 symbol = 100000; + const s32 vactive = nv_rd32(priv, 0x610af8 + (head * 0x540)) & 0xffff; + const s32 vblanke = nv_rd32(priv, 0x610ae8 + (head * 0x540)) & 0xffff; + const s32 vblanks = nv_rd32(priv, 0x610af0 + (head * 0x540)) & 0xffff; + u32 dpctrl = nv_rd32(priv, 0x61c10c + loff); + u32 clksor = nv_rd32(priv, 0x614300 + soff); + 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; + u32 link_nr, link_bw, bits; + u64 value; + + link_bw = (clksor & 0x000c0000) ? 270000 : 162000; + link_nr = hweight32(dpctrl & 0x000f0000); + + /* symbols/hblank - algorithm taken from comments in tegra driver */ + value = vblanke + vactive - vblanks - 7; + value = value * link_bw; + do_div(value, pclk); + value = value - (3 * !!(dpctrl & 0x00004000)) - (12 / link_nr); + nv_mask(priv, 0x61c1e8 + soff, 0x0000ffff, value); + + /* symbols/vblank - algorithm taken from comments in tegra driver */ + value = vblanks - vblanke - 25; + value = value * link_bw; + do_div(value, pclk); + value = value - ((36 / link_nr) + 3) - 1; + nv_mask(priv, 0x61c1ec + soff, 0x00ffffff, value); + + /* watermark / activesym */ + if ((ctrl & 0xf0000) == 0x60000) bits = 30; + else if ((ctrl & 0xf0000) == 0x50000) bits = 24; + else bits = 18; + + link_data_rate = (pclk * bits / 8) / link_nr; + + /* calculate ratio of packed data rate to link symbol rate */ + link_ratio = link_data_rate * symbol; + do_div(link_ratio, link_bw); + + for (TU = 64; 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 (!bestTU) { + nv_error(priv, "unable to find suitable dp config\n"); + return; + } + + /* 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; + + nv_mask(priv, 0x61c10c + loff, 0x000001fc, bestTU << 2); + nv_mask(priv, 0x61c128 + loff, 0x010f7f3f, bestVTUa << 24 | + bestVTUf << 16 | + bestVTUi << 8 | unk); +} + +static void +nv50_disp_intr_unk20_2(struct nv50_disp_priv *priv, int head) +{ + struct nvkm_output *outp; + u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff; + u32 hval, hreg = 0x614200 + (head * 0x800); + u32 oval, oreg; + u32 mask, conf; + + outp = exec_clkcmp(priv, head, 0xff, pclk, &conf); + if (!outp) + return; + + /* we allow both encoder attach and detach operations to occur + * within a single supervisor (ie. modeset) sequence. the + * encoder detach scripts quite often switch off power to the + * lanes, which requires the link to be re-trained. + * + * this is not generally an issue as the sink "must" (heh) + * signal an irq when it's lost sync so the driver can + * re-train. + * + * however, on some boards, if one does not configure at least + * the gpu side of the link *before* attaching, then various + * things can go horribly wrong (PDISP disappearing from mmio, + * third supervisor never happens, etc). + * + * the solution is simply to retrain here, if necessary. last + * i checked, the binary driver userspace does not appear to + * trigger this situation (it forces an UPDATE between steps). + */ + if (outp->info.type == DCB_OUTPUT_DP) { + u32 soff = (ffs(outp->info.or) - 1) * 0x08; + u32 ctrl, datarate; + + if (outp->info.location == 0) { + ctrl = nv_rd32(priv, 0x610794 + soff); + soff = 1; + } else { + ctrl = nv_rd32(priv, 0x610b80 + soff); + soff = 2; + } + + switch ((ctrl & 0x000f0000) >> 16) { + case 6: datarate = pclk * 30; break; + case 5: datarate = pclk * 24; break; + case 2: + default: + datarate = pclk * 18; + break; + } + + if (nvkm_output_dp_train(outp, datarate / soff, true)) + ERR("link not trained before attach\n"); + } + + exec_clkcmp(priv, head, 0, pclk, &conf); + + if (!outp->info.location && outp->info.type == DCB_OUTPUT_ANALOG) { + oreg = 0x614280 + (ffs(outp->info.or) - 1) * 0x800; + oval = 0x00000000; + hval = 0x00000000; + mask = 0xffffffff; + } else + if (!outp->info.location) { + if (outp->info.type == DCB_OUTPUT_DP) + nv50_disp_intr_unk20_2_dp(priv, head, &outp->info, pclk); + oreg = 0x614300 + (ffs(outp->info.or) - 1) * 0x800; + oval = (conf & 0x0100) ? 0x00000101 : 0x00000000; + hval = 0x00000000; + mask = 0x00000707; + } else { + oreg = 0x614380 + (ffs(outp->info.or) - 1) * 0x800; + oval = 0x00000001; + hval = 0x00000001; + mask = 0x00000707; + } + + nv_mask(priv, hreg, 0x0000000f, hval); + nv_mask(priv, oreg, mask, oval); +} + +/* If programming a TMDS output on a SOR that can also be configured for + * DisplayPort, make sure NV50_SOR_DP_CTRL_ENABLE is forced off. + * + * It looks like the VBIOS TMDS scripts make an attempt at this, however, + * the VBIOS scripts on at least one board I have only switch it off on + * link 0, causing a blank display if the output has previously been + * programmed for DisplayPort. + */ +static void +nv50_disp_intr_unk40_0_tmds(struct nv50_disp_priv *priv, + struct dcb_output *outp) +{ + struct nvkm_bios *bios = nvkm_bios(priv); + const int link = !(outp->sorconf.link & 1); + const int or = ffs(outp->or) - 1; + const u32 loff = (or * 0x800) + (link * 0x80); + const u16 mask = (outp->sorconf.link << 6) | outp->or; + struct dcb_output match; + u8 ver, hdr; + + if (dcb_outp_match(bios, DCB_OUTPUT_DP, mask, &ver, &hdr, &match)) + nv_mask(priv, 0x61c10c + loff, 0x00000001, 0x00000000); +} + +static void +nv50_disp_intr_unk40_0(struct nv50_disp_priv *priv, int head) +{ + struct nvkm_output *outp; + u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff; + u32 conf; + + outp = exec_clkcmp(priv, head, 1, pclk, &conf); + if (!outp) + return; + + if (outp->info.location == 0 && outp->info.type == DCB_OUTPUT_TMDS) + nv50_disp_intr_unk40_0_tmds(priv, &outp->info); +} + +void +nv50_disp_intr_supervisor(struct work_struct *work) +{ + struct nv50_disp_priv *priv = + container_of(work, struct nv50_disp_priv, supervisor); + struct nv50_disp_impl *impl = (void *)nv_object(priv)->oclass; + u32 super = nv_rd32(priv, 0x610030); + int head; + + nv_debug(priv, "supervisor 0x%08x 0x%08x\n", priv->super, super); + + if (priv->super & 0x00000010) { + nv50_disp_mthd_chan(priv, NV_DBG_DEBUG, 0, impl->mthd.core); + for (head = 0; head < priv->head.nr; head++) { + if (!(super & (0x00000020 << head))) + continue; + if (!(super & (0x00000080 << head))) + continue; + nv50_disp_intr_unk10_0(priv, head); + } + } else + if (priv->super & 0x00000020) { + for (head = 0; head < priv->head.nr; head++) { + if (!(super & (0x00000080 << head))) + continue; + nv50_disp_intr_unk20_0(priv, head); + } + for (head = 0; head < priv->head.nr; head++) { + if (!(super & (0x00000200 << head))) + continue; + nv50_disp_intr_unk20_1(priv, head); + } + for (head = 0; head < priv->head.nr; head++) { + if (!(super & (0x00000080 << head))) + continue; + nv50_disp_intr_unk20_2(priv, head); + } + } else + if (priv->super & 0x00000040) { + for (head = 0; head < priv->head.nr; head++) { + if (!(super & (0x00000080 << head))) + continue; + nv50_disp_intr_unk40_0(priv, head); + } + } + + nv_wr32(priv, 0x610030, 0x80000000); +} + +void +nv50_disp_intr(struct nvkm_subdev *subdev) +{ + struct nv50_disp_priv *priv = (void *)subdev; + u32 intr0 = nv_rd32(priv, 0x610020); + u32 intr1 = nv_rd32(priv, 0x610024); + + while (intr0 & 0x001f0000) { + u32 chid = __ffs(intr0 & 0x001f0000) - 16; + nv50_disp_intr_error(priv, chid); + intr0 &= ~(0x00010000 << chid); + } + + while (intr0 & 0x0000001f) { + u32 chid = __ffs(intr0 & 0x0000001f); + nv50_disp_chan_uevent_send(priv, chid); + intr0 &= ~(0x00000001 << chid); + } + + if (intr1 & 0x00000004) { + nvkm_disp_vblank(&priv->base, 0); + nv_wr32(priv, 0x610024, 0x00000004); + intr1 &= ~0x00000004; + } + + if (intr1 & 0x00000008) { + nvkm_disp_vblank(&priv->base, 1); + nv_wr32(priv, 0x610024, 0x00000008); + intr1 &= ~0x00000008; + } + + if (intr1 & 0x00000070) { + priv->super = (intr1 & 0x00000070); + schedule_work(&priv->supervisor); + nv_wr32(priv, 0x610024, priv->super); + intr1 &= ~0x00000070; + } +} + +static int +nv50_disp_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv50_disp_priv *priv; + int ret; + + ret = nvkm_disp_create(parent, engine, oclass, 2, "PDISP", + "display", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + ret = nvkm_event_init(&nv50_disp_chan_uevent, 1, 9, &priv->uevent); + if (ret) + return ret; + + nv_engine(priv)->sclass = nv50_disp_main_oclass; + nv_engine(priv)->cclass = &nv50_disp_cclass; + nv_subdev(priv)->intr = nv50_disp_intr; + INIT_WORK(&priv->supervisor, nv50_disp_intr_supervisor); + priv->sclass = nv50_disp_sclass; + priv->head.nr = 2; + priv->dac.nr = 3; + priv->sor.nr = 2; + priv->pior.nr = 3; + priv->dac.power = nv50_dac_power; + priv->dac.sense = nv50_dac_sense; + priv->sor.power = nv50_sor_power; + priv->pior.power = nv50_pior_power; + return 0; +} + +struct nvkm_oclass * +nv50_disp_outp_sclass[] = { + &nv50_pior_dp_impl.base.base, + NULL +}; + +struct nvkm_oclass * +nv50_disp_oclass = &(struct nv50_disp_impl) { + .base.base.handle = NV_ENGINE(DISP, 0x50), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_disp_ctor, + .dtor = _nvkm_disp_dtor, + .init = _nvkm_disp_init, + .fini = _nvkm_disp_fini, + }, + .base.vblank = &nv50_disp_vblank_func, + .base.outp = nv50_disp_outp_sclass, + .mthd.core = &nv50_disp_core_mthd_chan, + .mthd.base = &nv50_disp_base_mthd_chan, + .mthd.ovly = &nv50_disp_ovly_mthd_chan, + .mthd.prev = 0x000004, + .head.scanoutpos = nv50_disp_main_scanoutpos, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h new file mode 100644 index 000000000..b4ed62007 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h @@ -0,0 +1,226 @@ +#ifndef __NV50_DISP_H__ +#define __NV50_DISP_H__ +#include "priv.h" +struct nvkm_output; +struct nvkm_output_dp; + +#define NV50_DISP_MTHD_ struct nvkm_object *object, \ + struct nv50_disp_priv *priv, void *data, u32 size +#define NV50_DISP_MTHD_V0 NV50_DISP_MTHD_, int head +#define NV50_DISP_MTHD_V1 NV50_DISP_MTHD_, int head, struct nvkm_output *outp + +struct nv50_disp_priv { + struct nvkm_disp base; + struct nvkm_oclass *sclass; + + struct work_struct supervisor; + u32 super; + + struct nvkm_event uevent; + + struct { + int nr; + } head; + struct { + int nr; + int (*power)(NV50_DISP_MTHD_V1); + int (*sense)(NV50_DISP_MTHD_V1); + } dac; + struct { + int nr; + int (*power)(NV50_DISP_MTHD_V1); + int (*hda_eld)(NV50_DISP_MTHD_V1); + int (*hdmi)(NV50_DISP_MTHD_V1); + u32 lvdsconf; + void (*magic)(struct nvkm_output *); + } sor; + struct { + int nr; + int (*power)(NV50_DISP_MTHD_V1); + u8 type[3]; + } pior; +}; + +struct nv50_disp_impl { + struct nvkm_disp_impl base; + struct { + const struct nv50_disp_mthd_chan *core; + const struct nv50_disp_mthd_chan *base; + const struct nv50_disp_mthd_chan *ovly; + int prev; + } mthd; + struct { + int (*scanoutpos)(NV50_DISP_MTHD_V0); + } head; +}; + +int nv50_disp_main_scanoutpos(NV50_DISP_MTHD_V0); +int nv50_disp_main_mthd(struct nvkm_object *, u32, void *, u32); + +int gf110_disp_main_scanoutpos(NV50_DISP_MTHD_V0); + +int nv50_dac_power(NV50_DISP_MTHD_V1); +int nv50_dac_sense(NV50_DISP_MTHD_V1); + +int gt215_hda_eld(NV50_DISP_MTHD_V1); +int gf110_hda_eld(NV50_DISP_MTHD_V1); + +int g84_hdmi_ctrl(NV50_DISP_MTHD_V1); +int gt215_hdmi_ctrl(NV50_DISP_MTHD_V1); +int gf110_hdmi_ctrl(NV50_DISP_MTHD_V1); +int gk104_hdmi_ctrl(NV50_DISP_MTHD_V1); + +int nv50_sor_power(NV50_DISP_MTHD_V1); +int nv50_pior_power(NV50_DISP_MTHD_V1); + +#include + +struct nv50_disp_base { + struct nvkm_parent base; + struct nvkm_ramht *ramht; + u32 chan; +}; + +struct nv50_disp_chan_impl { + struct nvkm_ofuncs base; + int chid; + int (*attach)(struct nvkm_object *, struct nvkm_object *, u32); + void (*detach)(struct nvkm_object *, int); +}; + +#include + +struct nv50_disp_chan { + struct nvkm_namedb base; + int chid; +}; + +int nv50_disp_chan_ntfy(struct nvkm_object *, u32, struct nvkm_event **); +int nv50_disp_chan_map(struct nvkm_object *, u64 *, u32 *); +u32 nv50_disp_chan_rd32(struct nvkm_object *, u64); +void nv50_disp_chan_wr32(struct nvkm_object *, u64, u32); +extern const struct nvkm_event_func nv50_disp_chan_uevent; +int nv50_disp_chan_uevent_ctor(struct nvkm_object *, void *, u32, + struct nvkm_notify *); +void nv50_disp_chan_uevent_send(struct nv50_disp_priv *, int); + +extern const struct nvkm_event_func gf110_disp_chan_uevent; + +#define nv50_disp_chan_init(a) \ + nvkm_namedb_init(&(a)->base) +#define nv50_disp_chan_fini(a,b) \ + nvkm_namedb_fini(&(a)->base, (b)) + +struct nv50_disp_dmac { + struct nv50_disp_chan base; + struct nvkm_dmaobj *pushdma; + u32 push; +}; + +void nv50_disp_dmac_dtor(struct nvkm_object *); + +struct nv50_disp_pioc { + struct nv50_disp_chan base; +}; + +void nv50_disp_pioc_dtor(struct nvkm_object *); + +struct nv50_disp_mthd_list { + u32 mthd; + u32 addr; + struct { + u32 mthd; + u32 addr; + const char *name; + } data[]; +}; + +struct nv50_disp_mthd_chan { + const char *name; + u32 addr; + struct { + const char *name; + int nr; + const struct nv50_disp_mthd_list *mthd; + } data[]; +}; + +extern struct nv50_disp_chan_impl nv50_disp_core_ofuncs; +int nv50_disp_core_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); +extern const struct nv50_disp_mthd_list nv50_disp_core_mthd_base; +extern const struct nv50_disp_mthd_list nv50_disp_core_mthd_sor; +extern const struct nv50_disp_mthd_list nv50_disp_core_mthd_pior; +extern struct nv50_disp_chan_impl nv50_disp_base_ofuncs; +int nv50_disp_base_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); +extern const struct nv50_disp_mthd_list nv50_disp_base_mthd_image; +extern struct nv50_disp_chan_impl nv50_disp_ovly_ofuncs; +int nv50_disp_ovly_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); +extern const struct nv50_disp_mthd_list nv50_disp_ovly_mthd_base; +extern struct nv50_disp_chan_impl nv50_disp_oimm_ofuncs; +int nv50_disp_oimm_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); +extern struct nv50_disp_chan_impl nv50_disp_curs_ofuncs; +int nv50_disp_curs_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); +extern struct nvkm_ofuncs nv50_disp_main_ofuncs; +int nv50_disp_main_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); +void nv50_disp_main_dtor(struct nvkm_object *); +extern struct nvkm_omthds nv50_disp_main_omthds[]; +extern struct nvkm_oclass nv50_disp_cclass; +void nv50_disp_mthd_chan(struct nv50_disp_priv *, int debug, int head, + const struct nv50_disp_mthd_chan *); +void nv50_disp_intr_supervisor(struct work_struct *); +void nv50_disp_intr(struct nvkm_subdev *); +extern const struct nvkm_event_func nv50_disp_vblank_func; + +extern const struct nv50_disp_mthd_chan g84_disp_core_mthd_chan; +extern const struct nv50_disp_mthd_list g84_disp_core_mthd_dac; +extern const struct nv50_disp_mthd_list g84_disp_core_mthd_head; +extern const struct nv50_disp_mthd_chan g84_disp_base_mthd_chan; +extern const struct nv50_disp_mthd_chan g84_disp_ovly_mthd_chan; + +extern const struct nv50_disp_mthd_chan g94_disp_core_mthd_chan; + +extern struct nv50_disp_chan_impl gf110_disp_core_ofuncs; +extern const struct nv50_disp_mthd_list gf110_disp_core_mthd_base; +extern const struct nv50_disp_mthd_list gf110_disp_core_mthd_dac; +extern const struct nv50_disp_mthd_list gf110_disp_core_mthd_sor; +extern const struct nv50_disp_mthd_list gf110_disp_core_mthd_pior; +extern struct nv50_disp_chan_impl gf110_disp_base_ofuncs; +extern struct nv50_disp_chan_impl gf110_disp_ovly_ofuncs; +extern const struct nv50_disp_mthd_chan gf110_disp_base_mthd_chan; +extern struct nv50_disp_chan_impl gf110_disp_oimm_ofuncs; +extern struct nv50_disp_chan_impl gf110_disp_curs_ofuncs; +extern struct nvkm_ofuncs gf110_disp_main_ofuncs; +extern struct nvkm_oclass gf110_disp_cclass; +void gf110_disp_intr_supervisor(struct work_struct *); +void gf110_disp_intr(struct nvkm_subdev *); +extern const struct nvkm_event_func gf110_disp_vblank_func; + +extern const struct nv50_disp_mthd_chan gk104_disp_core_mthd_chan; +extern const struct nv50_disp_mthd_chan gk104_disp_ovly_mthd_chan; + +extern struct nvkm_output_dp_impl nv50_pior_dp_impl; +extern struct nvkm_oclass *nv50_disp_outp_sclass[]; + +extern struct nvkm_output_dp_impl g94_sor_dp_impl; +int g94_sor_dp_lnk_pwr(struct nvkm_output_dp *, int); +extern struct nvkm_oclass *g94_disp_outp_sclass[]; + +extern struct nvkm_output_dp_impl gf110_sor_dp_impl; +int gf110_sor_dp_lnk_ctl(struct nvkm_output_dp *, int, int, bool); +extern struct nvkm_oclass *gf110_disp_outp_sclass[]; + +void gm204_sor_magic(struct nvkm_output *outp); +extern struct nvkm_output_dp_impl gm204_sor_dp_impl; +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c new file mode 100644 index 000000000..9224bcbf0 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c @@ -0,0 +1,142 @@ +/* + * 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 "priv.h" + +#include +#include +#include +#include + +int +_nvkm_output_fini(struct nvkm_object *object, bool suspend) +{ + struct nvkm_output *outp = (void *)object; + nv_ofuncs(outp->conn)->fini(nv_object(outp->conn), suspend); + return nvkm_object_fini(&outp->base, suspend); +} + +int +_nvkm_output_init(struct nvkm_object *object) +{ + struct nvkm_output *outp = (void *)object; + int ret = nvkm_object_init(&outp->base); + if (ret == 0) + nv_ofuncs(outp->conn)->init(nv_object(outp->conn)); + return 0; +} + +void +_nvkm_output_dtor(struct nvkm_object *object) +{ + struct nvkm_output *outp = (void *)object; + list_del(&outp->head); + nvkm_object_ref(NULL, (void *)&outp->conn); + nvkm_object_destroy(&outp->base); +} + +int +nvkm_output_create_(struct nvkm_object *parent, + struct nvkm_object *engine, + struct nvkm_oclass *oclass, + struct dcb_output *dcbE, int index, + int length, void **pobject) +{ + struct nvkm_disp *disp = nvkm_disp(parent); + struct nvkm_bios *bios = nvkm_bios(parent); + struct nvkm_i2c *i2c = nvkm_i2c(parent); + struct nvbios_connE connE; + struct nvkm_output *outp; + u8 ver, hdr; + u32 data; + int ret; + + ret = nvkm_object_create_(parent, engine, oclass, 0, length, pobject); + outp = *pobject; + if (ret) + return ret; + + outp->info = *dcbE; + outp->index = index; + outp->or = ffs(outp->info.or) - 1; + + DBG("type %02x loc %d or %d link %d con %x edid %x bus %d head %x\n", + dcbE->type, dcbE->location, dcbE->or, dcbE->type >= 2 ? + dcbE->sorconf.link : 0, dcbE->connector, dcbE->i2c_index, + dcbE->bus, dcbE->heads); + + if (outp->info.type != DCB_OUTPUT_DP) + outp->port = i2c->find(i2c, NV_I2C_PORT(outp->info.i2c_index)); + else + outp->port = i2c->find(i2c, NV_I2C_AUX(outp->info.i2c_index)); + outp->edid = outp->port; + + data = nvbios_connEp(bios, outp->info.connector, &ver, &hdr, &connE); + if (!data) { + DBG("vbios connector data not found\n"); + memset(&connE, 0x00, sizeof(connE)); + connE.type = DCB_CONNECTOR_NONE; + } + + ret = nvkm_object_ctor(parent, NULL, nvkm_connector_oclass, + &connE, outp->info.connector, + (struct nvkm_object **)&outp->conn); + if (ret < 0) { + ERR("error %d creating connector, disabling\n", ret); + return ret; + } + + list_add_tail(&outp->head, &disp->outp); + return 0; +} + +int +_nvkm_output_ctor(struct nvkm_object *parent, + struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *dcbE, u32 index, + struct nvkm_object **pobject) +{ + struct nvkm_output *outp; + int ret; + + ret = nvkm_output_create(parent, engine, oclass, dcbE, index, &outp); + *pobject = nv_object(outp); + if (ret) + return ret; + + return 0; +} + +struct nvkm_oclass * +nvkm_output_oclass = &(struct nvkm_output_impl) { + .base = { + .handle = 0, + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_output_ctor, + .dtor = _nvkm_output_dtor, + .init = _nvkm_output_init, + .fini = _nvkm_output_fini, + }, + }, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h new file mode 100644 index 000000000..d9253d26c --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h @@ -0,0 +1,61 @@ +#ifndef __NVKM_DISP_OUTP_H__ +#define __NVKM_DISP_OUTP_H__ +#include + +#include +#include + +struct nvkm_output { + struct nvkm_object base; + struct list_head head; + + struct dcb_output info; + int index; + int or; + + struct nvkm_i2c_port *port; + struct nvkm_i2c_port *edid; + + struct nvkm_connector *conn; +}; + +#define nvkm_output_create(p,e,c,b,i,d) \ + nvkm_output_create_((p), (e), (c), (b), (i), sizeof(**d), (void **)d) +#define nvkm_output_destroy(d) ({ \ + struct nvkm_output *_outp = (d); \ + _nvkm_output_dtor(nv_object(_outp)); \ +}) +#define nvkm_output_init(d) ({ \ + struct nvkm_output *_outp = (d); \ + _nvkm_output_init(nv_object(_outp)); \ +}) +#define nvkm_output_fini(d,s) ({ \ + struct nvkm_output *_outp = (d); \ + _nvkm_output_fini(nv_object(_outp), (s)); \ +}) + +int nvkm_output_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, struct dcb_output *, + int, int, void **); + +int _nvkm_output_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); +void _nvkm_output_dtor(struct nvkm_object *); +int _nvkm_output_init(struct nvkm_object *); +int _nvkm_output_fini(struct nvkm_object *, bool); + +struct nvkm_output_impl { + struct nvkm_oclass base; +}; + +#ifndef MSG +#define MSG(l,f,a...) do { \ + struct nvkm_output *_outp = (void *)outp; \ + nv_##l(_outp, "%02x:%04x:%04x: "f, _outp->index, \ + _outp->info.hasht, _outp->info.hashm, ##a); \ +} while(0) +#define DBG(f,a...) MSG(debug, f, ##a) +#define ERR(f,a...) MSG(error, f, ##a) +#endif +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/outpdp.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/outpdp.c new file mode 100644 index 000000000..0bde0fa5b --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/outpdp.c @@ -0,0 +1,301 @@ +/* + * 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 "outpdp.h" +#include "conn.h" +#include "dport.h" +#include "priv.h" + +#include + +#include + +int +nvkm_output_dp_train(struct nvkm_output *base, u32 datarate, bool wait) +{ + struct nvkm_output_dp *outp = (void *)base; + bool retrain = true; + u8 link[2], stat[3]; + u32 linkrate; + int ret, i; + + /* check that the link is trained at a high enough rate */ + ret = nv_rdaux(outp->base.edid, DPCD_LC00_LINK_BW_SET, link, 2); + if (ret) { + DBG("failed to read link config, assuming no sink\n"); + goto done; + } + + linkrate = link[0] * 27000 * (link[1] & DPCD_LC01_LANE_COUNT_SET); + linkrate = (linkrate * 8) / 10; /* 8B/10B coding overhead */ + datarate = (datarate + 9) / 10; /* -> decakilobits */ + if (linkrate < datarate) { + DBG("link not trained at sufficient rate\n"); + goto done; + } + + /* check that link is still trained */ + ret = nv_rdaux(outp->base.edid, DPCD_LS02, stat, 3); + if (ret) { + DBG("failed to read link status, assuming no sink\n"); + goto done; + } + + if (stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE) { + for (i = 0; i < (link[1] & DPCD_LC01_LANE_COUNT_SET); 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)) { + DBG("lane %d not equalised\n", lane); + goto done; + } + } + retrain = false; + } else { + DBG("no inter-lane alignment\n"); + } + +done: + if (retrain || !atomic_read(&outp->lt.done)) { + /* no sink, but still need to configure source */ + if (outp->dpcd[DPCD_RC00_DPCD_REV] == 0x00) { + outp->dpcd[DPCD_RC01_MAX_LINK_RATE] = + outp->base.info.dpconf.link_bw; + outp->dpcd[DPCD_RC02] = + outp->base.info.dpconf.link_nr; + } + atomic_set(&outp->lt.done, 0); + schedule_work(&outp->lt.work); + } else { + nvkm_notify_get(&outp->irq); + } + + if (wait) { + if (!wait_event_timeout(outp->lt.wait, + atomic_read(&outp->lt.done), + msecs_to_jiffies(2000))) + ret = -ETIMEDOUT; + } + + return ret; +} + +static void +nvkm_output_dp_enable(struct nvkm_output_dp *outp, bool present) +{ + struct nvkm_i2c_port *port = outp->base.edid; + if (present) { + if (!outp->present) { + nvkm_i2c(port)->acquire_pad(port, 0); + DBG("aux power -> always\n"); + outp->present = true; + } + nvkm_output_dp_train(&outp->base, 0, true); + } else { + if (outp->present) { + nvkm_i2c(port)->release_pad(port); + DBG("aux power -> demand\n"); + outp->present = false; + } + atomic_set(&outp->lt.done, 0); + } +} + +static void +nvkm_output_dp_detect(struct nvkm_output_dp *outp) +{ + struct nvkm_i2c_port *port = outp->base.edid; + int ret = nvkm_i2c(port)->acquire_pad(port, 0); + if (ret == 0) { + ret = nv_rdaux(outp->base.edid, DPCD_RC00_DPCD_REV, + outp->dpcd, sizeof(outp->dpcd)); + nvkm_output_dp_enable(outp, ret == 0); + nvkm_i2c(port)->release_pad(port); + } +} + +static int +nvkm_output_dp_hpd(struct nvkm_notify *notify) +{ + struct nvkm_connector *conn = container_of(notify, typeof(*conn), hpd); + struct nvkm_output_dp *outp; + struct nvkm_disp *disp = nvkm_disp(conn); + const struct nvkm_i2c_ntfy_rep *line = notify->data; + struct nvif_notify_conn_rep_v0 rep = {}; + + list_for_each_entry(outp, &disp->outp, base.head) { + if (outp->base.conn == conn && + outp->info.type == DCB_OUTPUT_DP) { + DBG("HPD: %d\n", line->mask); + nvkm_output_dp_detect(outp); + + 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; + } + } + + WARN_ON(1); + return NVKM_NOTIFY_DROP; +} + +static int +nvkm_output_dp_irq(struct nvkm_notify *notify) +{ + struct nvkm_output_dp *outp = container_of(notify, typeof(*outp), irq); + struct nvkm_disp *disp = nvkm_disp(outp); + const struct nvkm_i2c_ntfy_rep *line = notify->data; + struct nvif_notify_conn_rep_v0 rep = { + .mask = NVIF_NOTIFY_CONN_V0_IRQ, + }; + int index = outp->base.info.connector; + + DBG("IRQ: %d\n", line->mask); + nvkm_output_dp_train(&outp->base, 0, true); + + nvkm_event_send(&disp->hpd, rep.mask, index, &rep, sizeof(rep)); + return NVKM_NOTIFY_DROP; +} + +int +_nvkm_output_dp_fini(struct nvkm_object *object, bool suspend) +{ + struct nvkm_output_dp *outp = (void *)object; + nvkm_notify_put(&outp->irq); + nvkm_output_dp_enable(outp, false); + return nvkm_output_fini(&outp->base, suspend); +} + +int +_nvkm_output_dp_init(struct nvkm_object *object) +{ + struct nvkm_output_dp *outp = (void *)object; + nvkm_output_dp_detect(outp); + return nvkm_output_init(&outp->base); +} + +void +_nvkm_output_dp_dtor(struct nvkm_object *object) +{ + struct nvkm_output_dp *outp = (void *)object; + nvkm_notify_fini(&outp->irq); + nvkm_output_destroy(&outp->base); +} + +int +nvkm_output_dp_create_(struct nvkm_object *parent, + struct nvkm_object *engine, + struct nvkm_oclass *oclass, + struct dcb_output *info, int index, + int length, void **pobject) +{ + struct nvkm_bios *bios = nvkm_bios(parent); + struct nvkm_i2c *i2c = nvkm_i2c(parent); + struct nvkm_output_dp *outp; + u8 hdr, cnt, len; + u32 data; + int ret; + + ret = nvkm_output_create_(parent, engine, oclass, info, index, + length, pobject); + outp = *pobject; + if (ret) + return ret; + + nvkm_notify_fini(&outp->base.conn->hpd); + + /* access to the aux channel is not optional... */ + if (!outp->base.edid) { + ERR("aux channel not found\n"); + return -ENODEV; + } + + /* nor is the bios data for this output... */ + data = nvbios_dpout_match(bios, outp->base.info.hasht, + outp->base.info.hashm, &outp->version, + &hdr, &cnt, &len, &outp->info); + if (!data) { + ERR("no bios dp data\n"); + return -ENODEV; + } + + DBG("bios dp %02x %02x %02x %02x\n", outp->version, hdr, cnt, len); + + /* link training */ + INIT_WORK(&outp->lt.work, nvkm_dp_train); + init_waitqueue_head(&outp->lt.wait); + atomic_set(&outp->lt.done, 0); + + /* link maintenance */ + ret = nvkm_notify_init(NULL, &i2c->event, nvkm_output_dp_irq, true, + &(struct nvkm_i2c_ntfy_req) { + .mask = NVKM_I2C_IRQ, + .port = outp->base.edid->index, + }, + sizeof(struct nvkm_i2c_ntfy_req), + sizeof(struct nvkm_i2c_ntfy_rep), + &outp->irq); + if (ret) { + ERR("error monitoring aux irq event: %d\n", ret); + return ret; + } + + /* hotplug detect, replaces gpio-based mechanism with aux events */ + ret = nvkm_notify_init(NULL, &i2c->event, nvkm_output_dp_hpd, true, + &(struct nvkm_i2c_ntfy_req) { + .mask = NVKM_I2C_PLUG | NVKM_I2C_UNPLUG, + .port = outp->base.edid->index, + }, + sizeof(struct nvkm_i2c_ntfy_req), + sizeof(struct nvkm_i2c_ntfy_rep), + &outp->base.conn->hpd); + if (ret) { + ERR("error monitoring aux hpd events: %d\n", ret); + return ret; + } + + return 0; +} + +int +_nvkm_output_dp_ctor(struct nvkm_object *parent, + struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *info, u32 index, + struct nvkm_object **pobject) +{ + struct nvkm_output_dp *outp; + int ret; + + ret = nvkm_output_dp_create(parent, engine, oclass, info, index, &outp); + *pobject = nv_object(outp); + if (ret) + return ret; + + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/outpdp.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/outpdp.h new file mode 100644 index 000000000..70c77aec4 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/outpdp.h @@ -0,0 +1,61 @@ +#ifndef __NVKM_DISP_OUTP_DP_H__ +#define __NVKM_DISP_OUTP_DP_H__ +#include "outp.h" + +#include +#include +#include + +struct nvkm_output_dp { + struct nvkm_output base; + + struct nvbios_dpout info; + u8 version; + + struct nvkm_notify irq; + bool present; + u8 dpcd[16]; + + struct { + struct work_struct work; + wait_queue_head_t wait; + atomic_t done; + } lt; +}; + +#define nvkm_output_dp_create(p,e,c,b,i,d) \ + nvkm_output_dp_create_((p), (e), (c), (b), (i), sizeof(**d), (void **)d) +#define nvkm_output_dp_destroy(d) ({ \ + struct nvkm_output_dp *_outp = (d); \ + _nvkm_output_dp_dtor(nv_object(_outp)); \ +}) +#define nvkm_output_dp_init(d) ({ \ + struct nvkm_output_dp *_outp = (d); \ + _nvkm_output_dp_init(nv_object(_outp)); \ +}) +#define nvkm_output_dp_fini(d,s) ({ \ + struct nvkm_output_dp *_outp = (d); \ + _nvkm_output_dp_fini(nv_object(_outp), (s)); \ +}) + +int nvkm_output_dp_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, struct dcb_output *, + int, int, void **); + +int _nvkm_output_dp_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); +void _nvkm_output_dp_dtor(struct nvkm_object *); +int _nvkm_output_dp_init(struct nvkm_object *); +int _nvkm_output_dp_fini(struct nvkm_object *, bool); + +struct nvkm_output_dp_impl { + struct nvkm_output_impl base; + int (*pattern)(struct nvkm_output_dp *, int); + int (*lnk_pwr)(struct nvkm_output_dp *, int nr); + int (*lnk_ctl)(struct nvkm_output_dp *, int nr, int bw, bool ef); + int (*drv_ctl)(struct nvkm_output_dp *, int ln, int vs, int pe, int pc); +}; + +int nvkm_output_dp_train(struct nvkm_output *, u32 rate, bool wait); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/piornv50.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/piornv50.c new file mode 100644 index 000000000..2a1d8871b --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/piornv50.c @@ -0,0 +1,170 @@ +/* + * 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 "outpdp.h" + +#include +#include +#include + +#include +#include + +/****************************************************************************** + * TMDS + *****************************************************************************/ + +static int +nv50_pior_tmds_ctor(struct nvkm_object *parent, + struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *info, u32 index, + struct nvkm_object **pobject) +{ + struct nvkm_i2c *i2c = nvkm_i2c(parent); + struct nvkm_output *outp; + int ret; + + ret = nvkm_output_create(parent, engine, oclass, info, index, &outp); + *pobject = nv_object(outp); + if (ret) + return ret; + + outp->edid = i2c->find_type(i2c, NV_I2C_TYPE_EXTDDC(outp->info.extdev)); + return 0; +} + +struct nvkm_output_impl +nv50_pior_tmds_impl = { + .base.handle = DCB_OUTPUT_TMDS | 0x0100, + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_pior_tmds_ctor, + .dtor = _nvkm_output_dtor, + .init = _nvkm_output_init, + .fini = _nvkm_output_fini, + }, +}; + +/****************************************************************************** + * DisplayPort + *****************************************************************************/ + +static int +nv50_pior_dp_pattern(struct nvkm_output_dp *outp, int pattern) +{ + struct nvkm_i2c_port *port = outp->base.edid; + if (port && port->func->pattern) + return port->func->pattern(port, pattern); + return port ? 0 : -ENODEV; +} + +static int +nv50_pior_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr) +{ + return 0; +} + +static int +nv50_pior_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef) +{ + struct nvkm_i2c_port *port = outp->base.edid; + if (port && port->func->lnk_ctl) + return port->func->lnk_ctl(port, nr, bw, ef); + return port ? 0 : -ENODEV; +} + +static int +nv50_pior_dp_drv_ctl(struct nvkm_output_dp *outp, int ln, int vs, int pe, int pc) +{ + struct nvkm_i2c_port *port = outp->base.edid; + if (port && port->func->drv_ctl) + return port->func->drv_ctl(port, ln, vs, pe); + return port ? 0 : -ENODEV; +} + +static int +nv50_pior_dp_ctor(struct nvkm_object *parent, + struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *info, u32 index, + struct nvkm_object **pobject) +{ + struct nvkm_i2c *i2c = nvkm_i2c(parent); + struct nvkm_output_dp *outp; + int ret; + + ret = nvkm_output_dp_create(parent, engine, oclass, info, index, &outp); + *pobject = nv_object(outp); + if (ret) + return ret; + + outp->base.edid = i2c->find_type(i2c, NV_I2C_TYPE_EXTAUX( + outp->base.info.extdev)); + return 0; +} + +struct nvkm_output_dp_impl +nv50_pior_dp_impl = { + .base.base.handle = DCB_OUTPUT_DP | 0x0010, + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_pior_dp_ctor, + .dtor = _nvkm_output_dp_dtor, + .init = _nvkm_output_dp_init, + .fini = _nvkm_output_dp_fini, + }, + .pattern = nv50_pior_dp_pattern, + .lnk_pwr = nv50_pior_dp_lnk_pwr, + .lnk_ctl = nv50_pior_dp_lnk_ctl, + .drv_ctl = nv50_pior_dp_drv_ctl, +}; + +/****************************************************************************** + * General PIOR handling + *****************************************************************************/ + +int +nv50_pior_power(NV50_DISP_MTHD_V1) +{ + const u32 soff = outp->or * 0x800; + union { + struct nv50_disp_pior_pwr_v0 v0; + } *args = data; + u32 ctrl, type; + int ret; + + nv_ioctl(object, "disp pior pwr size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(object, "disp pior pwr vers %d state %d type %x\n", + args->v0.version, args->v0.state, args->v0.type); + if (args->v0.type > 0x0f) + return -EINVAL; + ctrl = !!args->v0.state; + type = args->v0.type; + } else + return ret; + + nv_wait(priv, 0x61e004 + soff, 0x80000000, 0x00000000); + nv_mask(priv, 0x61e004 + soff, 0x80000101, 0x80000000 | ctrl); + nv_wait(priv, 0x61e004 + soff, 0x80000000, 0x00000000); + priv->pior.type[outp->or] = type; + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h new file mode 100644 index 000000000..961ce8bb2 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h @@ -0,0 +1,42 @@ +#ifndef __NVKM_DISP_PRIV_H__ +#define __NVKM_DISP_PRIV_H__ +#include + +struct nvkm_disp_impl { + struct nvkm_oclass base; + struct nvkm_oclass **outp; + struct nvkm_oclass **conn; + const struct nvkm_event_func *vblank; +}; + +#define nvkm_disp_create(p,e,c,h,i,x,d) \ + nvkm_disp_create_((p), (e), (c), (h), (i), (x), \ + sizeof(**d), (void **)d) +#define nvkm_disp_destroy(d) ({ \ + struct nvkm_disp *disp = (d); \ + _nvkm_disp_dtor(nv_object(disp)); \ +}) +#define nvkm_disp_init(d) ({ \ + struct nvkm_disp *disp = (d); \ + _nvkm_disp_init(nv_object(disp)); \ +}) +#define nvkm_disp_fini(d,s) ({ \ + struct nvkm_disp *disp = (d); \ + _nvkm_disp_fini(nv_object(disp), (s)); \ +}) + +int nvkm_disp_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, int heads, + const char *, const char *, int, void **); +void _nvkm_disp_dtor(struct nvkm_object *); +int _nvkm_disp_init(struct nvkm_object *); +int _nvkm_disp_fini(struct nvkm_object *, bool); + +extern struct nvkm_oclass *nvkm_output_oclass; +extern struct nvkm_oclass *nvkm_connector_oclass; + +int nvkm_disp_vblank_ctor(struct nvkm_object *, void *data, u32 size, + struct nvkm_notify *); +void nvkm_disp_vblank(struct nvkm_disp *, int head); +int nvkm_disp_ntfy(struct nvkm_object *, u32, struct nvkm_event **); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c new file mode 100644 index 000000000..8918da7ff --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c @@ -0,0 +1,145 @@ +/* + * 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 "outpdp.h" + +#include +#include + +static inline u32 +g94_sor_soff(struct nvkm_output_dp *outp) +{ + return (ffs(outp->base.info.or) - 1) * 0x800; +} + +static inline u32 +g94_sor_loff(struct nvkm_output_dp *outp) +{ + return g94_sor_soff(outp) + !(outp->base.info.sorconf.link & 1) * 0x80; +} + +static inline u32 +g94_sor_dp_lane_map(struct nv50_disp_priv *priv, u8 lane) +{ + static const u8 mcp89[] = { 24, 16, 8, 0 }; /* thanks, apple.. */ + static const u8 g94[] = { 16, 8, 0, 24 }; + if (nv_device(priv)->chipset == 0xaf) + return mcp89[lane]; + return g94[lane]; +} + +static int +g94_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern) +{ + struct nv50_disp_priv *priv = (void *)nvkm_disp(outp); + const u32 loff = g94_sor_loff(outp); + nv_mask(priv, 0x61c10c + loff, 0x0f000000, pattern << 24); + return 0; +} + +int +g94_sor_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr) +{ + struct nv50_disp_priv *priv = (void *)nvkm_disp(outp); + const u32 soff = g94_sor_soff(outp); + const u32 loff = g94_sor_loff(outp); + u32 mask = 0, i; + + for (i = 0; i < nr; i++) + mask |= 1 << (g94_sor_dp_lane_map(priv, i) >> 3); + + nv_mask(priv, 0x61c130 + loff, 0x0000000f, mask); + nv_mask(priv, 0x61c034 + soff, 0x80000000, 0x80000000); + nv_wait(priv, 0x61c034 + soff, 0x80000000, 0x00000000); + return 0; +} + +static int +g94_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef) +{ + struct nv50_disp_priv *priv = (void *)nvkm_disp(outp); + const u32 soff = g94_sor_soff(outp); + const u32 loff = g94_sor_loff(outp); + u32 dpctrl = 0x00000000; + u32 clksor = 0x00000000; + + dpctrl |= ((1 << nr) - 1) << 16; + if (ef) + dpctrl |= 0x00004000; + if (bw > 0x06) + clksor |= 0x00040000; + + nv_mask(priv, 0x614300 + soff, 0x000c0000, clksor); + nv_mask(priv, 0x61c10c + loff, 0x001f4000, dpctrl); + return 0; +} + +static int +g94_sor_dp_drv_ctl(struct nvkm_output_dp *outp, int ln, int vs, int pe, int pc) +{ + struct nv50_disp_priv *priv = (void *)nvkm_disp(outp); + struct nvkm_bios *bios = nvkm_bios(priv); + const u32 shift = g94_sor_dp_lane_map(priv, ln); + const u32 loff = g94_sor_loff(outp); + u32 addr, data[3]; + u8 ver, hdr, cnt, len; + struct nvbios_dpout info; + struct nvbios_dpcfg ocfg; + + addr = nvbios_dpout_match(bios, outp->base.info.hasht, + outp->base.info.hashm, + &ver, &hdr, &cnt, &len, &info); + if (!addr) + return -ENODEV; + + addr = nvbios_dpcfg_match(bios, addr, 0, vs, pe, + &ver, &hdr, &cnt, &len, &ocfg); + if (!addr) + return -EINVAL; + + data[0] = nv_rd32(priv, 0x61c118 + loff) & ~(0x000000ff << shift); + data[1] = nv_rd32(priv, 0x61c120 + loff) & ~(0x000000ff << shift); + data[2] = nv_rd32(priv, 0x61c130 + loff); + if ((data[2] & 0x0000ff00) < (ocfg.tx_pu << 8) || ln == 0) + data[2] = (data[2] & ~0x0000ff00) | (ocfg.tx_pu << 8); + nv_wr32(priv, 0x61c118 + loff, data[0] | (ocfg.dc << shift)); + nv_wr32(priv, 0x61c120 + loff, data[1] | (ocfg.pe << shift)); + nv_wr32(priv, 0x61c130 + loff, data[2] | (ocfg.tx_pu << 8)); + return 0; +} + +struct nvkm_output_dp_impl +g94_sor_dp_impl = { + .base.base.handle = DCB_OUTPUT_DP, + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_output_dp_ctor, + .dtor = _nvkm_output_dp_dtor, + .init = _nvkm_output_dp_init, + .fini = _nvkm_output_dp_fini, + }, + .pattern = g94_sor_dp_pattern, + .lnk_pwr = g94_sor_dp_lnk_pwr, + .lnk_ctl = g94_sor_dp_lnk_ctl, + .drv_ctl = g94_sor_dp_drv_ctl, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf110.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf110.c new file mode 100644 index 000000000..52fbe4880 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf110.c @@ -0,0 +1,124 @@ +/* + * 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 "outpdp.h" + +static inline u32 +gf110_sor_soff(struct nvkm_output_dp *outp) +{ + return (ffs(outp->base.info.or) - 1) * 0x800; +} + +static inline u32 +gf110_sor_loff(struct nvkm_output_dp *outp) +{ + return gf110_sor_soff(outp) + !(outp->base.info.sorconf.link & 1) * 0x80; +} + +static inline u32 +gf110_sor_dp_lane_map(struct nv50_disp_priv *priv, u8 lane) +{ + static const u8 gf110[] = { 16, 8, 0, 24 }; + return gf110[lane]; +} + +static int +gf110_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern) +{ + struct nv50_disp_priv *priv = (void *)nvkm_disp(outp); + const u32 loff = gf110_sor_loff(outp); + nv_mask(priv, 0x61c110 + loff, 0x0f0f0f0f, 0x01010101 * pattern); + return 0; +} + +int +gf110_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef) +{ + struct nv50_disp_priv *priv = (void *)nvkm_disp(outp); + const u32 soff = gf110_sor_soff(outp); + const u32 loff = gf110_sor_loff(outp); + u32 dpctrl = 0x00000000; + u32 clksor = 0x00000000; + + clksor |= bw << 18; + dpctrl |= ((1 << nr) - 1) << 16; + if (ef) + dpctrl |= 0x00004000; + + nv_mask(priv, 0x612300 + soff, 0x007c0000, clksor); + nv_mask(priv, 0x61c10c + loff, 0x001f4000, dpctrl); + return 0; +} + +static int +gf110_sor_dp_drv_ctl(struct nvkm_output_dp *outp, + int ln, int vs, int pe, int pc) +{ + struct nv50_disp_priv *priv = (void *)nvkm_disp(outp); + struct nvkm_bios *bios = nvkm_bios(priv); + const u32 shift = gf110_sor_dp_lane_map(priv, ln); + const u32 loff = gf110_sor_loff(outp); + u32 addr, data[4]; + u8 ver, hdr, cnt, len; + struct nvbios_dpout info; + struct nvbios_dpcfg ocfg; + + addr = nvbios_dpout_match(bios, outp->base.info.hasht, + outp->base.info.hashm, + &ver, &hdr, &cnt, &len, &info); + if (!addr) + return -ENODEV; + + addr = nvbios_dpcfg_match(bios, addr, pc, vs, pe, + &ver, &hdr, &cnt, &len, &ocfg); + if (!addr) + return -EINVAL; + + data[0] = nv_rd32(priv, 0x61c118 + loff) & ~(0x000000ff << shift); + data[1] = nv_rd32(priv, 0x61c120 + loff) & ~(0x000000ff << shift); + data[2] = nv_rd32(priv, 0x61c130 + loff); + if ((data[2] & 0x0000ff00) < (ocfg.tx_pu << 8) || ln == 0) + data[2] = (data[2] & ~0x0000ff00) | (ocfg.tx_pu << 8); + nv_wr32(priv, 0x61c118 + loff, data[0] | (ocfg.dc << shift)); + nv_wr32(priv, 0x61c120 + loff, data[1] | (ocfg.pe << shift)); + nv_wr32(priv, 0x61c130 + loff, data[2] | (ocfg.tx_pu << 8)); + data[3] = nv_rd32(priv, 0x61c13c + loff) & ~(0x000000ff << shift); + nv_wr32(priv, 0x61c13c + loff, data[3] | (ocfg.pc << shift)); + return 0; +} + +struct nvkm_output_dp_impl +gf110_sor_dp_impl = { + .base.base.handle = DCB_OUTPUT_DP, + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_output_dp_ctor, + .dtor = _nvkm_output_dp_dtor, + .init = _nvkm_output_dp_init, + .fini = _nvkm_output_dp_fini, + }, + .pattern = gf110_sor_dp_pattern, + .lnk_pwr = g94_sor_dp_lnk_pwr, + .lnk_ctl = gf110_sor_dp_lnk_ctl, + .drv_ctl = gf110_sor_dp_drv_ctl, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm204.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm204.c new file mode 100644 index 000000000..1e40dfe11 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm204.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 "nv50.h" +#include "outpdp.h" + +#include + +static inline u32 +gm204_sor_soff(struct nvkm_output_dp *outp) +{ + return (ffs(outp->base.info.or) - 1) * 0x800; +} + +static inline u32 +gm204_sor_loff(struct nvkm_output_dp *outp) +{ + return gm204_sor_soff(outp) + !(outp->base.info.sorconf.link & 1) * 0x80; +} + +void +gm204_sor_magic(struct nvkm_output *outp) +{ + struct nv50_disp_priv *priv = (void *)nvkm_disp(outp); + const u32 soff = outp->or * 0x100; + const u32 data = outp->or + 1; + if (outp->info.sorconf.link & 1) + nv_mask(priv, 0x612308 + soff, 0x0000001f, 0x00000000 | data); + if (outp->info.sorconf.link & 2) + nv_mask(priv, 0x612388 + soff, 0x0000001f, 0x00000010 | data); +} + +static inline u32 +gm204_sor_dp_lane_map(struct nv50_disp_priv *priv, u8 lane) +{ + return lane * 0x08; +} + +static int +gm204_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern) +{ + struct nv50_disp_priv *priv = (void *)nvkm_disp(outp); + const u32 soff = gm204_sor_soff(outp); + const u32 data = 0x01010101 * pattern; + if (outp->base.info.sorconf.link & 1) + nv_mask(priv, 0x61c110 + soff, 0x0f0f0f0f, data); + else + nv_mask(priv, 0x61c12c + soff, 0x0f0f0f0f, data); + return 0; +} + +static int +gm204_sor_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr) +{ + struct nv50_disp_priv *priv = (void *)nvkm_disp(outp); + const u32 soff = gm204_sor_soff(outp); + const u32 loff = gm204_sor_loff(outp); + u32 mask = 0, i; + + for (i = 0; i < nr; i++) + mask |= 1 << (gm204_sor_dp_lane_map(priv, i) >> 3); + + nv_mask(priv, 0x61c130 + loff, 0x0000000f, mask); + nv_mask(priv, 0x61c034 + soff, 0x80000000, 0x80000000); + nv_wait(priv, 0x61c034 + soff, 0x80000000, 0x00000000); + return 0; +} + +static int +gm204_sor_dp_drv_ctl(struct nvkm_output_dp *outp, + int ln, int vs, int pe, int pc) +{ + struct nv50_disp_priv *priv = (void *)nvkm_disp(outp); + struct nvkm_bios *bios = nvkm_bios(priv); + const u32 shift = gm204_sor_dp_lane_map(priv, ln); + const u32 loff = gm204_sor_loff(outp); + u32 addr, data[4]; + u8 ver, hdr, cnt, len; + struct nvbios_dpout info; + struct nvbios_dpcfg ocfg; + + addr = nvbios_dpout_match(bios, outp->base.info.hasht, + outp->base.info.hashm, + &ver, &hdr, &cnt, &len, &info); + if (!addr) + return -ENODEV; + + addr = nvbios_dpcfg_match(bios, addr, pc, vs, pe, + &ver, &hdr, &cnt, &len, &ocfg); + if (!addr) + return -EINVAL; + + data[0] = nv_rd32(priv, 0x61c118 + loff) & ~(0x000000ff << shift); + data[1] = nv_rd32(priv, 0x61c120 + loff) & ~(0x000000ff << shift); + data[2] = nv_rd32(priv, 0x61c130 + loff); + if ((data[2] & 0x0000ff00) < (ocfg.tx_pu << 8) || ln == 0) + data[2] = (data[2] & ~0x0000ff00) | (ocfg.tx_pu << 8); + nv_wr32(priv, 0x61c118 + loff, data[0] | (ocfg.dc << shift)); + nv_wr32(priv, 0x61c120 + loff, data[1] | (ocfg.pe << shift)); + nv_wr32(priv, 0x61c130 + loff, data[2] | (ocfg.tx_pu << 8)); + data[3] = nv_rd32(priv, 0x61c13c + loff) & ~(0x000000ff << shift); + nv_wr32(priv, 0x61c13c + loff, data[3] | (ocfg.pc << shift)); + return 0; +} + +struct nvkm_output_dp_impl +gm204_sor_dp_impl = { + .base.base.handle = DCB_OUTPUT_DP, + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_output_dp_ctor, + .dtor = _nvkm_output_dp_dtor, + .init = _nvkm_output_dp_init, + .fini = _nvkm_output_dp_fini, + }, + .pattern = gm204_sor_dp_pattern, + .lnk_pwr = gm204_sor_dp_lnk_pwr, + .lnk_ctl = gf110_sor_dp_lnk_ctl, + .drv_ctl = gm204_sor_dp_drv_ctl, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/sornv50.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/sornv50.c new file mode 100644 index 000000000..b229a311c --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/sornv50.c @@ -0,0 +1,56 @@ +/* + * 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 "outp.h" + +#include +#include + +#include +#include + +int +nv50_sor_power(NV50_DISP_MTHD_V1) +{ + union { + struct nv50_disp_sor_pwr_v0 v0; + } *args = data; + const u32 soff = outp->or * 0x800; + u32 stat; + int ret; + + nv_ioctl(object, "disp sor pwr size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(object, "disp sor pwr vers %d state %d\n", + args->v0.version, args->v0.state); + stat = !!args->v0.state; + } else + return ret; + + nv_wait(priv, 0x61c004 + soff, 0x80000000, 0x00000000); + nv_mask(priv, 0x61c004 + soff, 0x80000001, 0x80000000 | stat); + nv_wait(priv, 0x61c004 + soff, 0x80000000, 0x00000000); + nv_wait(priv, 0x61c030 + soff, 0x10000000, 0x00000000); + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/vga.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/vga.c new file mode 100644 index 000000000..c4622c738 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/disp/vga.c @@ -0,0 +1,219 @@ +/* + * 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 + +u8 +nv_rdport(void *obj, int head, u16 port) +{ + struct nvkm_device *device = nv_device(obj); + + if (device->card_type >= NV_50) + return nv_rd08(obj, 0x601000 + port); + + if (port == 0x03c0 || port == 0x03c1 || /* AR */ + port == 0x03c2 || port == 0x03da || /* INP0 */ + port == 0x03d4 || port == 0x03d5) /* CR */ + return nv_rd08(obj, 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 nv_rd08(obj, 0x0c0000 + (head * 0x2000) + port); + } + + nv_error(obj, "unknown vga port 0x%04x\n", port); + return 0x00; +} + +void +nv_wrport(void *obj, int head, u16 port, u8 data) +{ + struct nvkm_device *device = nv_device(obj); + + if (device->card_type >= NV_50) + nv_wr08(obj, 0x601000 + port, data); + else + if (port == 0x03c0 || port == 0x03c1 || /* AR */ + port == 0x03c2 || port == 0x03da || /* INP0 */ + port == 0x03d4 || port == 0x03d5) /* CR */ + nv_wr08(obj, 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 */ + nv_wr08(obj, 0x0c0000 + (head * 0x2000) + port, data); + } else + nv_error(obj, "unknown vga port 0x%04x\n", port); +} + +u8 +nv_rdvgas(void *obj, int head, u8 index) +{ + nv_wrport(obj, head, 0x03c4, index); + return nv_rdport(obj, head, 0x03c5); +} + +void +nv_wrvgas(void *obj, int head, u8 index, u8 value) +{ + nv_wrport(obj, head, 0x03c4, index); + nv_wrport(obj, head, 0x03c5, value); +} + +u8 +nv_rdvgag(void *obj, int head, u8 index) +{ + nv_wrport(obj, head, 0x03ce, index); + return nv_rdport(obj, head, 0x03cf); +} + +void +nv_wrvgag(void *obj, int head, u8 index, u8 value) +{ + nv_wrport(obj, head, 0x03ce, index); + nv_wrport(obj, head, 0x03cf, value); +} + +u8 +nv_rdvgac(void *obj, int head, u8 index) +{ + nv_wrport(obj, head, 0x03d4, index); + return nv_rdport(obj, head, 0x03d5); +} + +void +nv_wrvgac(void *obj, int head, u8 index, u8 value) +{ + nv_wrport(obj, head, 0x03d4, index); + nv_wrport(obj, head, 0x03d5, value); +} + +u8 +nv_rdvgai(void *obj, int head, u16 port, u8 index) +{ + if (port == 0x03c4) return nv_rdvgas(obj, head, index); + if (port == 0x03ce) return nv_rdvgag(obj, head, index); + if (port == 0x03d4) return nv_rdvgac(obj, head, index); + nv_error(obj, "unknown indexed vga port 0x%04x\n", port); + return 0x00; +} + +void +nv_wrvgai(void *obj, int head, u16 port, u8 index, u8 value) +{ + if (port == 0x03c4) nv_wrvgas(obj, head, index, value); + else if (port == 0x03ce) nv_wrvgag(obj, head, index, value); + else if (port == 0x03d4) nv_wrvgac(obj, head, index, value); + else nv_error(obj, "unknown indexed vga port 0x%04x\n", port); +} + +bool +nv_lockvgac(void *obj, bool lock) +{ + struct nvkm_device *dev = nv_device(obj); + + bool locked = !nv_rdvgac(obj, 0, 0x1f); + u8 data = lock ? 0x99 : 0x57; + if (dev->card_type < NV_50) + nv_wrvgac(obj, 0, 0x1f, data); + else + nv_wrvgac(obj, 0, 0x3f, data); + if (dev->chipset == 0x11) { + if (!(nv_rd32(obj, 0x001084) & 0x10000000)) + nv_wrvgac(obj, 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 +nv_rdvgaowner(void *obj) +{ + if (nv_device(obj)->card_type < NV_50) { + if (nv_device(obj)->chipset == 0x11) { + u32 tied = nv_rd32(obj, 0x001084) & 0x10000000; + if (tied == 0) { + u8 slA = nv_rdvgac(obj, 0, 0x28) & 0x80; + u8 tvA = nv_rdvgac(obj, 0, 0x33) & 0x01; + u8 slB = nv_rdvgac(obj, 1, 0x28) & 0x80; + u8 tvB = nv_rdvgac(obj, 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 nv_rdvgac(obj, 0, 0x44); + } + + nv_error(obj, "rdvgaowner after nv4x\n"); + return 0x00; +} + +void +nv_wrvgaowner(void *obj, u8 select) +{ + if (nv_device(obj)->card_type < NV_50) { + u8 owner = (select == 1) ? 3 : select; + if (nv_device(obj)->chipset == 0x11) { + /* workaround hw lockup bug */ + nv_rdvgac(obj, 0, 0x1f); + nv_rdvgac(obj, 1, 0x1f); + } + + nv_wrvgac(obj, 0, 0x44, owner); + + if (nv_device(obj)->chipset == 0x11) { + nv_wrvgac(obj, 0, 0x2e, owner); + nv_wrvgac(obj, 0, 0x2e, owner); + } + } else + nv_error(obj, "wrvgaowner after nv4x\n"); +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/dmaobj/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/dmaobj/Kbuild new file mode 100644 index 000000000..7529632db --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/dmaobj/Kbuild @@ -0,0 +1,5 @@ +nvkm-y += nvkm/engine/dmaobj/base.o +nvkm-y += nvkm/engine/dmaobj/nv04.o +nvkm-y += nvkm/engine/dmaobj/nv50.o +nvkm-y += nvkm/engine/dmaobj/gf100.o +nvkm-y += nvkm/engine/dmaobj/gf110.o diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/dmaobj/base.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/dmaobj/base.c new file mode 100644 index 000000000..a2b60d86b --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/dmaobj/base.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 "priv.h" + +#include +#include +#include +#include + +#include +#include + +static int +nvkm_dmaobj_bind(struct nvkm_dmaobj *dmaobj, struct nvkm_object *parent, + struct nvkm_gpuobj **pgpuobj) +{ + const struct nvkm_dmaeng_impl *impl = (void *) + nv_oclass(nv_object(dmaobj)->engine); + int ret = 0; + + if (nv_object(dmaobj) == parent) { /* ctor bind */ + if (nv_mclass(parent->parent) == NV_DEVICE) { + /* delayed, or no, binding */ + return 0; + } + ret = impl->bind(dmaobj, parent, pgpuobj); + if (ret == 0) + nvkm_object_ref(NULL, &parent); + return ret; + } + + return impl->bind(dmaobj, parent, pgpuobj); +} + +int +nvkm_dmaobj_create_(struct nvkm_object *parent, + struct nvkm_object *engine, + struct nvkm_oclass *oclass, void **pdata, u32 *psize, + int length, void **pobject) +{ + union { + struct nv_dma_v0 v0; + } *args = *pdata; + struct nvkm_instmem *instmem = nvkm_instmem(parent); + struct nvkm_client *client = nvkm_client(parent); + struct nvkm_device *device = nv_device(parent); + struct nvkm_fb *pfb = nvkm_fb(parent); + struct nvkm_dmaobj *dmaobj; + void *data = *pdata; + u32 size = *psize; + int ret; + + ret = nvkm_object_create_(parent, engine, oclass, 0, length, pobject); + dmaobj = *pobject; + if (ret) + return ret; + + nv_ioctl(parent, "create dma size %d\n", *psize); + if (nvif_unpack(args->v0, 0, 0, true)) { + nv_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: + if (!client->super) { + if (dmaobj->limit >= pfb->ram->size - instmem->reserved) + return -EACCES; + if (device->card_type >= NV_50) + return -EACCES; + } + dmaobj->target = NV_MEM_TARGET_VRAM; + break; + case NV_DMA_V0_TARGET_PCI: + if (!client->super) + return -EACCES; + dmaobj->target = NV_MEM_TARGET_PCI; + break; + case NV_DMA_V0_TARGET_PCI_US: + case NV_DMA_V0_TARGET_AGP: + if (!client->super) + return -EACCES; + 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; +} + +int +_nvkm_dmaeng_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + const struct nvkm_dmaeng_impl *impl = (void *)oclass; + struct nvkm_dmaeng *dmaeng; + int ret; + + ret = nvkm_engine_create(parent, engine, oclass, true, "DMAOBJ", + "dmaobj", &dmaeng); + *pobject = nv_object(dmaeng); + if (ret) + return ret; + + nv_engine(dmaeng)->sclass = impl->sclass; + dmaeng->bind = nvkm_dmaobj_bind; + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/dmaobj/gf100.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/dmaobj/gf100.c new file mode 100644 index 000000000..f880e5167 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/dmaobj/gf100.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 + */ +#include "priv.h" + +#include +#include +#include + +#include +#include + +struct gf100_dmaobj_priv { + struct nvkm_dmaobj base; + u32 flags0; + u32 flags5; +}; + +static int +gf100_dmaobj_bind(struct nvkm_dmaobj *dmaobj, struct nvkm_object *parent, + struct nvkm_gpuobj **pgpuobj) +{ + struct gf100_dmaobj_priv *priv = (void *)dmaobj; + int ret; + + if (!nv_iclass(parent, NV_ENGCTX_CLASS)) { + switch (nv_mclass(parent->parent)) { + case GT214_DISP_CORE_CHANNEL_DMA: + case GT214_DISP_BASE_CHANNEL_DMA: + case GT214_DISP_OVERLAY_CHANNEL_DMA: + break; + default: + return -EINVAL; + } + } else + return 0; + + ret = nvkm_gpuobj_new(parent, parent, 24, 32, 0, pgpuobj); + if (ret == 0) { + nv_wo32(*pgpuobj, 0x00, priv->flags0 | nv_mclass(dmaobj)); + nv_wo32(*pgpuobj, 0x04, lower_32_bits(priv->base.limit)); + nv_wo32(*pgpuobj, 0x08, lower_32_bits(priv->base.start)); + nv_wo32(*pgpuobj, 0x0c, upper_32_bits(priv->base.limit) << 24 | + upper_32_bits(priv->base.start)); + nv_wo32(*pgpuobj, 0x10, 0x00000000); + nv_wo32(*pgpuobj, 0x14, priv->flags5); + } + + return ret; +} + +static int +gf100_dmaobj_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_dmaeng *dmaeng = (void *)engine; + union { + struct gf100_dma_v0 v0; + } *args; + struct gf100_dmaobj_priv *priv; + u32 kind, user, unkn; + int ret; + + ret = nvkm_dmaobj_create(parent, engine, oclass, &data, &size, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + args = data; + + nv_ioctl(parent, "create gf100 dma size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_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 (priv->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; + priv->flags0 |= (kind << 22) | (user << 20); + priv->flags5 |= (unkn << 16); + + switch (priv->base.target) { + case NV_MEM_TARGET_VM: + priv->flags0 |= 0x00000000; + break; + case NV_MEM_TARGET_VRAM: + priv->flags0 |= 0x00010000; + break; + case NV_MEM_TARGET_PCI: + priv->flags0 |= 0x00020000; + break; + case NV_MEM_TARGET_PCI_NOSNOOP: + priv->flags0 |= 0x00030000; + break; + default: + return -EINVAL; + } + + switch (priv->base.access) { + case NV_MEM_ACCESS_VM: + break; + case NV_MEM_ACCESS_RO: + priv->flags0 |= 0x00040000; + break; + case NV_MEM_ACCESS_WO: + case NV_MEM_ACCESS_RW: + priv->flags0 |= 0x00080000; + break; + } + + return dmaeng->bind(&priv->base, nv_object(priv), (void *)pobject); +} + +static struct nvkm_ofuncs +gf100_dmaobj_ofuncs = { + .ctor = gf100_dmaobj_ctor, + .dtor = _nvkm_dmaobj_dtor, + .init = _nvkm_dmaobj_init, + .fini = _nvkm_dmaobj_fini, +}; + +static struct nvkm_oclass +gf100_dmaeng_sclass[] = { + { NV_DMA_FROM_MEMORY, &gf100_dmaobj_ofuncs }, + { NV_DMA_TO_MEMORY, &gf100_dmaobj_ofuncs }, + { NV_DMA_IN_MEMORY, &gf100_dmaobj_ofuncs }, + {} +}; + +struct nvkm_oclass * +gf100_dmaeng_oclass = &(struct nvkm_dmaeng_impl) { + .base.handle = NV_ENGINE(DMAOBJ, 0xc0), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_dmaeng_ctor, + .dtor = _nvkm_dmaeng_dtor, + .init = _nvkm_dmaeng_init, + .fini = _nvkm_dmaeng_fini, + }, + .sclass = gf100_dmaeng_sclass, + .bind = gf100_dmaobj_bind, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/dmaobj/gf110.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/dmaobj/gf110.c new file mode 100644 index 000000000..bf8f0f209 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/dmaobj/gf110.c @@ -0,0 +1,165 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +#include +#include +#include + +#include +#include + +struct gf110_dmaobj_priv { + struct nvkm_dmaobj base; + u32 flags0; +}; + +static int +gf110_dmaobj_bind(struct nvkm_dmaobj *dmaobj, struct nvkm_object *parent, + struct nvkm_gpuobj **pgpuobj) +{ + struct gf110_dmaobj_priv *priv = (void *)dmaobj; + int ret; + + if (!nv_iclass(parent, NV_ENGCTX_CLASS)) { + switch (nv_mclass(parent->parent)) { + case GF110_DISP_CORE_CHANNEL_DMA: + case GK104_DISP_CORE_CHANNEL_DMA: + case GK110_DISP_CORE_CHANNEL_DMA: + case GM107_DISP_CORE_CHANNEL_DMA: + case GM204_DISP_CORE_CHANNEL_DMA: + case GF110_DISP_BASE_CHANNEL_DMA: + case GK104_DISP_BASE_CHANNEL_DMA: + case GK110_DISP_BASE_CHANNEL_DMA: + case GF110_DISP_OVERLAY_CONTROL_DMA: + case GK104_DISP_OVERLAY_CONTROL_DMA: + break; + default: + return -EINVAL; + } + } else + return 0; + + ret = nvkm_gpuobj_new(parent, parent, 24, 32, 0, pgpuobj); + if (ret == 0) { + nv_wo32(*pgpuobj, 0x00, priv->flags0); + nv_wo32(*pgpuobj, 0x04, priv->base.start >> 8); + nv_wo32(*pgpuobj, 0x08, priv->base.limit >> 8); + nv_wo32(*pgpuobj, 0x0c, 0x00000000); + nv_wo32(*pgpuobj, 0x10, 0x00000000); + nv_wo32(*pgpuobj, 0x14, 0x00000000); + } + + return ret; +} + +static int +gf110_dmaobj_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_dmaeng *dmaeng = (void *)engine; + union { + struct gf110_dma_v0 v0; + } *args; + struct gf110_dmaobj_priv *priv; + u32 kind, page; + int ret; + + ret = nvkm_dmaobj_create(parent, engine, oclass, &data, &size, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + args = data; + + nv_ioctl(parent, "create gf110 dma size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_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 (priv->base.target != NV_MEM_TARGET_VM) { + kind = GF110_DMA_V0_KIND_PITCH; + page = GF110_DMA_V0_PAGE_SP; + } else { + kind = GF110_DMA_V0_KIND_VM; + page = GF110_DMA_V0_PAGE_LP; + } + } else + return ret; + + if (page > 1) + return -EINVAL; + priv->flags0 = (kind << 20) | (page << 6); + + switch (priv->base.target) { + case NV_MEM_TARGET_VRAM: + priv->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 dmaeng->bind(&priv->base, nv_object(priv), (void *)pobject); +} + +static struct nvkm_ofuncs +gf110_dmaobj_ofuncs = { + .ctor = gf110_dmaobj_ctor, + .dtor = _nvkm_dmaobj_dtor, + .init = _nvkm_dmaobj_init, + .fini = _nvkm_dmaobj_fini, +}; + +static struct nvkm_oclass +gf110_dmaeng_sclass[] = { + { NV_DMA_FROM_MEMORY, &gf110_dmaobj_ofuncs }, + { NV_DMA_TO_MEMORY, &gf110_dmaobj_ofuncs }, + { NV_DMA_IN_MEMORY, &gf110_dmaobj_ofuncs }, + {} +}; + +struct nvkm_oclass * +gf110_dmaeng_oclass = &(struct nvkm_dmaeng_impl) { + .base.handle = NV_ENGINE(DMAOBJ, 0xd0), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_dmaeng_ctor, + .dtor = _nvkm_dmaeng_dtor, + .init = _nvkm_dmaeng_init, + .fini = _nvkm_dmaeng_fini, + }, + .sclass = gf110_dmaeng_sclass, + .bind = gf110_dmaobj_bind, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/dmaobj/nv04.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/dmaobj/nv04.c new file mode 100644 index 000000000..b4379c2a2 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/dmaobj/nv04.c @@ -0,0 +1,163 @@ +/* + * 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 + +struct nv04_dmaobj_priv { + struct nvkm_dmaobj base; + bool clone; + u32 flags0; + u32 flags2; +}; + +static int +nv04_dmaobj_bind(struct nvkm_dmaobj *dmaobj, struct nvkm_object *parent, + struct nvkm_gpuobj **pgpuobj) +{ + struct nv04_dmaobj_priv *priv = (void *)dmaobj; + struct nvkm_gpuobj *gpuobj; + u64 offset = priv->base.start & 0xfffff000; + u64 adjust = priv->base.start & 0x00000fff; + u32 length = priv->base.limit - priv->base.start; + int ret; + + if (!nv_iclass(parent, NV_ENGCTX_CLASS)) { + switch (nv_mclass(parent->parent)) { + case NV03_CHANNEL_DMA: + case NV10_CHANNEL_DMA: + case NV17_CHANNEL_DMA: + case NV40_CHANNEL_DMA: + break; + default: + return -EINVAL; + } + } + + if (priv->clone) { + struct nv04_mmu_priv *mmu = nv04_mmu(dmaobj); + struct nvkm_gpuobj *pgt = mmu->vm->pgt[0].obj[0]; + if (!dmaobj->start) + return nvkm_gpuobj_dup(parent, pgt, pgpuobj); + offset = nv_ro32(pgt, 8 + (offset >> 10)); + offset &= 0xfffff000; + } + + ret = nvkm_gpuobj_new(parent, parent, 16, 16, 0, &gpuobj); + *pgpuobj = gpuobj; + if (ret == 0) { + nv_wo32(*pgpuobj, 0x00, priv->flags0 | (adjust << 20)); + nv_wo32(*pgpuobj, 0x04, length); + nv_wo32(*pgpuobj, 0x08, priv->flags2 | offset); + nv_wo32(*pgpuobj, 0x0c, priv->flags2 | offset); + } + + return ret; +} + +static int +nv04_dmaobj_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_dmaeng *dmaeng = (void *)engine; + struct nv04_mmu_priv *mmu = nv04_mmu(engine); + struct nv04_dmaobj_priv *priv; + int ret; + + ret = nvkm_dmaobj_create(parent, engine, oclass, &data, &size, &priv); + *pobject = nv_object(priv); + if (ret || (ret = -ENOSYS, size)) + return ret; + + if (priv->base.target == NV_MEM_TARGET_VM) { + if (nv_object(mmu)->oclass == &nv04_mmu_oclass) + priv->clone = true; + priv->base.target = NV_MEM_TARGET_PCI; + priv->base.access = NV_MEM_ACCESS_RW; + } + + priv->flags0 = nv_mclass(priv); + switch (priv->base.target) { + case NV_MEM_TARGET_VRAM: + priv->flags0 |= 0x00003000; + break; + case NV_MEM_TARGET_PCI: + priv->flags0 |= 0x00023000; + break; + case NV_MEM_TARGET_PCI_NOSNOOP: + priv->flags0 |= 0x00033000; + break; + default: + return -EINVAL; + } + + switch (priv->base.access) { + case NV_MEM_ACCESS_RO: + priv->flags0 |= 0x00004000; + break; + case NV_MEM_ACCESS_WO: + priv->flags0 |= 0x00008000; + case NV_MEM_ACCESS_RW: + priv->flags2 |= 0x00000002; + break; + default: + return -EINVAL; + } + + return dmaeng->bind(&priv->base, nv_object(priv), (void *)pobject); +} + +static struct nvkm_ofuncs +nv04_dmaobj_ofuncs = { + .ctor = nv04_dmaobj_ctor, + .dtor = _nvkm_dmaobj_dtor, + .init = _nvkm_dmaobj_init, + .fini = _nvkm_dmaobj_fini, +}; + +static struct nvkm_oclass +nv04_dmaeng_sclass[] = { + { NV_DMA_FROM_MEMORY, &nv04_dmaobj_ofuncs }, + { NV_DMA_TO_MEMORY, &nv04_dmaobj_ofuncs }, + { NV_DMA_IN_MEMORY, &nv04_dmaobj_ofuncs }, + {} +}; + +struct nvkm_oclass * +nv04_dmaeng_oclass = &(struct nvkm_dmaeng_impl) { + .base.handle = NV_ENGINE(DMAOBJ, 0x04), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_dmaeng_ctor, + .dtor = _nvkm_dmaeng_dtor, + .init = _nvkm_dmaeng_init, + .fini = _nvkm_dmaeng_fini, + }, + .sclass = nv04_dmaeng_sclass, + .bind = nv04_dmaobj_bind, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/dmaobj/nv50.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/dmaobj/nv50.c new file mode 100644 index 000000000..4d3c828fe --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/dmaobj/nv50.c @@ -0,0 +1,195 @@ +/* + * 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 +#include + +struct nv50_dmaobj_priv { + struct nvkm_dmaobj base; + u32 flags0; + u32 flags5; +}; + +static int +nv50_dmaobj_bind(struct nvkm_dmaobj *dmaobj, struct nvkm_object *parent, + struct nvkm_gpuobj **pgpuobj) +{ + struct nv50_dmaobj_priv *priv = (void *)dmaobj; + int ret; + + if (!nv_iclass(parent, NV_ENGCTX_CLASS)) { + switch (nv_mclass(parent->parent)) { + case NV40_CHANNEL_DMA: + case NV50_CHANNEL_GPFIFO: + case G82_CHANNEL_GPFIFO: + case NV50_DISP_CORE_CHANNEL_DMA: + case G82_DISP_CORE_CHANNEL_DMA: + case GT206_DISP_CORE_CHANNEL_DMA: + case GT200_DISP_CORE_CHANNEL_DMA: + case GT214_DISP_CORE_CHANNEL_DMA: + case NV50_DISP_BASE_CHANNEL_DMA: + case G82_DISP_BASE_CHANNEL_DMA: + case GT200_DISP_BASE_CHANNEL_DMA: + case GT214_DISP_BASE_CHANNEL_DMA: + case NV50_DISP_OVERLAY_CHANNEL_DMA: + case G82_DISP_OVERLAY_CHANNEL_DMA: + case GT200_DISP_OVERLAY_CHANNEL_DMA: + case GT214_DISP_OVERLAY_CHANNEL_DMA: + break; + default: + return -EINVAL; + } + } + + ret = nvkm_gpuobj_new(parent, parent, 24, 32, 0, pgpuobj); + if (ret == 0) { + nv_wo32(*pgpuobj, 0x00, priv->flags0 | nv_mclass(dmaobj)); + nv_wo32(*pgpuobj, 0x04, lower_32_bits(priv->base.limit)); + nv_wo32(*pgpuobj, 0x08, lower_32_bits(priv->base.start)); + nv_wo32(*pgpuobj, 0x0c, upper_32_bits(priv->base.limit) << 24 | + upper_32_bits(priv->base.start)); + nv_wo32(*pgpuobj, 0x10, 0x00000000); + nv_wo32(*pgpuobj, 0x14, priv->flags5); + } + + return ret; +} + +static int +nv50_dmaobj_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_dmaeng *dmaeng = (void *)engine; + union { + struct nv50_dma_v0 v0; + } *args; + struct nv50_dmaobj_priv *priv; + u32 user, part, comp, kind; + int ret; + + ret = nvkm_dmaobj_create(parent, engine, oclass, &data, &size, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + args = data; + + nv_ioctl(parent, "create nv50 dma size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_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 (priv->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; + priv->flags0 = (comp << 29) | (kind << 22) | (user << 20); + priv->flags5 = (part << 16); + + switch (priv->base.target) { + case NV_MEM_TARGET_VM: + priv->flags0 |= 0x00000000; + break; + case NV_MEM_TARGET_VRAM: + priv->flags0 |= 0x00010000; + break; + case NV_MEM_TARGET_PCI: + priv->flags0 |= 0x00020000; + break; + case NV_MEM_TARGET_PCI_NOSNOOP: + priv->flags0 |= 0x00030000; + break; + default: + return -EINVAL; + } + + switch (priv->base.access) { + case NV_MEM_ACCESS_VM: + break; + case NV_MEM_ACCESS_RO: + priv->flags0 |= 0x00040000; + break; + case NV_MEM_ACCESS_WO: + case NV_MEM_ACCESS_RW: + priv->flags0 |= 0x00080000; + break; + default: + return -EINVAL; + } + + return dmaeng->bind(&priv->base, nv_object(priv), (void *)pobject); +} + +static struct nvkm_ofuncs +nv50_dmaobj_ofuncs = { + .ctor = nv50_dmaobj_ctor, + .dtor = _nvkm_dmaobj_dtor, + .init = _nvkm_dmaobj_init, + .fini = _nvkm_dmaobj_fini, +}; + +static struct nvkm_oclass +nv50_dmaeng_sclass[] = { + { NV_DMA_FROM_MEMORY, &nv50_dmaobj_ofuncs }, + { NV_DMA_TO_MEMORY, &nv50_dmaobj_ofuncs }, + { NV_DMA_IN_MEMORY, &nv50_dmaobj_ofuncs }, + {} +}; + +struct nvkm_oclass * +nv50_dmaeng_oclass = &(struct nvkm_dmaeng_impl) { + .base.handle = NV_ENGINE(DMAOBJ, 0x50), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_dmaeng_ctor, + .dtor = _nvkm_dmaeng_dtor, + .init = _nvkm_dmaeng_init, + .fini = _nvkm_dmaeng_fini, + }, + .sclass = nv50_dmaeng_sclass, + .bind = nv50_dmaobj_bind, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/dmaobj/priv.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/dmaobj/priv.h new file mode 100644 index 000000000..44ae8a0ca --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/dmaobj/priv.h @@ -0,0 +1,28 @@ +#ifndef __NVKM_DMAOBJ_PRIV_H__ +#define __NVKM_DMAOBJ_PRIV_H__ +#include + +#define nvkm_dmaobj_create(p,e,c,pa,sa,d) \ + nvkm_dmaobj_create_((p), (e), (c), (pa), (sa), sizeof(**d), (void **)d) + +int nvkm_dmaobj_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void **, u32 *, + int, void **); +#define _nvkm_dmaobj_dtor nvkm_object_destroy +#define _nvkm_dmaobj_init nvkm_object_init +#define _nvkm_dmaobj_fini nvkm_object_fini + +int _nvkm_dmaeng_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); +#define _nvkm_dmaeng_dtor _nvkm_engine_dtor +#define _nvkm_dmaeng_init _nvkm_engine_init +#define _nvkm_dmaeng_fini _nvkm_engine_fini + +struct nvkm_dmaeng_impl { + struct nvkm_oclass base; + struct nvkm_oclass *sclass; + int (*bind)(struct nvkm_dmaobj *, struct nvkm_object *, + struct nvkm_gpuobj **); +}; +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/falcon.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/falcon.c new file mode 100644 index 000000000..30958c19e --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/falcon.c @@ -0,0 +1,277 @@ +/* + * 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 + +void +nvkm_falcon_intr(struct nvkm_subdev *subdev) +{ + struct nvkm_falcon *falcon = (void *)subdev; + u32 dispatch = nv_ro32(falcon, 0x01c); + u32 intr = nv_ro32(falcon, 0x008) & dispatch & ~(dispatch >> 16); + + if (intr & 0x00000010) { + nv_debug(falcon, "ucode halted\n"); + nv_wo32(falcon, 0x004, 0x00000010); + intr &= ~0x00000010; + } + + if (intr) { + nv_error(falcon, "unhandled intr 0x%08x\n", intr); + nv_wo32(falcon, 0x004, intr); + } +} + +u32 +_nvkm_falcon_rd32(struct nvkm_object *object, u64 addr) +{ + struct nvkm_falcon *falcon = (void *)object; + return nv_rd32(falcon, falcon->addr + addr); +} + +void +_nvkm_falcon_wr32(struct nvkm_object *object, u64 addr, u32 data) +{ + struct nvkm_falcon *falcon = (void *)object; + nv_wr32(falcon, falcon->addr + addr, data); +} + +static void * +vmemdup(const void *src, size_t len) +{ + void *p = vmalloc(len); + + if (p) + memcpy(p, src, len); + return p; +} + +int +_nvkm_falcon_init(struct nvkm_object *object) +{ + struct nvkm_device *device = nv_device(object); + struct nvkm_falcon *falcon = (void *)object; + const struct firmware *fw; + char name[32] = "internal"; + int ret, i; + u32 caps; + + /* enable engine, and determine its capabilities */ + ret = nvkm_engine_init(&falcon->base); + if (ret) + return ret; + + if (device->chipset < 0xa3 || + device->chipset == 0xaa || device->chipset == 0xac) { + falcon->version = 0; + falcon->secret = (falcon->addr == 0x087000) ? 1 : 0; + } else { + caps = nv_ro32(falcon, 0x12c); + falcon->version = (caps & 0x0000000f); + falcon->secret = (caps & 0x00000030) >> 4; + } + + caps = nv_ro32(falcon, 0x108); + falcon->code.limit = (caps & 0x000001ff) << 8; + falcon->data.limit = (caps & 0x0003fe00) >> 1; + + nv_debug(falcon, "falcon version: %d\n", falcon->version); + nv_debug(falcon, "secret level: %d\n", falcon->secret); + nv_debug(falcon, "code limit: %d\n", falcon->code.limit); + nv_debug(falcon, "data limit: %d\n", falcon->data.limit); + + /* wait for 'uc halted' to be signalled before continuing */ + if (falcon->secret && falcon->version < 4) { + if (!falcon->version) + nv_wait(falcon, 0x008, 0x00000010, 0x00000010); + else + nv_wait(falcon, 0x180, 0x80000000, 0); + nv_wo32(falcon, 0x004, 0x00000010); + } + + /* disable all interrupts */ + nv_wo32(falcon, 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, nv_device_base(device)); + 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, nv_device_base(device)); + if (ret) { + nv_error(falcon, "unable to load firmware data\n"); + return ret; + } + + 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, nv_device_base(device)); + if (ret) { + nv_error(falcon, "unable to load firmware code\n"); + return ret; + } + + falcon->code.data = vmemdup(fw->data, fw->size); + falcon->code.size = fw->size; + release_firmware(fw); + if (!falcon->code.data) + return -ENOMEM; + } + + nv_debug(falcon, "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_gpuobj_new(object->parent, NULL, falcon->code.size, + 256, 0, &falcon->core); + if (ret) { + nv_error(falcon, "core allocation failed, %d\n", ret); + return ret; + } + + for (i = 0; i < falcon->code.size; i += 4) + nv_wo32(falcon->core, i, falcon->code.data[i / 4]); + } + + /* upload firmware bootloader (or the full code segments) */ + if (falcon->core) { + if (device->card_type < NV_C0) + nv_wo32(falcon, 0x618, 0x04000000); + else + nv_wo32(falcon, 0x618, 0x00000114); + nv_wo32(falcon, 0x11c, 0); + nv_wo32(falcon, 0x110, falcon->core->addr >> 8); + nv_wo32(falcon, 0x114, 0); + nv_wo32(falcon, 0x118, 0x00006610); + } else { + if (falcon->code.size > falcon->code.limit || + falcon->data.size > falcon->data.limit) { + nv_error(falcon, "ucode exceeds falcon limit(s)\n"); + return -EINVAL; + } + + if (falcon->version < 3) { + nv_wo32(falcon, 0xff8, 0x00100000); + for (i = 0; i < falcon->code.size / 4; i++) + nv_wo32(falcon, 0xff4, falcon->code.data[i]); + } else { + nv_wo32(falcon, 0x180, 0x01000000); + for (i = 0; i < falcon->code.size / 4; i++) { + if ((i & 0x3f) == 0) + nv_wo32(falcon, 0x188, i >> 6); + nv_wo32(falcon, 0x184, falcon->code.data[i]); + } + } + } + + /* upload data segment (if necessary), zeroing the remainder */ + if (falcon->version < 3) { + nv_wo32(falcon, 0xff8, 0x00000000); + for (i = 0; !falcon->core && i < falcon->data.size / 4; i++) + nv_wo32(falcon, 0xff4, falcon->data.data[i]); + for (; i < falcon->data.limit; i += 4) + nv_wo32(falcon, 0xff4, 0x00000000); + } else { + nv_wo32(falcon, 0x1c0, 0x01000000); + for (i = 0; !falcon->core && i < falcon->data.size / 4; i++) + nv_wo32(falcon, 0x1c4, falcon->data.data[i]); + for (; i < falcon->data.limit / 4; i++) + nv_wo32(falcon, 0x1c4, 0x00000000); + } + + /* start it running */ + nv_wo32(falcon, 0x10c, 0x00000001); /* BLOCK_ON_FIFO */ + nv_wo32(falcon, 0x104, 0x00000000); /* ENTRY */ + nv_wo32(falcon, 0x100, 0x00000002); /* TRIGGER */ + nv_wo32(falcon, 0x048, 0x00000003); /* FIFO | CHSW */ + return 0; +} + +int +_nvkm_falcon_fini(struct nvkm_object *object, bool suspend) +{ + struct nvkm_falcon *falcon = (void *)object; + + if (!suspend) { + nvkm_gpuobj_ref(NULL, &falcon->core); + if (falcon->external) { + vfree(falcon->data.data); + vfree(falcon->code.data); + falcon->code.data = NULL; + } + } + + nv_mo32(falcon, 0x048, 0x00000003, 0x00000000); + nv_wo32(falcon, 0x014, 0xffffffff); + + return nvkm_engine_fini(&falcon->base, suspend); +} + +int +nvkm_falcon_create_(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, u32 addr, bool enable, + const char *iname, const char *fname, + int length, void **pobject) +{ + struct nvkm_falcon *falcon; + int ret; + + ret = nvkm_engine_create_(parent, engine, oclass, enable, iname, + fname, length, pobject); + falcon = *pobject; + if (ret) + return ret; + + falcon->addr = addr; + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/Kbuild new file mode 100644 index 000000000..42891cb71 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/Kbuild @@ -0,0 +1,12 @@ +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/gk20a.o +nvkm-y += nvkm/engine/fifo/gk208.o +nvkm-y += nvkm/engine/fifo/gm204.o diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/base.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/base.c new file mode 100644 index 000000000..fa223f88d --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/base.c @@ -0,0 +1,282 @@ +/* + * 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 + +static int +nvkm_fifo_event_ctor(struct nvkm_object *object, void *data, u32 size, + struct nvkm_notify *notify) +{ + if (size == 0) { + notify->size = 0; + notify->types = 1; + notify->index = 0; + return 0; + } + return -ENOSYS; +} + +static const struct nvkm_event_func +nvkm_fifo_event_func = { + .ctor = nvkm_fifo_event_ctor, +}; + +int +nvkm_fifo_channel_create_(struct nvkm_object *parent, + struct nvkm_object *engine, + struct nvkm_oclass *oclass, + int bar, u32 addr, u32 size, u32 pushbuf, + u64 engmask, int len, void **ptr) +{ + struct nvkm_device *device = nv_device(engine); + struct nvkm_fifo *priv = (void *)engine; + struct nvkm_fifo_chan *chan; + struct nvkm_dmaeng *dmaeng; + unsigned long flags; + int ret; + + /* create base object class */ + ret = nvkm_namedb_create_(parent, engine, oclass, 0, NULL, + engmask, len, ptr); + chan = *ptr; + if (ret) + return ret; + + /* validate dma object representing push buffer */ + chan->pushdma = (void *)nvkm_handle_ref(parent, pushbuf); + if (!chan->pushdma) + return -ENOENT; + + dmaeng = (void *)chan->pushdma->base.engine; + switch (chan->pushdma->base.oclass->handle) { + case NV_DMA_FROM_MEMORY: + case NV_DMA_IN_MEMORY: + break; + default: + return -EINVAL; + } + + ret = dmaeng->bind(chan->pushdma, parent, &chan->pushgpu); + if (ret) + return ret; + + /* find a free fifo channel */ + spin_lock_irqsave(&priv->lock, flags); + for (chan->chid = priv->min; chan->chid < priv->max; chan->chid++) { + if (!priv->channel[chan->chid]) { + priv->channel[chan->chid] = nv_object(chan); + break; + } + } + spin_unlock_irqrestore(&priv->lock, flags); + + if (chan->chid == priv->max) { + nv_error(priv, "no free channels\n"); + return -ENOSPC; + } + + chan->addr = nv_device_resource_start(device, bar) + + addr + size * chan->chid; + chan->size = size; + nvkm_event_send(&priv->cevent, 1, 0, NULL, 0); + return 0; +} + +void +nvkm_fifo_channel_destroy(struct nvkm_fifo_chan *chan) +{ + struct nvkm_fifo *priv = (void *)nv_object(chan)->engine; + unsigned long flags; + + if (chan->user) + iounmap(chan->user); + + spin_lock_irqsave(&priv->lock, flags); + priv->channel[chan->chid] = NULL; + spin_unlock_irqrestore(&priv->lock, flags); + + nvkm_gpuobj_ref(NULL, &chan->pushgpu); + nvkm_object_ref(NULL, (struct nvkm_object **)&chan->pushdma); + nvkm_namedb_destroy(&chan->namedb); +} + +void +_nvkm_fifo_channel_dtor(struct nvkm_object *object) +{ + struct nvkm_fifo_chan *chan = (void *)object; + nvkm_fifo_channel_destroy(chan); +} + +int +_nvkm_fifo_channel_map(struct nvkm_object *object, u64 *addr, u32 *size) +{ + struct nvkm_fifo_chan *chan = (void *)object; + *addr = chan->addr; + *size = chan->size; + return 0; +} + +u32 +_nvkm_fifo_channel_rd32(struct nvkm_object *object, u64 addr) +{ + struct nvkm_fifo_chan *chan = (void *)object; + if (unlikely(!chan->user)) { + chan->user = ioremap(chan->addr, chan->size); + if (WARN_ON_ONCE(chan->user == NULL)) + return 0; + } + return ioread32_native(chan->user + addr); +} + +void +_nvkm_fifo_channel_wr32(struct nvkm_object *object, u64 addr, u32 data) +{ + struct nvkm_fifo_chan *chan = (void *)object; + if (unlikely(!chan->user)) { + chan->user = ioremap(chan->addr, chan->size); + if (WARN_ON_ONCE(chan->user == NULL)) + return; + } + iowrite32_native(data, chan->user + addr); +} + +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; + + if (nvif_unvers(req->none)) { + notify->size = sizeof(struct nvif_notify_uevent_rep); + notify->types = 1; + notify->index = 0; + } + + return ret; +} + +void +nvkm_fifo_uevent(struct nvkm_fifo *fifo) +{ + struct nvif_notify_uevent_rep rep = { + }; + nvkm_event_send(&fifo->uevent, 1, 0, &rep, sizeof(rep)); +} + +int +_nvkm_fifo_channel_ntfy(struct nvkm_object *object, u32 type, + struct nvkm_event **event) +{ + struct nvkm_fifo *fifo = (void *)object->engine; + switch (type) { + case G82_CHANNEL_DMA_V0_NTFY_UEVENT: + if (nv_mclass(object) >= G82_CHANNEL_DMA) { + *event = &fifo->uevent; + return 0; + } + break; + default: + break; + } + return -EINVAL; +} + +static int +nvkm_fifo_chid(struct nvkm_fifo *priv, struct nvkm_object *object) +{ + int engidx = nv_hclass(priv) & 0xff; + + while (object && object->parent) { + if ( nv_iclass(object->parent, NV_ENGCTX_CLASS) && + (nv_hclass(object->parent) & 0xff) == engidx) + return nvkm_fifo_chan(object)->chid; + object = object->parent; + } + + return -1; +} + +const char * +nvkm_client_name_for_fifo_chid(struct nvkm_fifo *fifo, u32 chid) +{ + struct nvkm_fifo_chan *chan = NULL; + unsigned long flags; + + spin_lock_irqsave(&fifo->lock, flags); + if (chid >= fifo->min && chid <= fifo->max) + chan = (void *)fifo->channel[chid]; + spin_unlock_irqrestore(&fifo->lock, flags); + + return nvkm_client_name(chan); +} + +void +nvkm_fifo_destroy(struct nvkm_fifo *priv) +{ + kfree(priv->channel); + nvkm_event_fini(&priv->uevent); + nvkm_event_fini(&priv->cevent); + nvkm_engine_destroy(&priv->base); +} + +int +nvkm_fifo_create_(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, + int min, int max, int length, void **pobject) +{ + struct nvkm_fifo *priv; + int ret; + + ret = nvkm_engine_create_(parent, engine, oclass, true, "PFIFO", + "fifo", length, pobject); + priv = *pobject; + if (ret) + return ret; + + priv->min = min; + priv->max = max; + priv->channel = kzalloc(sizeof(*priv->channel) * (max + 1), GFP_KERNEL); + if (!priv->channel) + return -ENOMEM; + + ret = nvkm_event_init(&nvkm_fifo_event_func, 1, 1, &priv->cevent); + if (ret) + return ret; + + priv->chid = nvkm_fifo_chid; + spin_lock_init(&priv->lock); + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/g84.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/g84.c new file mode 100644 index 000000000..a04920b3c --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/g84.c @@ -0,0 +1,487 @@ +/* + * 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 "nv04.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +/******************************************************************************* + * FIFO channel objects + ******************************************************************************/ + +static int +g84_fifo_context_attach(struct nvkm_object *parent, struct nvkm_object *object) +{ + struct nvkm_bar *bar = nvkm_bar(parent); + struct nv50_fifo_base *base = (void *)parent->parent; + struct nvkm_gpuobj *ectx = (void *)object; + u64 limit = ectx->addr + ectx->size - 1; + u64 start = ectx->addr; + u32 addr; + + switch (nv_engidx(object->engine)) { + case NVDEV_ENGINE_SW : return 0; + case NVDEV_ENGINE_GR : addr = 0x0020; break; + case NVDEV_ENGINE_VP : + case NVDEV_ENGINE_MSPDEC: addr = 0x0040; break; + case NVDEV_ENGINE_MSPPP : + case NVDEV_ENGINE_MPEG : addr = 0x0060; break; + case NVDEV_ENGINE_BSP : + case NVDEV_ENGINE_MSVLD : addr = 0x0080; break; + case NVDEV_ENGINE_CIPHER: + case NVDEV_ENGINE_SEC : addr = 0x00a0; break; + case NVDEV_ENGINE_CE0 : addr = 0x00c0; break; + default: + return -EINVAL; + } + + nv_engctx(ectx)->addr = nv_gpuobj(base)->addr >> 12; + nv_wo32(base->eng, addr + 0x00, 0x00190000); + nv_wo32(base->eng, addr + 0x04, lower_32_bits(limit)); + nv_wo32(base->eng, addr + 0x08, lower_32_bits(start)); + nv_wo32(base->eng, addr + 0x0c, upper_32_bits(limit) << 24 | + upper_32_bits(start)); + nv_wo32(base->eng, addr + 0x10, 0x00000000); + nv_wo32(base->eng, addr + 0x14, 0x00000000); + bar->flush(bar); + return 0; +} + +static int +g84_fifo_context_detach(struct nvkm_object *parent, bool suspend, + struct nvkm_object *object) +{ + struct nvkm_bar *bar = nvkm_bar(parent); + struct nv50_fifo_priv *priv = (void *)parent->engine; + struct nv50_fifo_base *base = (void *)parent->parent; + struct nv50_fifo_chan *chan = (void *)parent; + u32 addr, save, engn; + bool done; + + switch (nv_engidx(object->engine)) { + case NVDEV_ENGINE_SW : return 0; + case NVDEV_ENGINE_GR : engn = 0; addr = 0x0020; break; + case NVDEV_ENGINE_VP : + case NVDEV_ENGINE_MSPDEC: engn = 3; addr = 0x0040; break; + case NVDEV_ENGINE_MSPPP : + case NVDEV_ENGINE_MPEG : engn = 1; addr = 0x0060; break; + case NVDEV_ENGINE_BSP : + case NVDEV_ENGINE_MSVLD : engn = 5; addr = 0x0080; break; + case NVDEV_ENGINE_CIPHER: + case NVDEV_ENGINE_SEC : engn = 4; addr = 0x00a0; break; + case NVDEV_ENGINE_CE0 : engn = 2; addr = 0x00c0; break; + default: + return -EINVAL; + } + + save = nv_mask(priv, 0x002520, 0x0000003f, 1 << engn); + nv_wr32(priv, 0x0032fc, nv_gpuobj(base)->addr >> 12); + done = nv_wait_ne(priv, 0x0032fc, 0xffffffff, 0xffffffff); + nv_wr32(priv, 0x002520, save); + if (!done) { + nv_error(priv, "channel %d [%s] unload timeout\n", + chan->base.chid, nvkm_client_name(chan)); + if (suspend) + return -EBUSY; + } + + nv_wo32(base->eng, addr + 0x00, 0x00000000); + nv_wo32(base->eng, addr + 0x04, 0x00000000); + nv_wo32(base->eng, addr + 0x08, 0x00000000); + nv_wo32(base->eng, addr + 0x0c, 0x00000000); + nv_wo32(base->eng, addr + 0x10, 0x00000000); + nv_wo32(base->eng, addr + 0x14, 0x00000000); + bar->flush(bar); + return 0; +} + +static int +g84_fifo_object_attach(struct nvkm_object *parent, + struct nvkm_object *object, u32 handle) +{ + struct nv50_fifo_chan *chan = (void *)parent; + u32 context; + + if (nv_iclass(object, NV_GPUOBJ_CLASS)) + context = nv_gpuobj(object)->node->offset >> 4; + else + context = 0x00000004; /* just non-zero */ + + switch (nv_engidx(object->engine)) { + case NVDEV_ENGINE_DMAOBJ: + case NVDEV_ENGINE_SW : context |= 0x00000000; break; + case NVDEV_ENGINE_GR : context |= 0x00100000; break; + case NVDEV_ENGINE_MPEG : + case NVDEV_ENGINE_MSPPP : context |= 0x00200000; break; + case NVDEV_ENGINE_ME : + case NVDEV_ENGINE_CE0 : context |= 0x00300000; break; + case NVDEV_ENGINE_VP : + case NVDEV_ENGINE_MSPDEC: context |= 0x00400000; break; + case NVDEV_ENGINE_CIPHER: + case NVDEV_ENGINE_SEC : + case NVDEV_ENGINE_VIC : context |= 0x00500000; break; + case NVDEV_ENGINE_BSP : + case NVDEV_ENGINE_MSVLD : context |= 0x00600000; break; + default: + return -EINVAL; + } + + return nvkm_ramht_insert(chan->ramht, 0, handle, context); +} + +static int +g84_fifo_chan_ctor_dma(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + union { + struct nv03_channel_dma_v0 v0; + } *args = data; + struct nvkm_bar *bar = nvkm_bar(parent); + struct nv50_fifo_base *base = (void *)parent; + struct nv50_fifo_chan *chan; + int ret; + + nv_ioctl(parent, "create channel dma size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(parent, "create channel dma vers %d pushbuf %08x " + "offset %016llx\n", args->v0.version, + args->v0.pushbuf, args->v0.offset); + } else + return ret; + + ret = nvkm_fifo_channel_create(parent, engine, oclass, 0, 0xc00000, + 0x2000, args->v0.pushbuf, + (1ULL << NVDEV_ENGINE_DMAOBJ) | + (1ULL << NVDEV_ENGINE_SW) | + (1ULL << NVDEV_ENGINE_GR) | + (1ULL << NVDEV_ENGINE_MPEG) | + (1ULL << NVDEV_ENGINE_ME) | + (1ULL << NVDEV_ENGINE_VP) | + (1ULL << NVDEV_ENGINE_CIPHER) | + (1ULL << NVDEV_ENGINE_SEC) | + (1ULL << NVDEV_ENGINE_BSP) | + (1ULL << NVDEV_ENGINE_MSVLD) | + (1ULL << NVDEV_ENGINE_MSPDEC) | + (1ULL << NVDEV_ENGINE_MSPPP) | + (1ULL << NVDEV_ENGINE_CE0) | + (1ULL << NVDEV_ENGINE_VIC), &chan); + *pobject = nv_object(chan); + if (ret) + return ret; + + args->v0.chid = chan->base.chid; + + ret = nvkm_ramht_new(nv_object(chan), nv_object(chan), 0x8000, 16, + &chan->ramht); + if (ret) + return ret; + + nv_parent(chan)->context_attach = g84_fifo_context_attach; + nv_parent(chan)->context_detach = g84_fifo_context_detach; + nv_parent(chan)->object_attach = g84_fifo_object_attach; + nv_parent(chan)->object_detach = nv50_fifo_object_detach; + + nv_wo32(base->ramfc, 0x08, lower_32_bits(args->v0.offset)); + nv_wo32(base->ramfc, 0x0c, upper_32_bits(args->v0.offset)); + nv_wo32(base->ramfc, 0x10, lower_32_bits(args->v0.offset)); + nv_wo32(base->ramfc, 0x14, upper_32_bits(args->v0.offset)); + nv_wo32(base->ramfc, 0x3c, 0x003f6078); + nv_wo32(base->ramfc, 0x44, 0x01003fff); + nv_wo32(base->ramfc, 0x48, chan->base.pushgpu->node->offset >> 4); + nv_wo32(base->ramfc, 0x4c, 0xffffffff); + nv_wo32(base->ramfc, 0x60, 0x7fffffff); + nv_wo32(base->ramfc, 0x78, 0x00000000); + nv_wo32(base->ramfc, 0x7c, 0x30000001); + nv_wo32(base->ramfc, 0x80, ((chan->ramht->bits - 9) << 27) | + (4 << 24) /* SEARCH_FULL */ | + (chan->ramht->gpuobj.node->offset >> 4)); + nv_wo32(base->ramfc, 0x88, base->cache->addr >> 10); + nv_wo32(base->ramfc, 0x98, nv_gpuobj(base)->addr >> 12); + bar->flush(bar); + return 0; +} + +static int +g84_fifo_chan_ctor_ind(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + union { + struct nv50_channel_gpfifo_v0 v0; + } *args = data; + struct nvkm_bar *bar = nvkm_bar(parent); + struct nv50_fifo_base *base = (void *)parent; + struct nv50_fifo_chan *chan; + u64 ioffset, ilength; + int ret; + + nv_ioctl(parent, "create channel gpfifo size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(parent, "create channel gpfifo vers %d pushbuf %08x " + "ioffset %016llx ilength %08x\n", + args->v0.version, args->v0.pushbuf, args->v0.ioffset, + args->v0.ilength); + } else + return ret; + + ret = nvkm_fifo_channel_create(parent, engine, oclass, 0, 0xc00000, + 0x2000, args->v0.pushbuf, + (1ULL << NVDEV_ENGINE_DMAOBJ) | + (1ULL << NVDEV_ENGINE_SW) | + (1ULL << NVDEV_ENGINE_GR) | + (1ULL << NVDEV_ENGINE_MPEG) | + (1ULL << NVDEV_ENGINE_ME) | + (1ULL << NVDEV_ENGINE_VP) | + (1ULL << NVDEV_ENGINE_CIPHER) | + (1ULL << NVDEV_ENGINE_SEC) | + (1ULL << NVDEV_ENGINE_BSP) | + (1ULL << NVDEV_ENGINE_MSVLD) | + (1ULL << NVDEV_ENGINE_MSPDEC) | + (1ULL << NVDEV_ENGINE_MSPPP) | + (1ULL << NVDEV_ENGINE_CE0) | + (1ULL << NVDEV_ENGINE_VIC), &chan); + *pobject = nv_object(chan); + if (ret) + return ret; + + args->v0.chid = chan->base.chid; + + ret = nvkm_ramht_new(nv_object(chan), nv_object(chan), 0x8000, 16, + &chan->ramht); + if (ret) + return ret; + + nv_parent(chan)->context_attach = g84_fifo_context_attach; + nv_parent(chan)->context_detach = g84_fifo_context_detach; + nv_parent(chan)->object_attach = g84_fifo_object_attach; + nv_parent(chan)->object_detach = nv50_fifo_object_detach; + + ioffset = args->v0.ioffset; + ilength = order_base_2(args->v0.ilength / 8); + + nv_wo32(base->ramfc, 0x3c, 0x403f6078); + nv_wo32(base->ramfc, 0x44, 0x01003fff); + nv_wo32(base->ramfc, 0x48, chan->base.pushgpu->node->offset >> 4); + nv_wo32(base->ramfc, 0x50, lower_32_bits(ioffset)); + nv_wo32(base->ramfc, 0x54, upper_32_bits(ioffset) | (ilength << 16)); + nv_wo32(base->ramfc, 0x60, 0x7fffffff); + nv_wo32(base->ramfc, 0x78, 0x00000000); + nv_wo32(base->ramfc, 0x7c, 0x30000001); + nv_wo32(base->ramfc, 0x80, ((chan->ramht->bits - 9) << 27) | + (4 << 24) /* SEARCH_FULL */ | + (chan->ramht->gpuobj.node->offset >> 4)); + nv_wo32(base->ramfc, 0x88, base->cache->addr >> 10); + nv_wo32(base->ramfc, 0x98, nv_gpuobj(base)->addr >> 12); + bar->flush(bar); + return 0; +} + +static int +g84_fifo_chan_init(struct nvkm_object *object) +{ + struct nv50_fifo_priv *priv = (void *)object->engine; + struct nv50_fifo_base *base = (void *)object->parent; + struct nv50_fifo_chan *chan = (void *)object; + struct nvkm_gpuobj *ramfc = base->ramfc; + u32 chid = chan->base.chid; + int ret; + + ret = nvkm_fifo_channel_init(&chan->base); + if (ret) + return ret; + + nv_wr32(priv, 0x002600 + (chid * 4), 0x80000000 | ramfc->addr >> 8); + nv50_fifo_playlist_update(priv); + return 0; +} + +static struct nvkm_ofuncs +g84_fifo_ofuncs_dma = { + .ctor = g84_fifo_chan_ctor_dma, + .dtor = nv50_fifo_chan_dtor, + .init = g84_fifo_chan_init, + .fini = nv50_fifo_chan_fini, + .map = _nvkm_fifo_channel_map, + .rd32 = _nvkm_fifo_channel_rd32, + .wr32 = _nvkm_fifo_channel_wr32, + .ntfy = _nvkm_fifo_channel_ntfy +}; + +static struct nvkm_ofuncs +g84_fifo_ofuncs_ind = { + .ctor = g84_fifo_chan_ctor_ind, + .dtor = nv50_fifo_chan_dtor, + .init = g84_fifo_chan_init, + .fini = nv50_fifo_chan_fini, + .map = _nvkm_fifo_channel_map, + .rd32 = _nvkm_fifo_channel_rd32, + .wr32 = _nvkm_fifo_channel_wr32, + .ntfy = _nvkm_fifo_channel_ntfy +}; + +static struct nvkm_oclass +g84_fifo_sclass[] = { + { G82_CHANNEL_DMA, &g84_fifo_ofuncs_dma }, + { G82_CHANNEL_GPFIFO, &g84_fifo_ofuncs_ind }, + {} +}; + +/******************************************************************************* + * FIFO context - basically just the instmem reserved for the channel + ******************************************************************************/ + +static int +g84_fifo_context_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv50_fifo_base *base; + int ret; + + ret = nvkm_fifo_context_create(parent, engine, oclass, NULL, 0x10000, + 0x1000, NVOBJ_FLAG_HEAP, &base); + *pobject = nv_object(base); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(nv_object(base), nv_object(base), 0x0200, 0, + NVOBJ_FLAG_ZERO_ALLOC, &base->eng); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(nv_object(base), nv_object(base), 0x4000, 0, + 0, &base->pgd); + if (ret) + return ret; + + ret = nvkm_vm_ref(nvkm_client(parent)->vm, &base->vm, base->pgd); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(nv_object(base), nv_object(base), 0x1000, + 0x400, NVOBJ_FLAG_ZERO_ALLOC, &base->cache); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(nv_object(base), nv_object(base), 0x0100, + 0x100, NVOBJ_FLAG_ZERO_ALLOC, &base->ramfc); + if (ret) + return ret; + + return 0; +} + +static struct nvkm_oclass +g84_fifo_cclass = { + .handle = NV_ENGCTX(FIFO, 0x84), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = g84_fifo_context_ctor, + .dtor = nv50_fifo_context_dtor, + .init = _nvkm_fifo_context_init, + .fini = _nvkm_fifo_context_fini, + .rd32 = _nvkm_fifo_context_rd32, + .wr32 = _nvkm_fifo_context_wr32, + }, +}; + +/******************************************************************************* + * PFIFO engine + ******************************************************************************/ + +static void +g84_fifo_uevent_init(struct nvkm_event *event, int type, int index) +{ + struct nvkm_fifo *fifo = container_of(event, typeof(*fifo), uevent); + nv_mask(fifo, 0x002140, 0x40000000, 0x40000000); +} + +static void +g84_fifo_uevent_fini(struct nvkm_event *event, int type, int index) +{ + struct nvkm_fifo *fifo = container_of(event, typeof(*fifo), uevent); + nv_mask(fifo, 0x002140, 0x40000000, 0x00000000); +} + +static const struct nvkm_event_func +g84_fifo_uevent_func = { + .ctor = nvkm_fifo_uevent_ctor, + .init = g84_fifo_uevent_init, + .fini = g84_fifo_uevent_fini, +}; + +static int +g84_fifo_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv50_fifo_priv *priv; + int ret; + + ret = nvkm_fifo_create(parent, engine, oclass, 1, 127, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(nv_object(priv), NULL, 128 * 4, 0x1000, 0, + &priv->playlist[0]); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(nv_object(priv), NULL, 128 * 4, 0x1000, 0, + &priv->playlist[1]); + if (ret) + return ret; + + ret = nvkm_event_init(&g84_fifo_uevent_func, 1, 1, &priv->base.uevent); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00000100; + nv_subdev(priv)->intr = nv04_fifo_intr; + nv_engine(priv)->cclass = &g84_fifo_cclass; + nv_engine(priv)->sclass = g84_fifo_sclass; + priv->base.pause = nv04_fifo_pause; + priv->base.start = nv04_fifo_start; + return 0; +} + +struct nvkm_oclass * +g84_fifo_oclass = &(struct nvkm_oclass) { + .handle = NV_ENGINE(FIFO, 0x84), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = g84_fifo_ctor, + .dtor = nv50_fifo_dtor, + .init = nv50_fifo_init, + .fini = _nvkm_fifo_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gf100.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gf100.c new file mode 100644 index 000000000..b745252f2 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gf100.c @@ -0,0 +1,967 @@ +/* + * 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 + +struct gf100_fifo_priv { + struct nvkm_fifo base; + + struct work_struct fault; + u64 mask; + + struct { + struct nvkm_gpuobj *mem[2]; + int active; + wait_queue_head_t wait; + } runlist; + + struct { + struct nvkm_gpuobj *mem; + struct nvkm_vma bar; + } user; + int spoon_nr; +}; + +struct gf100_fifo_base { + struct nvkm_fifo_base base; + struct nvkm_gpuobj *pgd; + struct nvkm_vm *vm; +}; + +struct gf100_fifo_chan { + struct nvkm_fifo_chan base; + enum { + STOPPED, + RUNNING, + KILLED + } state; +}; + +/******************************************************************************* + * FIFO channel objects + ******************************************************************************/ + +static void +gf100_fifo_runlist_update(struct gf100_fifo_priv *priv) +{ + struct nvkm_bar *bar = nvkm_bar(priv); + struct nvkm_gpuobj *cur; + int i, p; + + mutex_lock(&nv_subdev(priv)->mutex); + cur = priv->runlist.mem[priv->runlist.active]; + priv->runlist.active = !priv->runlist.active; + + for (i = 0, p = 0; i < 128; i++) { + struct gf100_fifo_chan *chan = (void *)priv->base.channel[i]; + if (chan && chan->state == RUNNING) { + nv_wo32(cur, p + 0, i); + nv_wo32(cur, p + 4, 0x00000004); + p += 8; + } + } + bar->flush(bar); + + nv_wr32(priv, 0x002270, cur->addr >> 12); + nv_wr32(priv, 0x002274, 0x01f00000 | (p >> 3)); + + if (wait_event_timeout(priv->runlist.wait, + !(nv_rd32(priv, 0x00227c) & 0x00100000), + msecs_to_jiffies(2000)) == 0) + nv_error(priv, "runlist update timeout\n"); + mutex_unlock(&nv_subdev(priv)->mutex); +} + +static int +gf100_fifo_context_attach(struct nvkm_object *parent, + struct nvkm_object *object) +{ + struct nvkm_bar *bar = nvkm_bar(parent); + struct gf100_fifo_base *base = (void *)parent->parent; + struct nvkm_engctx *ectx = (void *)object; + u32 addr; + int ret; + + switch (nv_engidx(object->engine)) { + case NVDEV_ENGINE_SW : return 0; + case NVDEV_ENGINE_GR : addr = 0x0210; break; + case NVDEV_ENGINE_CE0 : addr = 0x0230; break; + case NVDEV_ENGINE_CE1 : addr = 0x0240; break; + case NVDEV_ENGINE_MSVLD : addr = 0x0270; break; + case NVDEV_ENGINE_MSPDEC: addr = 0x0250; break; + case NVDEV_ENGINE_MSPPP : addr = 0x0260; break; + default: + return -EINVAL; + } + + if (!ectx->vma.node) { + ret = nvkm_gpuobj_map_vm(nv_gpuobj(ectx), base->vm, + NV_MEM_ACCESS_RW, &ectx->vma); + if (ret) + return ret; + + nv_engctx(ectx)->addr = nv_gpuobj(base)->addr >> 12; + } + + nv_wo32(base, addr + 0x00, lower_32_bits(ectx->vma.offset) | 4); + nv_wo32(base, addr + 0x04, upper_32_bits(ectx->vma.offset)); + bar->flush(bar); + return 0; +} + +static int +gf100_fifo_context_detach(struct nvkm_object *parent, bool suspend, + struct nvkm_object *object) +{ + struct nvkm_bar *bar = nvkm_bar(parent); + struct gf100_fifo_priv *priv = (void *)parent->engine; + struct gf100_fifo_base *base = (void *)parent->parent; + struct gf100_fifo_chan *chan = (void *)parent; + u32 addr; + + switch (nv_engidx(object->engine)) { + case NVDEV_ENGINE_SW : return 0; + case NVDEV_ENGINE_GR : addr = 0x0210; break; + case NVDEV_ENGINE_CE0 : addr = 0x0230; break; + case NVDEV_ENGINE_CE1 : addr = 0x0240; break; + case NVDEV_ENGINE_MSVLD : addr = 0x0270; break; + case NVDEV_ENGINE_MSPDEC: addr = 0x0250; break; + case NVDEV_ENGINE_MSPPP : addr = 0x0260; break; + default: + return -EINVAL; + } + + nv_wr32(priv, 0x002634, chan->base.chid); + if (!nv_wait(priv, 0x002634, 0xffffffff, chan->base.chid)) { + nv_error(priv, "channel %d [%s] kick timeout\n", + chan->base.chid, nvkm_client_name(chan)); + if (suspend) + return -EBUSY; + } + + nv_wo32(base, addr + 0x00, 0x00000000); + nv_wo32(base, addr + 0x04, 0x00000000); + bar->flush(bar); + return 0; +} + +static int +gf100_fifo_chan_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + union { + struct nv50_channel_gpfifo_v0 v0; + } *args = data; + struct nvkm_bar *bar = nvkm_bar(parent); + struct gf100_fifo_priv *priv = (void *)engine; + struct gf100_fifo_base *base = (void *)parent; + struct gf100_fifo_chan *chan; + u64 usermem, ioffset, ilength; + int ret, i; + + nv_ioctl(parent, "create channel gpfifo size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(parent, "create channel gpfifo vers %d pushbuf %08x " + "ioffset %016llx ilength %08x\n", + args->v0.version, args->v0.pushbuf, args->v0.ioffset, + args->v0.ilength); + } else + return ret; + + ret = nvkm_fifo_channel_create(parent, engine, oclass, 1, + priv->user.bar.offset, 0x1000, + args->v0.pushbuf, + (1ULL << NVDEV_ENGINE_SW) | + (1ULL << NVDEV_ENGINE_GR) | + (1ULL << NVDEV_ENGINE_CE0) | + (1ULL << NVDEV_ENGINE_CE1) | + (1ULL << NVDEV_ENGINE_MSVLD) | + (1ULL << NVDEV_ENGINE_MSPDEC) | + (1ULL << NVDEV_ENGINE_MSPPP), &chan); + *pobject = nv_object(chan); + if (ret) + return ret; + + args->v0.chid = chan->base.chid; + + nv_parent(chan)->context_attach = gf100_fifo_context_attach; + nv_parent(chan)->context_detach = gf100_fifo_context_detach; + + usermem = chan->base.chid * 0x1000; + ioffset = args->v0.ioffset; + ilength = order_base_2(args->v0.ilength / 8); + + for (i = 0; i < 0x1000; i += 4) + nv_wo32(priv->user.mem, usermem + i, 0x00000000); + + nv_wo32(base, 0x08, lower_32_bits(priv->user.mem->addr + usermem)); + nv_wo32(base, 0x0c, upper_32_bits(priv->user.mem->addr + usermem)); + nv_wo32(base, 0x10, 0x0000face); + nv_wo32(base, 0x30, 0xfffff902); + nv_wo32(base, 0x48, lower_32_bits(ioffset)); + nv_wo32(base, 0x4c, upper_32_bits(ioffset) | (ilength << 16)); + nv_wo32(base, 0x54, 0x00000002); + nv_wo32(base, 0x84, 0x20400000); + nv_wo32(base, 0x94, 0x30000001); + nv_wo32(base, 0x9c, 0x00000100); + nv_wo32(base, 0xa4, 0x1f1f1f1f); + nv_wo32(base, 0xa8, 0x1f1f1f1f); + nv_wo32(base, 0xac, 0x0000001f); + nv_wo32(base, 0xb8, 0xf8000000); + nv_wo32(base, 0xf8, 0x10003080); /* 0x002310 */ + nv_wo32(base, 0xfc, 0x10000010); /* 0x002350 */ + bar->flush(bar); + return 0; +} + +static int +gf100_fifo_chan_init(struct nvkm_object *object) +{ + struct nvkm_gpuobj *base = nv_gpuobj(object->parent); + struct gf100_fifo_priv *priv = (void *)object->engine; + struct gf100_fifo_chan *chan = (void *)object; + u32 chid = chan->base.chid; + int ret; + + ret = nvkm_fifo_channel_init(&chan->base); + if (ret) + return ret; + + nv_wr32(priv, 0x003000 + (chid * 8), 0xc0000000 | base->addr >> 12); + + if (chan->state == STOPPED && (chan->state = RUNNING) == RUNNING) { + nv_wr32(priv, 0x003004 + (chid * 8), 0x001f0001); + gf100_fifo_runlist_update(priv); + } + + return 0; +} + +static void gf100_fifo_intr_engine(struct gf100_fifo_priv *priv); + +static int +gf100_fifo_chan_fini(struct nvkm_object *object, bool suspend) +{ + struct gf100_fifo_priv *priv = (void *)object->engine; + struct gf100_fifo_chan *chan = (void *)object; + u32 chid = chan->base.chid; + + if (chan->state == RUNNING && (chan->state = STOPPED) == STOPPED) { + nv_mask(priv, 0x003004 + (chid * 8), 0x00000001, 0x00000000); + gf100_fifo_runlist_update(priv); + } + + gf100_fifo_intr_engine(priv); + + nv_wr32(priv, 0x003000 + (chid * 8), 0x00000000); + return nvkm_fifo_channel_fini(&chan->base, suspend); +} + +static struct nvkm_ofuncs +gf100_fifo_ofuncs = { + .ctor = gf100_fifo_chan_ctor, + .dtor = _nvkm_fifo_channel_dtor, + .init = gf100_fifo_chan_init, + .fini = gf100_fifo_chan_fini, + .map = _nvkm_fifo_channel_map, + .rd32 = _nvkm_fifo_channel_rd32, + .wr32 = _nvkm_fifo_channel_wr32, + .ntfy = _nvkm_fifo_channel_ntfy +}; + +static struct nvkm_oclass +gf100_fifo_sclass[] = { + { FERMI_CHANNEL_GPFIFO, &gf100_fifo_ofuncs }, + {} +}; + +/******************************************************************************* + * FIFO context - instmem heap and vm setup + ******************************************************************************/ + +static int +gf100_fifo_context_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gf100_fifo_base *base; + int ret; + + ret = nvkm_fifo_context_create(parent, engine, oclass, NULL, 0x1000, + 0x1000, NVOBJ_FLAG_ZERO_ALLOC | + NVOBJ_FLAG_HEAP, &base); + *pobject = nv_object(base); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(nv_object(base), NULL, 0x10000, 0x1000, 0, + &base->pgd); + if (ret) + return ret; + + nv_wo32(base, 0x0200, lower_32_bits(base->pgd->addr)); + nv_wo32(base, 0x0204, upper_32_bits(base->pgd->addr)); + nv_wo32(base, 0x0208, 0xffffffff); + nv_wo32(base, 0x020c, 0x000000ff); + + ret = nvkm_vm_ref(nvkm_client(parent)->vm, &base->vm, base->pgd); + if (ret) + return ret; + + return 0; +} + +static void +gf100_fifo_context_dtor(struct nvkm_object *object) +{ + struct gf100_fifo_base *base = (void *)object; + nvkm_vm_ref(NULL, &base->vm, base->pgd); + nvkm_gpuobj_ref(NULL, &base->pgd); + nvkm_fifo_context_destroy(&base->base); +} + +static struct nvkm_oclass +gf100_fifo_cclass = { + .handle = NV_ENGCTX(FIFO, 0xc0), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_fifo_context_ctor, + .dtor = gf100_fifo_context_dtor, + .init = _nvkm_fifo_context_init, + .fini = _nvkm_fifo_context_fini, + .rd32 = _nvkm_fifo_context_rd32, + .wr32 = _nvkm_fifo_context_wr32, + }, +}; + +/******************************************************************************* + * PFIFO engine + ******************************************************************************/ + +static inline int +gf100_fifo_engidx(struct gf100_fifo_priv *priv, u32 engn) +{ + switch (engn) { + case NVDEV_ENGINE_GR : engn = 0; break; + case NVDEV_ENGINE_MSVLD : engn = 1; break; + case NVDEV_ENGINE_MSPPP : engn = 2; break; + case NVDEV_ENGINE_MSPDEC: engn = 3; break; + case NVDEV_ENGINE_CE0 : engn = 4; break; + case NVDEV_ENGINE_CE1 : engn = 5; break; + default: + return -1; + } + + return engn; +} + +static inline struct nvkm_engine * +gf100_fifo_engine(struct gf100_fifo_priv *priv, u32 engn) +{ + switch (engn) { + case 0: engn = NVDEV_ENGINE_GR; break; + case 1: engn = NVDEV_ENGINE_MSVLD; break; + case 2: engn = NVDEV_ENGINE_MSPPP; break; + case 3: engn = NVDEV_ENGINE_MSPDEC; break; + case 4: engn = NVDEV_ENGINE_CE0; break; + case 5: engn = NVDEV_ENGINE_CE1; break; + default: + return NULL; + } + + return nvkm_engine(priv, engn); +} + +static void +gf100_fifo_recover_work(struct work_struct *work) +{ + struct gf100_fifo_priv *priv = container_of(work, typeof(*priv), fault); + struct nvkm_object *engine; + unsigned long flags; + u32 engn, engm = 0; + u64 mask, todo; + + spin_lock_irqsave(&priv->base.lock, flags); + mask = priv->mask; + priv->mask = 0ULL; + spin_unlock_irqrestore(&priv->base.lock, flags); + + for (todo = mask; engn = __ffs64(todo), todo; todo &= ~(1 << engn)) + engm |= 1 << gf100_fifo_engidx(priv, engn); + nv_mask(priv, 0x002630, engm, engm); + + for (todo = mask; engn = __ffs64(todo), todo; todo &= ~(1 << engn)) { + if ((engine = (void *)nvkm_engine(priv, engn))) { + nv_ofuncs(engine)->fini(engine, false); + WARN_ON(nv_ofuncs(engine)->init(engine)); + } + } + + gf100_fifo_runlist_update(priv); + nv_wr32(priv, 0x00262c, engm); + nv_mask(priv, 0x002630, engm, 0x00000000); +} + +static void +gf100_fifo_recover(struct gf100_fifo_priv *priv, struct nvkm_engine *engine, + struct gf100_fifo_chan *chan) +{ + u32 chid = chan->base.chid; + unsigned long flags; + + nv_error(priv, "%s engine fault on channel %d, recovering...\n", + nv_subdev(engine)->name, chid); + + nv_mask(priv, 0x003004 + (chid * 0x08), 0x00000001, 0x00000000); + chan->state = KILLED; + + spin_lock_irqsave(&priv->base.lock, flags); + priv->mask |= 1ULL << nv_engidx(engine); + spin_unlock_irqrestore(&priv->base.lock, flags); + schedule_work(&priv->fault); +} + +static int +gf100_fifo_swmthd(struct gf100_fifo_priv *priv, u32 chid, u32 mthd, u32 data) +{ + struct gf100_fifo_chan *chan = NULL; + struct nvkm_handle *bind; + unsigned long flags; + int ret = -EINVAL; + + spin_lock_irqsave(&priv->base.lock, flags); + if (likely(chid >= priv->base.min && chid <= priv->base.max)) + chan = (void *)priv->base.channel[chid]; + if (unlikely(!chan)) + goto out; + + bind = nvkm_namedb_get_class(nv_namedb(chan), 0x906e); + if (likely(bind)) { + if (!mthd || !nv_call(bind->object, mthd, data)) + ret = 0; + nvkm_namedb_put(bind); + } + +out: + spin_unlock_irqrestore(&priv->base.lock, flags); + return ret; +} + +static const struct nvkm_enum +gf100_fifo_sched_reason[] = { + { 0x0a, "CTXSW_TIMEOUT" }, + {} +}; + +static void +gf100_fifo_intr_sched_ctxsw(struct gf100_fifo_priv *priv) +{ + struct nvkm_engine *engine; + struct gf100_fifo_chan *chan; + u32 engn; + + for (engn = 0; engn < 6; engn++) { + u32 stat = nv_rd32(priv, 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) { + if (!(chan = (void *)priv->base.channel[chid])) + continue; + if (!(engine = gf100_fifo_engine(priv, engn))) + continue; + gf100_fifo_recover(priv, engine, chan); + } + } +} + +static void +gf100_fifo_intr_sched(struct gf100_fifo_priv *priv) +{ + u32 intr = nv_rd32(priv, 0x00254c); + u32 code = intr & 0x000000ff; + const struct nvkm_enum *en; + char enunk[6] = ""; + + en = nvkm_enum_find(gf100_fifo_sched_reason, code); + if (!en) + snprintf(enunk, sizeof(enunk), "UNK%02x", code); + + nv_error(priv, "SCHED_ERROR [ %s ]\n", en ? en->name : enunk); + + switch (code) { + case 0x0a: + gf100_fifo_intr_sched_ctxsw(priv); + break; + default: + break; + } +} + +static const struct nvkm_enum +gf100_fifo_fault_engine[] = { + { 0x00, "PGRAPH", NULL, NVDEV_ENGINE_GR }, + { 0x03, "PEEPHOLE", NULL, NVDEV_ENGINE_IFB }, + { 0x04, "BAR1", NULL, NVDEV_SUBDEV_BAR }, + { 0x05, "BAR3", NULL, NVDEV_SUBDEV_INSTMEM }, + { 0x07, "PFIFO", NULL, NVDEV_ENGINE_FIFO }, + { 0x10, "PMSVLD", NULL, NVDEV_ENGINE_MSVLD }, + { 0x11, "PMSPPP", NULL, NVDEV_ENGINE_MSPPP }, + { 0x13, "PCOUNTER" }, + { 0x14, "PMSPDEC", NULL, NVDEV_ENGINE_MSPDEC }, + { 0x15, "PCE0", NULL, NVDEV_ENGINE_CE0 }, + { 0x16, "PCE1", NULL, NVDEV_ENGINE_CE1 }, + { 0x17, "PDAEMON" }, + {} +}; + +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, "PDAEMON" }, + { 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_intr_fault(struct gf100_fifo_priv *priv, int unit) +{ + u32 inst = nv_rd32(priv, 0x002800 + (unit * 0x10)); + u32 valo = nv_rd32(priv, 0x002804 + (unit * 0x10)); + u32 vahi = nv_rd32(priv, 0x002808 + (unit * 0x10)); + u32 stat = nv_rd32(priv, 0x00280c + (unit * 0x10)); + u32 gpc = (stat & 0x1f000000) >> 24; + u32 client = (stat & 0x00001f00) >> 8; + u32 write = (stat & 0x00000080); + u32 hub = (stat & 0x00000040); + u32 reason = (stat & 0x0000000f); + struct nvkm_object *engctx = NULL, *object; + struct nvkm_engine *engine = NULL; + const struct nvkm_enum *er, *eu, *ec; + char erunk[6] = ""; + char euunk[6] = ""; + char ecunk[6] = ""; + char gpcid[3] = ""; + + er = nvkm_enum_find(gf100_fifo_fault_reason, reason); + if (!er) + snprintf(erunk, sizeof(erunk), "UNK%02X", reason); + + eu = nvkm_enum_find(gf100_fifo_fault_engine, unit); + if (eu) { + switch (eu->data2) { + case NVDEV_SUBDEV_BAR: + nv_mask(priv, 0x001704, 0x00000000, 0x00000000); + break; + case NVDEV_SUBDEV_INSTMEM: + nv_mask(priv, 0x001714, 0x00000000, 0x00000000); + break; + case NVDEV_ENGINE_IFB: + nv_mask(priv, 0x001718, 0x00000000, 0x00000000); + break; + default: + engine = nvkm_engine(priv, eu->data2); + if (engine) + engctx = nvkm_engctx_get(engine, inst); + break; + } + } else { + snprintf(euunk, sizeof(euunk), "UNK%02x", unit); + } + + if (hub) { + ec = nvkm_enum_find(gf100_fifo_fault_hubclient, client); + } else { + ec = nvkm_enum_find(gf100_fifo_fault_gpcclient, client); + snprintf(gpcid, sizeof(gpcid), "%d", gpc); + } + + if (!ec) + snprintf(ecunk, sizeof(ecunk), "UNK%02x", client); + + nv_error(priv, "%s fault at 0x%010llx [%s] from %s/%s%s%s%s on " + "channel 0x%010llx [%s]\n", write ? "write" : "read", + (u64)vahi << 32 | valo, er ? er->name : erunk, + eu ? eu->name : euunk, hub ? "" : "GPC", gpcid, hub ? "" : "/", + ec ? ec->name : ecunk, (u64)inst << 12, + nvkm_client_name(engctx)); + + object = engctx; + while (object) { + switch (nv_mclass(object)) { + case FERMI_CHANNEL_GPFIFO: + gf100_fifo_recover(priv, engine, (void *)object); + break; + } + object = object->parent; + } + + nvkm_engctx_put(engctx); +} + +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_priv *priv, int unit) +{ + u32 stat = nv_rd32(priv, 0x040108 + (unit * 0x2000)); + u32 addr = nv_rd32(priv, 0x0400c0 + (unit * 0x2000)); + u32 data = nv_rd32(priv, 0x0400c4 + (unit * 0x2000)); + u32 chid = nv_rd32(priv, 0x040120 + (unit * 0x2000)) & 0x7f; + u32 subc = (addr & 0x00070000) >> 16; + u32 mthd = (addr & 0x00003ffc); + u32 show = stat; + + if (stat & 0x00800000) { + if (!gf100_fifo_swmthd(priv, chid, mthd, data)) + show &= ~0x00800000; + } + + if (show) { + nv_error(priv, "PBDMA%d:", unit); + nvkm_bitfield_print(gf100_fifo_pbdma_intr, show); + pr_cont("\n"); + nv_error(priv, + "PBDMA%d: ch %d [%s] subc %d mthd 0x%04x data 0x%08x\n", + unit, chid, + nvkm_client_name_for_fifo_chid(&priv->base, chid), + subc, mthd, data); + } + + nv_wr32(priv, 0x0400c0 + (unit * 0x2000), 0x80600008); + nv_wr32(priv, 0x040108 + (unit * 0x2000), stat); +} + +static void +gf100_fifo_intr_runlist(struct gf100_fifo_priv *priv) +{ + u32 intr = nv_rd32(priv, 0x002a00); + + if (intr & 0x10000000) { + wake_up(&priv->runlist.wait); + nv_wr32(priv, 0x002a00, 0x10000000); + intr &= ~0x10000000; + } + + if (intr) { + nv_error(priv, "RUNLIST 0x%08x\n", intr); + nv_wr32(priv, 0x002a00, intr); + } +} + +static void +gf100_fifo_intr_engine_unit(struct gf100_fifo_priv *priv, int engn) +{ + u32 intr = nv_rd32(priv, 0x0025a8 + (engn * 0x04)); + u32 inte = nv_rd32(priv, 0x002628); + u32 unkn; + + nv_wr32(priv, 0x0025a8 + (engn * 0x04), intr); + + for (unkn = 0; unkn < 8; unkn++) { + u32 ints = (intr >> (unkn * 0x04)) & inte; + if (ints & 0x1) { + nvkm_fifo_uevent(&priv->base); + ints &= ~1; + } + if (ints) { + nv_error(priv, "ENGINE %d %d %01x", engn, unkn, ints); + nv_mask(priv, 0x002628, ints, 0); + } + } +} + +static void +gf100_fifo_intr_engine(struct gf100_fifo_priv *priv) +{ + u32 mask = nv_rd32(priv, 0x0025a4); + while (mask) { + u32 unit = __ffs(mask); + gf100_fifo_intr_engine_unit(priv, unit); + mask &= ~(1 << unit); + } +} + +static void +gf100_fifo_intr(struct nvkm_subdev *subdev) +{ + struct gf100_fifo_priv *priv = (void *)subdev; + u32 mask = nv_rd32(priv, 0x002140); + u32 stat = nv_rd32(priv, 0x002100) & mask; + + if (stat & 0x00000001) { + u32 intr = nv_rd32(priv, 0x00252c); + nv_warn(priv, "INTR 0x00000001: 0x%08x\n", intr); + nv_wr32(priv, 0x002100, 0x00000001); + stat &= ~0x00000001; + } + + if (stat & 0x00000100) { + gf100_fifo_intr_sched(priv); + nv_wr32(priv, 0x002100, 0x00000100); + stat &= ~0x00000100; + } + + if (stat & 0x00010000) { + u32 intr = nv_rd32(priv, 0x00256c); + nv_warn(priv, "INTR 0x00010000: 0x%08x\n", intr); + nv_wr32(priv, 0x002100, 0x00010000); + stat &= ~0x00010000; + } + + if (stat & 0x01000000) { + u32 intr = nv_rd32(priv, 0x00258c); + nv_warn(priv, "INTR 0x01000000: 0x%08x\n", intr); + nv_wr32(priv, 0x002100, 0x01000000); + stat &= ~0x01000000; + } + + if (stat & 0x10000000) { + u32 mask = nv_rd32(priv, 0x00259c); + while (mask) { + u32 unit = __ffs(mask); + gf100_fifo_intr_fault(priv, unit); + nv_wr32(priv, 0x00259c, (1 << unit)); + mask &= ~(1 << unit); + } + stat &= ~0x10000000; + } + + if (stat & 0x20000000) { + u32 mask = nv_rd32(priv, 0x0025a0); + while (mask) { + u32 unit = __ffs(mask); + gf100_fifo_intr_pbdma(priv, unit); + nv_wr32(priv, 0x0025a0, (1 << unit)); + mask &= ~(1 << unit); + } + stat &= ~0x20000000; + } + + if (stat & 0x40000000) { + gf100_fifo_intr_runlist(priv); + stat &= ~0x40000000; + } + + if (stat & 0x80000000) { + gf100_fifo_intr_engine(priv); + stat &= ~0x80000000; + } + + if (stat) { + nv_error(priv, "INTR 0x%08x\n", stat); + nv_mask(priv, 0x002140, stat, 0x00000000); + nv_wr32(priv, 0x002100, stat); + } +} + +static void +gf100_fifo_uevent_init(struct nvkm_event *event, int type, int index) +{ + struct nvkm_fifo *fifo = container_of(event, typeof(*fifo), uevent); + nv_mask(fifo, 0x002140, 0x80000000, 0x80000000); +} + +static void +gf100_fifo_uevent_fini(struct nvkm_event *event, int type, int index) +{ + struct nvkm_fifo *fifo = container_of(event, typeof(*fifo), uevent); + nv_mask(fifo, 0x002140, 0x80000000, 0x00000000); +} + +static const struct nvkm_event_func +gf100_fifo_uevent_func = { + .ctor = nvkm_fifo_uevent_ctor, + .init = gf100_fifo_uevent_init, + .fini = gf100_fifo_uevent_fini, +}; + +static int +gf100_fifo_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gf100_fifo_priv *priv; + int ret; + + ret = nvkm_fifo_create(parent, engine, oclass, 0, 127, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + INIT_WORK(&priv->fault, gf100_fifo_recover_work); + + ret = nvkm_gpuobj_new(nv_object(priv), NULL, 0x1000, 0x1000, 0, + &priv->runlist.mem[0]); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(nv_object(priv), NULL, 0x1000, 0x1000, 0, + &priv->runlist.mem[1]); + if (ret) + return ret; + + init_waitqueue_head(&priv->runlist.wait); + + ret = nvkm_gpuobj_new(nv_object(priv), NULL, 128 * 0x1000, 0x1000, 0, + &priv->user.mem); + if (ret) + return ret; + + ret = nvkm_gpuobj_map(priv->user.mem, NV_MEM_ACCESS_RW, + &priv->user.bar); + if (ret) + return ret; + + ret = nvkm_event_init(&gf100_fifo_uevent_func, 1, 1, &priv->base.uevent); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00000100; + nv_subdev(priv)->intr = gf100_fifo_intr; + nv_engine(priv)->cclass = &gf100_fifo_cclass; + nv_engine(priv)->sclass = gf100_fifo_sclass; + return 0; +} + +static void +gf100_fifo_dtor(struct nvkm_object *object) +{ + struct gf100_fifo_priv *priv = (void *)object; + + nvkm_gpuobj_unmap(&priv->user.bar); + nvkm_gpuobj_ref(NULL, &priv->user.mem); + nvkm_gpuobj_ref(NULL, &priv->runlist.mem[0]); + nvkm_gpuobj_ref(NULL, &priv->runlist.mem[1]); + + nvkm_fifo_destroy(&priv->base); +} + +static int +gf100_fifo_init(struct nvkm_object *object) +{ + struct gf100_fifo_priv *priv = (void *)object; + int ret, i; + + ret = nvkm_fifo_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, 0x000204, 0xffffffff); + nv_wr32(priv, 0x002204, 0xffffffff); + + priv->spoon_nr = hweight32(nv_rd32(priv, 0x002204)); + nv_debug(priv, "%d PBDMA unit(s)\n", priv->spoon_nr); + + /* assign engines to PBDMAs */ + if (priv->spoon_nr >= 3) { + nv_wr32(priv, 0x002208, ~(1 << 0)); /* PGRAPH */ + nv_wr32(priv, 0x00220c, ~(1 << 1)); /* PVP */ + nv_wr32(priv, 0x002210, ~(1 << 1)); /* PMSPP */ + nv_wr32(priv, 0x002214, ~(1 << 1)); /* PMSVLD */ + nv_wr32(priv, 0x002218, ~(1 << 2)); /* PCE0 */ + nv_wr32(priv, 0x00221c, ~(1 << 1)); /* PCE1 */ + } + + /* PBDMA[n] */ + for (i = 0; i < priv->spoon_nr; i++) { + nv_mask(priv, 0x04013c + (i * 0x2000), 0x10000100, 0x00000000); + nv_wr32(priv, 0x040108 + (i * 0x2000), 0xffffffff); /* INTR */ + nv_wr32(priv, 0x04010c + (i * 0x2000), 0xfffffeff); /* INTREN */ + } + + nv_mask(priv, 0x002200, 0x00000001, 0x00000001); + nv_wr32(priv, 0x002254, 0x10000000 | priv->user.bar.offset >> 12); + + nv_wr32(priv, 0x002100, 0xffffffff); + nv_wr32(priv, 0x002140, 0x7fffffff); + nv_wr32(priv, 0x002628, 0x00000001); /* ENGINE_INTR_EN */ + return 0; +} + +struct nvkm_oclass * +gf100_fifo_oclass = &(struct nvkm_oclass) { + .handle = NV_ENGINE(FIFO, 0xc0), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_fifo_ctor, + .dtor = gf100_fifo_dtor, + .init = gf100_fifo_init, + .fini = _nvkm_fifo_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c new file mode 100644 index 000000000..e10f96441 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c @@ -0,0 +1,1139 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define _(a,b) { (a), ((1ULL << (a)) | (b)) } +static const struct { + u64 subdev; + u64 mask; +} fifo_engine[] = { + _(NVDEV_ENGINE_GR , (1ULL << NVDEV_ENGINE_SW) | + (1ULL << NVDEV_ENGINE_CE2)), + _(NVDEV_ENGINE_MSPDEC , 0), + _(NVDEV_ENGINE_MSPPP , 0), + _(NVDEV_ENGINE_MSVLD , 0), + _(NVDEV_ENGINE_CE0 , 0), + _(NVDEV_ENGINE_CE1 , 0), + _(NVDEV_ENGINE_MSENC , 0), +}; +#undef _ +#define FIFO_ENGINE_NR ARRAY_SIZE(fifo_engine) + +struct gk104_fifo_engn { + struct nvkm_gpuobj *runlist[2]; + int cur_runlist; + wait_queue_head_t wait; +}; + +struct gk104_fifo_priv { + struct nvkm_fifo base; + + struct work_struct fault; + u64 mask; + + struct gk104_fifo_engn engine[FIFO_ENGINE_NR]; + struct { + struct nvkm_gpuobj *mem; + struct nvkm_vma bar; + } user; + int spoon_nr; +}; + +struct gk104_fifo_base { + struct nvkm_fifo_base base; + struct nvkm_gpuobj *pgd; + struct nvkm_vm *vm; +}; + +struct gk104_fifo_chan { + struct nvkm_fifo_chan base; + u32 engine; + enum { + STOPPED, + RUNNING, + KILLED + } state; +}; + +/******************************************************************************* + * FIFO channel objects + ******************************************************************************/ + +static void +gk104_fifo_runlist_update(struct gk104_fifo_priv *priv, u32 engine) +{ + struct nvkm_bar *bar = nvkm_bar(priv); + struct gk104_fifo_engn *engn = &priv->engine[engine]; + struct nvkm_gpuobj *cur; + int i, p; + + mutex_lock(&nv_subdev(priv)->mutex); + cur = engn->runlist[engn->cur_runlist]; + engn->cur_runlist = !engn->cur_runlist; + + for (i = 0, p = 0; i < priv->base.max; i++) { + struct gk104_fifo_chan *chan = (void *)priv->base.channel[i]; + if (chan && chan->state == RUNNING && chan->engine == engine) { + nv_wo32(cur, p + 0, i); + nv_wo32(cur, p + 4, 0x00000000); + p += 8; + } + } + bar->flush(bar); + + nv_wr32(priv, 0x002270, cur->addr >> 12); + nv_wr32(priv, 0x002274, (engine << 20) | (p >> 3)); + + if (wait_event_timeout(engn->wait, !(nv_rd32(priv, 0x002284 + + (engine * 0x08)) & 0x00100000), + msecs_to_jiffies(2000)) == 0) + nv_error(priv, "runlist %d update timeout\n", engine); + mutex_unlock(&nv_subdev(priv)->mutex); +} + +static int +gk104_fifo_context_attach(struct nvkm_object *parent, + struct nvkm_object *object) +{ + struct nvkm_bar *bar = nvkm_bar(parent); + struct gk104_fifo_base *base = (void *)parent->parent; + struct nvkm_engctx *ectx = (void *)object; + u32 addr; + int ret; + + switch (nv_engidx(object->engine)) { + case NVDEV_ENGINE_SW : + return 0; + case NVDEV_ENGINE_CE0: + case NVDEV_ENGINE_CE1: + case NVDEV_ENGINE_CE2: + nv_engctx(ectx)->addr = nv_gpuobj(base)->addr >> 12; + return 0; + case NVDEV_ENGINE_GR : addr = 0x0210; break; + case NVDEV_ENGINE_MSVLD : addr = 0x0270; break; + case NVDEV_ENGINE_MSPDEC: addr = 0x0250; break; + case NVDEV_ENGINE_MSPPP : addr = 0x0260; break; + default: + return -EINVAL; + } + + if (!ectx->vma.node) { + ret = nvkm_gpuobj_map_vm(nv_gpuobj(ectx), base->vm, + NV_MEM_ACCESS_RW, &ectx->vma); + if (ret) + return ret; + + nv_engctx(ectx)->addr = nv_gpuobj(base)->addr >> 12; + } + + nv_wo32(base, addr + 0x00, lower_32_bits(ectx->vma.offset) | 4); + nv_wo32(base, addr + 0x04, upper_32_bits(ectx->vma.offset)); + bar->flush(bar); + return 0; +} + +static int +gk104_fifo_context_detach(struct nvkm_object *parent, bool suspend, + struct nvkm_object *object) +{ + struct nvkm_bar *bar = nvkm_bar(parent); + struct gk104_fifo_priv *priv = (void *)parent->engine; + struct gk104_fifo_base *base = (void *)parent->parent; + struct gk104_fifo_chan *chan = (void *)parent; + u32 addr; + + switch (nv_engidx(object->engine)) { + case NVDEV_ENGINE_SW : return 0; + case NVDEV_ENGINE_CE0 : + case NVDEV_ENGINE_CE1 : + case NVDEV_ENGINE_CE2 : addr = 0x0000; break; + case NVDEV_ENGINE_GR : addr = 0x0210; break; + case NVDEV_ENGINE_MSVLD : addr = 0x0270; break; + case NVDEV_ENGINE_MSPDEC: addr = 0x0250; break; + case NVDEV_ENGINE_MSPPP : addr = 0x0260; break; + default: + return -EINVAL; + } + + nv_wr32(priv, 0x002634, chan->base.chid); + if (!nv_wait(priv, 0x002634, 0xffffffff, chan->base.chid)) { + nv_error(priv, "channel %d [%s] kick timeout\n", + chan->base.chid, nvkm_client_name(chan)); + if (suspend) + return -EBUSY; + } + + if (addr) { + nv_wo32(base, addr + 0x00, 0x00000000); + nv_wo32(base, addr + 0x04, 0x00000000); + bar->flush(bar); + } + + return 0; +} + +static int +gk104_fifo_chan_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + union { + struct kepler_channel_gpfifo_a_v0 v0; + } *args = data; + struct nvkm_bar *bar = nvkm_bar(parent); + struct gk104_fifo_priv *priv = (void *)engine; + struct gk104_fifo_base *base = (void *)parent; + struct gk104_fifo_chan *chan; + u64 usermem, ioffset, ilength; + int ret, i; + + nv_ioctl(parent, "create channel gpfifo size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(parent, "create channel gpfifo vers %d pushbuf %08x " + "ioffset %016llx ilength %08x engine %08x\n", + args->v0.version, args->v0.pushbuf, args->v0.ioffset, + args->v0.ilength, args->v0.engine); + } else + return ret; + + for (i = 0; i < FIFO_ENGINE_NR; i++) { + if (args->v0.engine & (1 << i)) { + if (nvkm_engine(parent, fifo_engine[i].subdev)) { + args->v0.engine = (1 << i); + break; + } + } + } + + if (i == FIFO_ENGINE_NR) { + nv_error(priv, "unsupported engines 0x%08x\n", args->v0.engine); + return -ENODEV; + } + + ret = nvkm_fifo_channel_create(parent, engine, oclass, 1, + priv->user.bar.offset, 0x200, + args->v0.pushbuf, + fifo_engine[i].mask, &chan); + *pobject = nv_object(chan); + if (ret) + return ret; + + args->v0.chid = chan->base.chid; + + nv_parent(chan)->context_attach = gk104_fifo_context_attach; + nv_parent(chan)->context_detach = gk104_fifo_context_detach; + chan->engine = i; + + usermem = chan->base.chid * 0x200; + ioffset = args->v0.ioffset; + ilength = order_base_2(args->v0.ilength / 8); + + for (i = 0; i < 0x200; i += 4) + nv_wo32(priv->user.mem, usermem + i, 0x00000000); + + nv_wo32(base, 0x08, lower_32_bits(priv->user.mem->addr + usermem)); + nv_wo32(base, 0x0c, upper_32_bits(priv->user.mem->addr + usermem)); + nv_wo32(base, 0x10, 0x0000face); + nv_wo32(base, 0x30, 0xfffff902); + nv_wo32(base, 0x48, lower_32_bits(ioffset)); + nv_wo32(base, 0x4c, upper_32_bits(ioffset) | (ilength << 16)); + nv_wo32(base, 0x84, 0x20400000); + nv_wo32(base, 0x94, 0x30000001); + nv_wo32(base, 0x9c, 0x00000100); + nv_wo32(base, 0xac, 0x0000001f); + nv_wo32(base, 0xe8, chan->base.chid); + nv_wo32(base, 0xb8, 0xf8000000); + nv_wo32(base, 0xf8, 0x10003080); /* 0x002310 */ + nv_wo32(base, 0xfc, 0x10000010); /* 0x002350 */ + bar->flush(bar); + return 0; +} + +static int +gk104_fifo_chan_init(struct nvkm_object *object) +{ + struct nvkm_gpuobj *base = nv_gpuobj(object->parent); + struct gk104_fifo_priv *priv = (void *)object->engine; + struct gk104_fifo_chan *chan = (void *)object; + u32 chid = chan->base.chid; + int ret; + + ret = nvkm_fifo_channel_init(&chan->base); + if (ret) + return ret; + + nv_mask(priv, 0x800004 + (chid * 8), 0x000f0000, chan->engine << 16); + nv_wr32(priv, 0x800000 + (chid * 8), 0x80000000 | base->addr >> 12); + + if (chan->state == STOPPED && (chan->state = RUNNING) == RUNNING) { + nv_mask(priv, 0x800004 + (chid * 8), 0x00000400, 0x00000400); + gk104_fifo_runlist_update(priv, chan->engine); + nv_mask(priv, 0x800004 + (chid * 8), 0x00000400, 0x00000400); + } + + return 0; +} + +static int +gk104_fifo_chan_fini(struct nvkm_object *object, bool suspend) +{ + struct gk104_fifo_priv *priv = (void *)object->engine; + struct gk104_fifo_chan *chan = (void *)object; + u32 chid = chan->base.chid; + + if (chan->state == RUNNING && (chan->state = STOPPED) == STOPPED) { + nv_mask(priv, 0x800004 + (chid * 8), 0x00000800, 0x00000800); + gk104_fifo_runlist_update(priv, chan->engine); + } + + nv_wr32(priv, 0x800000 + (chid * 8), 0x00000000); + return nvkm_fifo_channel_fini(&chan->base, suspend); +} + +struct nvkm_ofuncs +gk104_fifo_chan_ofuncs = { + .ctor = gk104_fifo_chan_ctor, + .dtor = _nvkm_fifo_channel_dtor, + .init = gk104_fifo_chan_init, + .fini = gk104_fifo_chan_fini, + .map = _nvkm_fifo_channel_map, + .rd32 = _nvkm_fifo_channel_rd32, + .wr32 = _nvkm_fifo_channel_wr32, + .ntfy = _nvkm_fifo_channel_ntfy +}; + +static struct nvkm_oclass +gk104_fifo_sclass[] = { + { KEPLER_CHANNEL_GPFIFO_A, &gk104_fifo_chan_ofuncs }, + {} +}; + +/******************************************************************************* + * FIFO context - instmem heap and vm setup + ******************************************************************************/ + +static int +gk104_fifo_context_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gk104_fifo_base *base; + int ret; + + ret = nvkm_fifo_context_create(parent, engine, oclass, NULL, 0x1000, + 0x1000, NVOBJ_FLAG_ZERO_ALLOC, &base); + *pobject = nv_object(base); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(nv_object(base), NULL, 0x10000, 0x1000, 0, + &base->pgd); + if (ret) + return ret; + + nv_wo32(base, 0x0200, lower_32_bits(base->pgd->addr)); + nv_wo32(base, 0x0204, upper_32_bits(base->pgd->addr)); + nv_wo32(base, 0x0208, 0xffffffff); + nv_wo32(base, 0x020c, 0x000000ff); + + ret = nvkm_vm_ref(nvkm_client(parent)->vm, &base->vm, base->pgd); + if (ret) + return ret; + + return 0; +} + +static void +gk104_fifo_context_dtor(struct nvkm_object *object) +{ + struct gk104_fifo_base *base = (void *)object; + nvkm_vm_ref(NULL, &base->vm, base->pgd); + nvkm_gpuobj_ref(NULL, &base->pgd); + nvkm_fifo_context_destroy(&base->base); +} + +static struct nvkm_oclass +gk104_fifo_cclass = { + .handle = NV_ENGCTX(FIFO, 0xe0), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gk104_fifo_context_ctor, + .dtor = gk104_fifo_context_dtor, + .init = _nvkm_fifo_context_init, + .fini = _nvkm_fifo_context_fini, + .rd32 = _nvkm_fifo_context_rd32, + .wr32 = _nvkm_fifo_context_wr32, + }, +}; + +/******************************************************************************* + * PFIFO engine + ******************************************************************************/ + +static inline int +gk104_fifo_engidx(struct gk104_fifo_priv *priv, u32 engn) +{ + switch (engn) { + case NVDEV_ENGINE_GR : + case NVDEV_ENGINE_CE2 : engn = 0; break; + case NVDEV_ENGINE_MSVLD : engn = 1; break; + case NVDEV_ENGINE_MSPPP : engn = 2; break; + case NVDEV_ENGINE_MSPDEC: engn = 3; break; + case NVDEV_ENGINE_CE0 : engn = 4; break; + case NVDEV_ENGINE_CE1 : engn = 5; break; + case NVDEV_ENGINE_MSENC : engn = 6; break; + default: + return -1; + } + + return engn; +} + +static inline struct nvkm_engine * +gk104_fifo_engine(struct gk104_fifo_priv *priv, u32 engn) +{ + if (engn >= ARRAY_SIZE(fifo_engine)) + return NULL; + return nvkm_engine(priv, fifo_engine[engn].subdev); +} + +static void +gk104_fifo_recover_work(struct work_struct *work) +{ + struct gk104_fifo_priv *priv = container_of(work, typeof(*priv), fault); + struct nvkm_object *engine; + unsigned long flags; + u32 engn, engm = 0; + u64 mask, todo; + + spin_lock_irqsave(&priv->base.lock, flags); + mask = priv->mask; + priv->mask = 0ULL; + spin_unlock_irqrestore(&priv->base.lock, flags); + + for (todo = mask; engn = __ffs64(todo), todo; todo &= ~(1 << engn)) + engm |= 1 << gk104_fifo_engidx(priv, engn); + nv_mask(priv, 0x002630, engm, engm); + + for (todo = mask; engn = __ffs64(todo), todo; todo &= ~(1 << engn)) { + if ((engine = (void *)nvkm_engine(priv, engn))) { + nv_ofuncs(engine)->fini(engine, false); + WARN_ON(nv_ofuncs(engine)->init(engine)); + } + gk104_fifo_runlist_update(priv, gk104_fifo_engidx(priv, engn)); + } + + nv_wr32(priv, 0x00262c, engm); + nv_mask(priv, 0x002630, engm, 0x00000000); +} + +static void +gk104_fifo_recover(struct gk104_fifo_priv *priv, struct nvkm_engine *engine, + struct gk104_fifo_chan *chan) +{ + u32 chid = chan->base.chid; + unsigned long flags; + + nv_error(priv, "%s engine fault on channel %d, recovering...\n", + nv_subdev(engine)->name, chid); + + nv_mask(priv, 0x800004 + (chid * 0x08), 0x00000800, 0x00000800); + chan->state = KILLED; + + spin_lock_irqsave(&priv->base.lock, flags); + priv->mask |= 1ULL << nv_engidx(engine); + spin_unlock_irqrestore(&priv->base.lock, flags); + schedule_work(&priv->fault); +} + +static int +gk104_fifo_swmthd(struct gk104_fifo_priv *priv, u32 chid, u32 mthd, u32 data) +{ + struct gk104_fifo_chan *chan = NULL; + struct nvkm_handle *bind; + unsigned long flags; + int ret = -EINVAL; + + spin_lock_irqsave(&priv->base.lock, flags); + if (likely(chid >= priv->base.min && chid <= priv->base.max)) + chan = (void *)priv->base.channel[chid]; + if (unlikely(!chan)) + goto out; + + bind = nvkm_namedb_get_class(nv_namedb(chan), 0x906e); + if (likely(bind)) { + if (!mthd || !nv_call(bind->object, mthd, data)) + ret = 0; + nvkm_namedb_put(bind); + } + +out: + spin_unlock_irqrestore(&priv->base.lock, flags); + return ret; +} + +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" }, + {} +}; + +static void +gk104_fifo_intr_bind(struct gk104_fifo_priv *priv) +{ + u32 intr = nv_rd32(priv, 0x00252c); + u32 code = intr & 0x000000ff; + const struct nvkm_enum *en; + char enunk[6] = ""; + + en = nvkm_enum_find(gk104_fifo_bind_reason, code); + if (!en) + snprintf(enunk, sizeof(enunk), "UNK%02x", code); + + nv_error(priv, "BIND_ERROR [ %s ]\n", en ? en->name : enunk); +} + +static const struct nvkm_enum +gk104_fifo_sched_reason[] = { + { 0x0a, "CTXSW_TIMEOUT" }, + {} +}; + +static void +gk104_fifo_intr_sched_ctxsw(struct gk104_fifo_priv *priv) +{ + struct nvkm_engine *engine; + struct gk104_fifo_chan *chan; + u32 engn; + + for (engn = 0; engn < ARRAY_SIZE(fifo_engine); engn++) { + u32 stat = nv_rd32(priv, 0x002640 + (engn * 0x04)); + u32 busy = (stat & 0x80000000); + u32 next = (stat & 0x07ff0000) >> 16; + u32 chsw = (stat & 0x00008000); + u32 save = (stat & 0x00004000); + u32 load = (stat & 0x00002000); + u32 prev = (stat & 0x000007ff); + u32 chid = load ? next : prev; + (void)save; + + if (busy && chsw) { + if (!(chan = (void *)priv->base.channel[chid])) + continue; + if (!(engine = gk104_fifo_engine(priv, engn))) + continue; + gk104_fifo_recover(priv, engine, chan); + } + } +} + +static void +gk104_fifo_intr_sched(struct gk104_fifo_priv *priv) +{ + u32 intr = nv_rd32(priv, 0x00254c); + u32 code = intr & 0x000000ff; + const struct nvkm_enum *en; + char enunk[6] = ""; + + en = nvkm_enum_find(gk104_fifo_sched_reason, code); + if (!en) + snprintf(enunk, sizeof(enunk), "UNK%02x", code); + + nv_error(priv, "SCHED_ERROR [ %s ]\n", en ? en->name : enunk); + + switch (code) { + case 0x0a: + gk104_fifo_intr_sched_ctxsw(priv); + break; + default: + break; + } +} + +static void +gk104_fifo_intr_chsw(struct gk104_fifo_priv *priv) +{ + u32 stat = nv_rd32(priv, 0x00256c); + nv_error(priv, "CHSW_ERROR 0x%08x\n", stat); + nv_wr32(priv, 0x00256c, stat); +} + +static void +gk104_fifo_intr_dropped_fault(struct gk104_fifo_priv *priv) +{ + u32 stat = nv_rd32(priv, 0x00259c); + nv_error(priv, "DROPPED_MMU_FAULT 0x%08x\n", stat); +} + +static const struct nvkm_enum +gk104_fifo_fault_engine[] = { + { 0x00, "GR", NULL, NVDEV_ENGINE_GR }, + { 0x03, "IFB", NULL, NVDEV_ENGINE_IFB }, + { 0x04, "BAR1", NULL, NVDEV_SUBDEV_BAR }, + { 0x05, "BAR3", NULL, NVDEV_SUBDEV_INSTMEM }, + { 0x07, "PBDMA0", NULL, NVDEV_ENGINE_FIFO }, + { 0x08, "PBDMA1", NULL, NVDEV_ENGINE_FIFO }, + { 0x09, "PBDMA2", NULL, NVDEV_ENGINE_FIFO }, + { 0x10, "MSVLD", NULL, NVDEV_ENGINE_MSVLD }, + { 0x11, "MSPPP", NULL, NVDEV_ENGINE_MSPPP }, + { 0x13, "PERF" }, + { 0x14, "MSPDEC", NULL, NVDEV_ENGINE_MSPDEC }, + { 0x15, "CE0", NULL, NVDEV_ENGINE_CE0 }, + { 0x16, "CE1", NULL, NVDEV_ENGINE_CE1 }, + { 0x17, "PMU" }, + { 0x19, "MSENC", NULL, NVDEV_ENGINE_MSENC }, + { 0x1b, "CE2", NULL, NVDEV_ENGINE_CE2 }, + {} +}; + +static 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" }, + {} +}; + +static 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" }, + {} +}; + +static 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 void +gk104_fifo_intr_fault(struct gk104_fifo_priv *priv, int unit) +{ + u32 inst = nv_rd32(priv, 0x002800 + (unit * 0x10)); + u32 valo = nv_rd32(priv, 0x002804 + (unit * 0x10)); + u32 vahi = nv_rd32(priv, 0x002808 + (unit * 0x10)); + u32 stat = nv_rd32(priv, 0x00280c + (unit * 0x10)); + u32 gpc = (stat & 0x1f000000) >> 24; + u32 client = (stat & 0x00001f00) >> 8; + u32 write = (stat & 0x00000080); + u32 hub = (stat & 0x00000040); + u32 reason = (stat & 0x0000000f); + struct nvkm_object *engctx = NULL, *object; + struct nvkm_engine *engine = NULL; + const struct nvkm_enum *er, *eu, *ec; + char erunk[6] = ""; + char euunk[6] = ""; + char ecunk[6] = ""; + char gpcid[3] = ""; + + er = nvkm_enum_find(gk104_fifo_fault_reason, reason); + if (!er) + snprintf(erunk, sizeof(erunk), "UNK%02X", reason); + + eu = nvkm_enum_find(gk104_fifo_fault_engine, unit); + if (eu) { + switch (eu->data2) { + case NVDEV_SUBDEV_BAR: + nv_mask(priv, 0x001704, 0x00000000, 0x00000000); + break; + case NVDEV_SUBDEV_INSTMEM: + nv_mask(priv, 0x001714, 0x00000000, 0x00000000); + break; + case NVDEV_ENGINE_IFB: + nv_mask(priv, 0x001718, 0x00000000, 0x00000000); + break; + default: + engine = nvkm_engine(priv, eu->data2); + if (engine) + engctx = nvkm_engctx_get(engine, inst); + break; + } + } else { + snprintf(euunk, sizeof(euunk), "UNK%02x", unit); + } + + if (hub) { + ec = nvkm_enum_find(gk104_fifo_fault_hubclient, client); + } else { + ec = nvkm_enum_find(gk104_fifo_fault_gpcclient, client); + snprintf(gpcid, sizeof(gpcid), "%d", gpc); + } + + if (!ec) + snprintf(ecunk, sizeof(ecunk), "UNK%02x", client); + + nv_error(priv, "%s fault at 0x%010llx [%s] from %s/%s%s%s%s on " + "channel 0x%010llx [%s]\n", write ? "write" : "read", + (u64)vahi << 32 | valo, er ? er->name : erunk, + eu ? eu->name : euunk, hub ? "" : "GPC", gpcid, hub ? "" : "/", + ec ? ec->name : ecunk, (u64)inst << 12, + nvkm_client_name(engctx)); + + object = engctx; + while (object) { + switch (nv_mclass(object)) { + case KEPLER_CHANNEL_GPFIFO_A: + case MAXWELL_CHANNEL_GPFIFO_A: + gk104_fifo_recover(priv, engine, (void *)object); + break; + } + object = object->parent; + } + + nvkm_engctx_put(engctx); +} + +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" }, + {} +}; + +static void +gk104_fifo_intr_pbdma_0(struct gk104_fifo_priv *priv, int unit) +{ + u32 mask = nv_rd32(priv, 0x04010c + (unit * 0x2000)); + u32 stat = nv_rd32(priv, 0x040108 + (unit * 0x2000)) & mask; + u32 addr = nv_rd32(priv, 0x0400c0 + (unit * 0x2000)); + u32 data = nv_rd32(priv, 0x0400c4 + (unit * 0x2000)); + u32 chid = nv_rd32(priv, 0x040120 + (unit * 0x2000)) & 0xfff; + u32 subc = (addr & 0x00070000) >> 16; + u32 mthd = (addr & 0x00003ffc); + u32 show = stat; + + if (stat & 0x00800000) { + if (!gk104_fifo_swmthd(priv, chid, mthd, data)) + show &= ~0x00800000; + nv_wr32(priv, 0x0400c0 + (unit * 0x2000), 0x80600008); + } + + if (show) { + nv_error(priv, "PBDMA%d:", unit); + nvkm_bitfield_print(gk104_fifo_pbdma_intr_0, show); + pr_cont("\n"); + nv_error(priv, + "PBDMA%d: ch %d [%s] subc %d mthd 0x%04x data 0x%08x\n", + unit, chid, + nvkm_client_name_for_fifo_chid(&priv->base, chid), + subc, mthd, data); + } + + nv_wr32(priv, 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" }, + {} +}; + +static void +gk104_fifo_intr_pbdma_1(struct gk104_fifo_priv *priv, int unit) +{ + u32 mask = nv_rd32(priv, 0x04014c + (unit * 0x2000)); + u32 stat = nv_rd32(priv, 0x040148 + (unit * 0x2000)) & mask; + u32 chid = nv_rd32(priv, 0x040120 + (unit * 0x2000)) & 0xfff; + + if (stat) { + nv_error(priv, "PBDMA%d:", unit); + nvkm_bitfield_print(gk104_fifo_pbdma_intr_1, stat); + pr_cont("\n"); + nv_error(priv, "PBDMA%d: ch %d %08x %08x\n", unit, chid, + nv_rd32(priv, 0x040150 + (unit * 0x2000)), + nv_rd32(priv, 0x040154 + (unit * 0x2000))); + } + + nv_wr32(priv, 0x040148 + (unit * 0x2000), stat); +} + +static void +gk104_fifo_intr_runlist(struct gk104_fifo_priv *priv) +{ + u32 mask = nv_rd32(priv, 0x002a00); + while (mask) { + u32 engn = __ffs(mask); + wake_up(&priv->engine[engn].wait); + nv_wr32(priv, 0x002a00, 1 << engn); + mask &= ~(1 << engn); + } +} + +static void +gk104_fifo_intr_engine(struct gk104_fifo_priv *priv) +{ + nvkm_fifo_uevent(&priv->base); +} + +static void +gk104_fifo_intr(struct nvkm_subdev *subdev) +{ + struct gk104_fifo_priv *priv = (void *)subdev; + u32 mask = nv_rd32(priv, 0x002140); + u32 stat = nv_rd32(priv, 0x002100) & mask; + + if (stat & 0x00000001) { + gk104_fifo_intr_bind(priv); + nv_wr32(priv, 0x002100, 0x00000001); + stat &= ~0x00000001; + } + + if (stat & 0x00000010) { + nv_error(priv, "PIO_ERROR\n"); + nv_wr32(priv, 0x002100, 0x00000010); + stat &= ~0x00000010; + } + + if (stat & 0x00000100) { + gk104_fifo_intr_sched(priv); + nv_wr32(priv, 0x002100, 0x00000100); + stat &= ~0x00000100; + } + + if (stat & 0x00010000) { + gk104_fifo_intr_chsw(priv); + nv_wr32(priv, 0x002100, 0x00010000); + stat &= ~0x00010000; + } + + if (stat & 0x00800000) { + nv_error(priv, "FB_FLUSH_TIMEOUT\n"); + nv_wr32(priv, 0x002100, 0x00800000); + stat &= ~0x00800000; + } + + if (stat & 0x01000000) { + nv_error(priv, "LB_ERROR\n"); + nv_wr32(priv, 0x002100, 0x01000000); + stat &= ~0x01000000; + } + + if (stat & 0x08000000) { + gk104_fifo_intr_dropped_fault(priv); + nv_wr32(priv, 0x002100, 0x08000000); + stat &= ~0x08000000; + } + + if (stat & 0x10000000) { + u32 mask = nv_rd32(priv, 0x00259c); + while (mask) { + u32 unit = __ffs(mask); + gk104_fifo_intr_fault(priv, unit); + nv_wr32(priv, 0x00259c, (1 << unit)); + mask &= ~(1 << unit); + } + stat &= ~0x10000000; + } + + if (stat & 0x20000000) { + u32 mask = nv_rd32(priv, 0x0025a0); + while (mask) { + u32 unit = __ffs(mask); + gk104_fifo_intr_pbdma_0(priv, unit); + gk104_fifo_intr_pbdma_1(priv, unit); + nv_wr32(priv, 0x0025a0, (1 << unit)); + mask &= ~(1 << unit); + } + stat &= ~0x20000000; + } + + if (stat & 0x40000000) { + gk104_fifo_intr_runlist(priv); + stat &= ~0x40000000; + } + + if (stat & 0x80000000) { + nv_wr32(priv, 0x002100, 0x80000000); + gk104_fifo_intr_engine(priv); + stat &= ~0x80000000; + } + + if (stat) { + nv_error(priv, "INTR 0x%08x\n", stat); + nv_mask(priv, 0x002140, stat, 0x00000000); + nv_wr32(priv, 0x002100, stat); + } +} + +static void +gk104_fifo_uevent_init(struct nvkm_event *event, int type, int index) +{ + struct nvkm_fifo *fifo = container_of(event, typeof(*fifo), uevent); + nv_mask(fifo, 0x002140, 0x80000000, 0x80000000); +} + +static void +gk104_fifo_uevent_fini(struct nvkm_event *event, int type, int index) +{ + struct nvkm_fifo *fifo = container_of(event, typeof(*fifo), uevent); + nv_mask(fifo, 0x002140, 0x80000000, 0x00000000); +} + +static const struct nvkm_event_func +gk104_fifo_uevent_func = { + .ctor = nvkm_fifo_uevent_ctor, + .init = gk104_fifo_uevent_init, + .fini = gk104_fifo_uevent_fini, +}; + +int +gk104_fifo_fini(struct nvkm_object *object, bool suspend) +{ + struct gk104_fifo_priv *priv = (void *)object; + int ret; + + ret = nvkm_fifo_fini(&priv->base, suspend); + if (ret) + return ret; + + /* allow mmu fault interrupts, even when we're not using fifo */ + nv_mask(priv, 0x002140, 0x10000000, 0x10000000); + return 0; +} + +int +gk104_fifo_init(struct nvkm_object *object) +{ + struct gk104_fifo_priv *priv = (void *)object; + int ret, i; + + ret = nvkm_fifo_init(&priv->base); + if (ret) + return ret; + + /* enable all available PBDMA units */ + nv_wr32(priv, 0x000204, 0xffffffff); + priv->spoon_nr = hweight32(nv_rd32(priv, 0x000204)); + nv_debug(priv, "%d PBDMA unit(s)\n", priv->spoon_nr); + + /* PBDMA[n] */ + for (i = 0; i < priv->spoon_nr; i++) { + nv_mask(priv, 0x04013c + (i * 0x2000), 0x10000100, 0x00000000); + nv_wr32(priv, 0x040108 + (i * 0x2000), 0xffffffff); /* INTR */ + nv_wr32(priv, 0x04010c + (i * 0x2000), 0xfffffeff); /* INTREN */ + } + + /* PBDMA[n].HCE */ + for (i = 0; i < priv->spoon_nr; i++) { + nv_wr32(priv, 0x040148 + (i * 0x2000), 0xffffffff); /* INTR */ + nv_wr32(priv, 0x04014c + (i * 0x2000), 0xffffffff); /* INTREN */ + } + + nv_wr32(priv, 0x002254, 0x10000000 | priv->user.bar.offset >> 12); + + nv_wr32(priv, 0x002100, 0xffffffff); + nv_wr32(priv, 0x002140, 0x7fffffff); + return 0; +} + +void +gk104_fifo_dtor(struct nvkm_object *object) +{ + struct gk104_fifo_priv *priv = (void *)object; + int i; + + nvkm_gpuobj_unmap(&priv->user.bar); + nvkm_gpuobj_ref(NULL, &priv->user.mem); + + for (i = 0; i < FIFO_ENGINE_NR; i++) { + nvkm_gpuobj_ref(NULL, &priv->engine[i].runlist[1]); + nvkm_gpuobj_ref(NULL, &priv->engine[i].runlist[0]); + } + + nvkm_fifo_destroy(&priv->base); +} + +int +gk104_fifo_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gk104_fifo_impl *impl = (void *)oclass; + struct gk104_fifo_priv *priv; + int ret, i; + + ret = nvkm_fifo_create(parent, engine, oclass, 0, + impl->channels - 1, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + INIT_WORK(&priv->fault, gk104_fifo_recover_work); + + for (i = 0; i < FIFO_ENGINE_NR; i++) { + ret = nvkm_gpuobj_new(nv_object(priv), NULL, 0x8000, 0x1000, + 0, &priv->engine[i].runlist[0]); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(nv_object(priv), NULL, 0x8000, 0x1000, + 0, &priv->engine[i].runlist[1]); + if (ret) + return ret; + + init_waitqueue_head(&priv->engine[i].wait); + } + + ret = nvkm_gpuobj_new(nv_object(priv), NULL, impl->channels * 0x200, + 0x1000, NVOBJ_FLAG_ZERO_ALLOC, &priv->user.mem); + if (ret) + return ret; + + ret = nvkm_gpuobj_map(priv->user.mem, NV_MEM_ACCESS_RW, + &priv->user.bar); + if (ret) + return ret; + + ret = nvkm_event_init(&gk104_fifo_uevent_func, 1, 1, &priv->base.uevent); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00000100; + nv_subdev(priv)->intr = gk104_fifo_intr; + nv_engine(priv)->cclass = &gk104_fifo_cclass; + nv_engine(priv)->sclass = gk104_fifo_sclass; + return 0; +} + +struct nvkm_oclass * +gk104_fifo_oclass = &(struct gk104_fifo_impl) { + .base.handle = NV_ENGINE(FIFO, 0xe0), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gk104_fifo_ctor, + .dtor = gk104_fifo_dtor, + .init = gk104_fifo_init, + .fini = gk104_fifo_fini, + }, + .channels = 4096, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.h new file mode 100644 index 000000000..318d30d6e --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.h @@ -0,0 +1,18 @@ +#ifndef __NVKM_FIFO_NVE0_H__ +#define __NVKM_FIFO_NVE0_H__ +#include + +int gk104_fifo_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); +void gk104_fifo_dtor(struct nvkm_object *); +int gk104_fifo_init(struct nvkm_object *); +int gk104_fifo_fini(struct nvkm_object *, bool); + +struct gk104_fifo_impl { + struct nvkm_oclass base; + u32 channels; +}; + +extern struct nvkm_ofuncs gk104_fifo_chan_ofuncs; +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk208.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk208.c new file mode 100644 index 000000000..927092217 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk208.c @@ -0,0 +1,36 @@ +/* + * 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" + +struct nvkm_oclass * +gk208_fifo_oclass = &(struct gk104_fifo_impl) { + .base.handle = NV_ENGINE(FIFO, 0x08), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gk104_fifo_ctor, + .dtor = gk104_fifo_dtor, + .init = gk104_fifo_init, + .fini = _nvkm_fifo_fini, + }, + .channels = 1024, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk20a.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk20a.c new file mode 100644 index 000000000..b30dc87a1 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk20a.c @@ -0,0 +1,34 @@ +/* + * 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" + +struct nvkm_oclass * +gk20a_fifo_oclass = &(struct gk104_fifo_impl) { + .base.handle = NV_ENGINE(FIFO, 0xea), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gk104_fifo_ctor, + .dtor = gk104_fifo_dtor, + .init = gk104_fifo_init, + .fini = gk104_fifo_fini, + }, + .channels = 128, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gm204.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gm204.c new file mode 100644 index 000000000..749d525dd --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gm204.c @@ -0,0 +1,57 @@ +/* + * 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 + +static struct nvkm_oclass +gm204_fifo_sclass[] = { + { MAXWELL_CHANNEL_GPFIFO_A, &gk104_fifo_chan_ofuncs }, + {} +}; + +static int +gm204_fifo_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + int ret = gk104_fifo_ctor(parent, engine, oclass, data, size, pobject); + if (ret == 0) { + struct gk104_fifo_priv *priv = (void *)*pobject; + nv_engine(priv)->sclass = gm204_fifo_sclass; + } + return ret; +} + +struct nvkm_oclass * +gm204_fifo_oclass = &(struct gk104_fifo_impl) { + .base.handle = NV_ENGINE(FIFO, 0x24), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gm204_fifo_ctor, + .dtor = gk104_fifo_dtor, + .init = gk104_fifo_init, + .fini = _nvkm_fifo_fini, + }, + .channels = 4096, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv04.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv04.c new file mode 100644 index 000000000..043e42960 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv04.c @@ -0,0 +1,635 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#include +#include + +static struct ramfc_desc +nv04_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 }, + {} +}; + +/******************************************************************************* + * FIFO channel objects + ******************************************************************************/ + +int +nv04_fifo_object_attach(struct nvkm_object *parent, + struct nvkm_object *object, u32 handle) +{ + struct nv04_fifo_priv *priv = (void *)parent->engine; + struct nv04_fifo_chan *chan = (void *)parent; + u32 context, chid = chan->base.chid; + int ret; + + if (nv_iclass(object, NV_GPUOBJ_CLASS)) + context = nv_gpuobj(object)->addr >> 4; + else + context = 0x00000004; /* just non-zero */ + + switch (nv_engidx(object->engine)) { + case NVDEV_ENGINE_DMAOBJ: + case NVDEV_ENGINE_SW: + context |= 0x00000000; + break; + case NVDEV_ENGINE_GR: + context |= 0x00010000; + break; + case NVDEV_ENGINE_MPEG: + context |= 0x00020000; + break; + default: + return -EINVAL; + } + + context |= 0x80000000; /* valid */ + context |= chid << 24; + + mutex_lock(&nv_subdev(priv)->mutex); + ret = nvkm_ramht_insert(priv->ramht, chid, handle, context); + mutex_unlock(&nv_subdev(priv)->mutex); + return ret; +} + +void +nv04_fifo_object_detach(struct nvkm_object *parent, int cookie) +{ + struct nv04_fifo_priv *priv = (void *)parent->engine; + mutex_lock(&nv_subdev(priv)->mutex); + nvkm_ramht_remove(priv->ramht, cookie); + mutex_unlock(&nv_subdev(priv)->mutex); +} + +int +nv04_fifo_context_attach(struct nvkm_object *parent, + struct nvkm_object *object) +{ + nv_engctx(object)->addr = nvkm_fifo_chan(parent)->chid; + return 0; +} + +static int +nv04_fifo_chan_ctor(struct nvkm_object *parent, + struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + union { + struct nv03_channel_dma_v0 v0; + } *args = data; + struct nv04_fifo_priv *priv = (void *)engine; + struct nv04_fifo_chan *chan; + int ret; + + nv_ioctl(parent, "create channel dma size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(parent, "create channel dma vers %d pushbuf %08x " + "offset %016llx\n", args->v0.version, + args->v0.pushbuf, args->v0.offset); + } else + return ret; + + ret = nvkm_fifo_channel_create(parent, engine, oclass, 0, 0x800000, + 0x10000, args->v0.pushbuf, + (1ULL << NVDEV_ENGINE_DMAOBJ) | + (1ULL << NVDEV_ENGINE_SW) | + (1ULL << NVDEV_ENGINE_GR), &chan); + *pobject = nv_object(chan); + if (ret) + return ret; + + args->v0.chid = chan->base.chid; + + nv_parent(chan)->object_attach = nv04_fifo_object_attach; + nv_parent(chan)->object_detach = nv04_fifo_object_detach; + nv_parent(chan)->context_attach = nv04_fifo_context_attach; + chan->ramfc = chan->base.chid * 32; + + nv_wo32(priv->ramfc, chan->ramfc + 0x00, args->v0.offset); + nv_wo32(priv->ramfc, chan->ramfc + 0x04, args->v0.offset); + nv_wo32(priv->ramfc, chan->ramfc + 0x08, chan->base.pushgpu->addr >> 4); + nv_wo32(priv->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); + return 0; +} + +void +nv04_fifo_chan_dtor(struct nvkm_object *object) +{ + struct nv04_fifo_priv *priv = (void *)object->engine; + struct nv04_fifo_chan *chan = (void *)object; + struct ramfc_desc *c = priv->ramfc_desc; + + do { + nv_wo32(priv->ramfc, chan->ramfc + c->ctxp, 0x00000000); + } while ((++c)->bits); + + nvkm_fifo_channel_destroy(&chan->base); +} + +int +nv04_fifo_chan_init(struct nvkm_object *object) +{ + struct nv04_fifo_priv *priv = (void *)object->engine; + struct nv04_fifo_chan *chan = (void *)object; + u32 mask = 1 << chan->base.chid; + unsigned long flags; + int ret; + + ret = nvkm_fifo_channel_init(&chan->base); + if (ret) + return ret; + + spin_lock_irqsave(&priv->base.lock, flags); + nv_mask(priv, NV04_PFIFO_MODE, mask, mask); + spin_unlock_irqrestore(&priv->base.lock, flags); + return 0; +} + +int +nv04_fifo_chan_fini(struct nvkm_object *object, bool suspend) +{ + struct nv04_fifo_priv *priv = (void *)object->engine; + struct nv04_fifo_chan *chan = (void *)object; + struct nvkm_gpuobj *fctx = priv->ramfc; + struct ramfc_desc *c; + unsigned long flags; + u32 data = chan->ramfc; + u32 chid; + + /* prevent fifo context switches */ + spin_lock_irqsave(&priv->base.lock, flags); + nv_wr32(priv, NV03_PFIFO_CACHES, 0); + + /* if this channel is active, replace it with a null context */ + chid = nv_rd32(priv, NV03_PFIFO_CACHE1_PUSH1) & priv->base.max; + if (chid == chan->base.chid) { + nv_mask(priv, NV04_PFIFO_CACHE1_DMA_PUSH, 0x00000001, 0); + nv_wr32(priv, NV03_PFIFO_CACHE1_PUSH0, 0); + nv_mask(priv, NV04_PFIFO_CACHE1_PULL0, 0x00000001, 0); + + c = priv->ramfc_desc; + do { + u32 rm = ((1ULL << c->bits) - 1) << c->regs; + u32 cm = ((1ULL << c->bits) - 1) << c->ctxs; + u32 rv = (nv_rd32(priv, c->regp) & rm) >> c->regs; + u32 cv = (nv_ro32(fctx, c->ctxp + data) & ~cm); + nv_wo32(fctx, c->ctxp + data, cv | (rv << c->ctxs)); + } while ((++c)->bits); + + c = priv->ramfc_desc; + do { + nv_wr32(priv, c->regp, 0x00000000); + } while ((++c)->bits); + + nv_wr32(priv, NV03_PFIFO_CACHE1_GET, 0); + nv_wr32(priv, NV03_PFIFO_CACHE1_PUT, 0); + nv_wr32(priv, NV03_PFIFO_CACHE1_PUSH1, priv->base.max); + nv_wr32(priv, NV03_PFIFO_CACHE1_PUSH0, 1); + nv_wr32(priv, NV04_PFIFO_CACHE1_PULL0, 1); + } + + /* restore normal operation, after disabling dma mode */ + nv_mask(priv, NV04_PFIFO_MODE, 1 << chan->base.chid, 0); + nv_wr32(priv, NV03_PFIFO_CACHES, 1); + spin_unlock_irqrestore(&priv->base.lock, flags); + + return nvkm_fifo_channel_fini(&chan->base, suspend); +} + +static struct nvkm_ofuncs +nv04_fifo_ofuncs = { + .ctor = nv04_fifo_chan_ctor, + .dtor = nv04_fifo_chan_dtor, + .init = nv04_fifo_chan_init, + .fini = nv04_fifo_chan_fini, + .map = _nvkm_fifo_channel_map, + .rd32 = _nvkm_fifo_channel_rd32, + .wr32 = _nvkm_fifo_channel_wr32, + .ntfy = _nvkm_fifo_channel_ntfy +}; + +static struct nvkm_oclass +nv04_fifo_sclass[] = { + { NV03_CHANNEL_DMA, &nv04_fifo_ofuncs }, + {} +}; + +/******************************************************************************* + * FIFO context - basically just the instmem reserved for the channel + ******************************************************************************/ + +int +nv04_fifo_context_ctor(struct nvkm_object *parent, + struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv04_fifo_base *base; + int ret; + + ret = nvkm_fifo_context_create(parent, engine, oclass, NULL, 0x1000, + 0x1000, NVOBJ_FLAG_HEAP, &base); + *pobject = nv_object(base); + if (ret) + return ret; + + return 0; +} + +static struct nvkm_oclass +nv04_fifo_cclass = { + .handle = NV_ENGCTX(FIFO, 0x04), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_fifo_context_ctor, + .dtor = _nvkm_fifo_context_dtor, + .init = _nvkm_fifo_context_init, + .fini = _nvkm_fifo_context_fini, + .rd32 = _nvkm_fifo_context_rd32, + .wr32 = _nvkm_fifo_context_wr32, + }, +}; + +/******************************************************************************* + * PFIFO engine + ******************************************************************************/ + +void +nv04_fifo_pause(struct nvkm_fifo *pfifo, unsigned long *pflags) +__acquires(priv->base.lock) +{ + struct nv04_fifo_priv *priv = (void *)pfifo; + unsigned long flags; + + spin_lock_irqsave(&priv->base.lock, flags); + *pflags = flags; + + nv_wr32(priv, NV03_PFIFO_CACHES, 0x00000000); + nv_mask(priv, 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. + */ + if (!nv_wait(priv, NV04_PFIFO_CACHE1_PULL0, + NV04_PFIFO_CACHE1_PULL0_HASH_BUSY, 0x00000000)) + nv_warn(priv, "timeout idling puller\n"); + + if (nv_rd32(priv, NV04_PFIFO_CACHE1_PULL0) & + NV04_PFIFO_CACHE1_PULL0_HASH_FAILED) + nv_wr32(priv, NV03_PFIFO_INTR_0, NV_PFIFO_INTR_CACHE_ERROR); + + nv_wr32(priv, NV04_PFIFO_CACHE1_HASH, 0x00000000); +} + +void +nv04_fifo_start(struct nvkm_fifo *pfifo, unsigned long *pflags) +__releases(priv->base.lock) +{ + struct nv04_fifo_priv *priv = (void *)pfifo; + unsigned long flags = *pflags; + + nv_mask(priv, NV04_PFIFO_CACHE1_PULL0, 0x00000001, 0x00000001); + nv_wr32(priv, NV03_PFIFO_CACHES, 0x00000001); + + spin_unlock_irqrestore(&priv->base.lock, flags); +} + +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 nv04_fifo_priv *priv, u32 chid, u32 addr, u32 data) +{ + struct nv04_fifo_chan *chan = NULL; + struct nvkm_handle *bind; + const int subc = (addr >> 13) & 0x7; + const int mthd = addr & 0x1ffc; + bool handled = false; + unsigned long flags; + u32 engine; + + spin_lock_irqsave(&priv->base.lock, flags); + if (likely(chid >= priv->base.min && chid <= priv->base.max)) + chan = (void *)priv->base.channel[chid]; + if (unlikely(!chan)) + goto out; + + switch (mthd) { + case 0x0000: + bind = nvkm_namedb_get(nv_namedb(chan), data); + if (unlikely(!bind)) + break; + + if (nv_engidx(bind->object->engine) == NVDEV_ENGINE_SW) { + engine = 0x0000000f << (subc * 4); + chan->subc[subc] = data; + handled = true; + + nv_mask(priv, NV04_PFIFO_CACHE1_ENGINE, engine, 0); + } + + nvkm_namedb_put(bind); + break; + default: + engine = nv_rd32(priv, NV04_PFIFO_CACHE1_ENGINE); + if (unlikely(((engine >> (subc * 4)) & 0xf) != 0)) + break; + + bind = nvkm_namedb_get(nv_namedb(chan), chan->subc[subc]); + if (likely(bind)) { + if (!nv_call(bind->object, mthd, data)) + handled = true; + nvkm_namedb_put(bind); + } + break; + } + +out: + spin_unlock_irqrestore(&priv->base.lock, flags); + return handled; +} + +static void +nv04_fifo_cache_error(struct nvkm_device *device, + struct nv04_fifo_priv *priv, u32 chid, u32 get) +{ + 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 = nv_rd32(priv, NV04_PFIFO_CACHE1_METHOD(ptr)); + data = nv_rd32(priv, NV04_PFIFO_CACHE1_DATA(ptr)); + } else { + mthd = nv_rd32(priv, NV40_PFIFO_CACHE1_METHOD(ptr)); + data = nv_rd32(priv, NV40_PFIFO_CACHE1_DATA(ptr)); + } + + if (!nv04_fifo_swmthd(priv, chid, mthd, data)) { + const char *client_name = + nvkm_client_name_for_fifo_chid(&priv->base, chid); + nv_error(priv, + "CACHE_ERROR - ch %d [%s] subc %d mthd 0x%04x data 0x%08x\n", + chid, client_name, (mthd >> 13) & 7, mthd & 0x1ffc, + data); + } + + nv_wr32(priv, NV04_PFIFO_CACHE1_DMA_PUSH, 0); + nv_wr32(priv, NV03_PFIFO_INTR_0, NV_PFIFO_INTR_CACHE_ERROR); + + nv_wr32(priv, NV03_PFIFO_CACHE1_PUSH0, + nv_rd32(priv, NV03_PFIFO_CACHE1_PUSH0) & ~1); + nv_wr32(priv, NV03_PFIFO_CACHE1_GET, get + 4); + nv_wr32(priv, NV03_PFIFO_CACHE1_PUSH0, + nv_rd32(priv, NV03_PFIFO_CACHE1_PUSH0) | 1); + nv_wr32(priv, NV04_PFIFO_CACHE1_HASH, 0); + + nv_wr32(priv, NV04_PFIFO_CACHE1_DMA_PUSH, + nv_rd32(priv, NV04_PFIFO_CACHE1_DMA_PUSH) | 1); + nv_wr32(priv, NV04_PFIFO_CACHE1_PULL0, 1); +} + +static void +nv04_fifo_dma_pusher(struct nvkm_device *device, + struct nv04_fifo_priv *priv, u32 chid) +{ + const char *client_name; + u32 dma_get = nv_rd32(priv, 0x003244); + u32 dma_put = nv_rd32(priv, 0x003240); + u32 push = nv_rd32(priv, 0x003220); + u32 state = nv_rd32(priv, 0x003228); + + client_name = nvkm_client_name_for_fifo_chid(&priv->base, chid); + + if (device->card_type == NV_50) { + u32 ho_get = nv_rd32(priv, 0x003328); + u32 ho_put = nv_rd32(priv, 0x003320); + u32 ib_get = nv_rd32(priv, 0x003334); + u32 ib_put = nv_rd32(priv, 0x003330); + + nv_error(priv, + "DMA_PUSHER - ch %d [%s] get 0x%02x%08x put 0x%02x%08x ib_get 0x%08x ib_put 0x%08x state 0x%08x (err: %s) push 0x%08x\n", + chid, client_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 */ + nv_wr32(priv, 0x003364, 0x00000000); + if (dma_get != dma_put || ho_get != ho_put) { + nv_wr32(priv, 0x003244, dma_put); + nv_wr32(priv, 0x003328, ho_put); + } else + if (ib_get != ib_put) + nv_wr32(priv, 0x003334, ib_put); + } else { + nv_error(priv, + "DMA_PUSHER - ch %d [%s] get 0x%08x put 0x%08x state 0x%08x (err: %s) push 0x%08x\n", + chid, client_name, dma_get, dma_put, state, + nv_dma_state_err(state), push); + + if (dma_get != dma_put) + nv_wr32(priv, 0x003244, dma_put); + } + + nv_wr32(priv, 0x003228, 0x00000000); + nv_wr32(priv, 0x003220, 0x00000001); + nv_wr32(priv, 0x002100, NV_PFIFO_INTR_DMA_PUSHER); +} + +void +nv04_fifo_intr(struct nvkm_subdev *subdev) +{ + struct nvkm_device *device = nv_device(subdev); + struct nv04_fifo_priv *priv = (void *)subdev; + u32 mask = nv_rd32(priv, NV03_PFIFO_INTR_EN_0); + u32 stat = nv_rd32(priv, NV03_PFIFO_INTR_0) & mask; + u32 reassign, chid, get, sem; + + reassign = nv_rd32(priv, NV03_PFIFO_CACHES) & 1; + nv_wr32(priv, NV03_PFIFO_CACHES, 0); + + chid = nv_rd32(priv, NV03_PFIFO_CACHE1_PUSH1) & priv->base.max; + get = nv_rd32(priv, NV03_PFIFO_CACHE1_GET); + + if (stat & NV_PFIFO_INTR_CACHE_ERROR) { + nv04_fifo_cache_error(device, priv, chid, get); + stat &= ~NV_PFIFO_INTR_CACHE_ERROR; + } + + if (stat & NV_PFIFO_INTR_DMA_PUSHER) { + nv04_fifo_dma_pusher(device, priv, chid); + stat &= ~NV_PFIFO_INTR_DMA_PUSHER; + } + + if (stat & NV_PFIFO_INTR_SEMAPHORE) { + stat &= ~NV_PFIFO_INTR_SEMAPHORE; + nv_wr32(priv, NV03_PFIFO_INTR_0, NV_PFIFO_INTR_SEMAPHORE); + + sem = nv_rd32(priv, NV10_PFIFO_CACHE1_SEMAPHORE); + nv_wr32(priv, NV10_PFIFO_CACHE1_SEMAPHORE, sem | 0x1); + + nv_wr32(priv, NV03_PFIFO_CACHE1_GET, get + 4); + nv_wr32(priv, NV04_PFIFO_CACHE1_PULL0, 1); + } + + if (device->card_type == NV_50) { + if (stat & 0x00000010) { + stat &= ~0x00000010; + nv_wr32(priv, 0x002100, 0x00000010); + } + + if (stat & 0x40000000) { + nv_wr32(priv, 0x002100, 0x40000000); + nvkm_fifo_uevent(&priv->base); + stat &= ~0x40000000; + } + } + + if (stat) { + nv_warn(priv, "unknown intr 0x%08x\n", stat); + nv_mask(priv, NV03_PFIFO_INTR_EN_0, stat, 0x00000000); + nv_wr32(priv, NV03_PFIFO_INTR_0, stat); + } + + nv_wr32(priv, NV03_PFIFO_CACHES, reassign); +} + +static int +nv04_fifo_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv04_instmem_priv *imem = nv04_instmem(parent); + struct nv04_fifo_priv *priv; + int ret; + + ret = nvkm_fifo_create(parent, engine, oclass, 0, 15, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nvkm_ramht_ref(imem->ramht, &priv->ramht); + nvkm_gpuobj_ref(imem->ramro, &priv->ramro); + nvkm_gpuobj_ref(imem->ramfc, &priv->ramfc); + + nv_subdev(priv)->unit = 0x00000100; + nv_subdev(priv)->intr = nv04_fifo_intr; + nv_engine(priv)->cclass = &nv04_fifo_cclass; + nv_engine(priv)->sclass = nv04_fifo_sclass; + priv->base.pause = nv04_fifo_pause; + priv->base.start = nv04_fifo_start; + priv->ramfc_desc = nv04_ramfc; + return 0; +} + +void +nv04_fifo_dtor(struct nvkm_object *object) +{ + struct nv04_fifo_priv *priv = (void *)object; + nvkm_gpuobj_ref(NULL, &priv->ramfc); + nvkm_gpuobj_ref(NULL, &priv->ramro); + nvkm_ramht_ref(NULL, &priv->ramht); + nvkm_fifo_destroy(&priv->base); +} + +int +nv04_fifo_init(struct nvkm_object *object) +{ + struct nv04_fifo_priv *priv = (void *)object; + int ret; + + ret = nvkm_fifo_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, NV04_PFIFO_DELAY_0, 0x000000ff); + nv_wr32(priv, NV04_PFIFO_DMA_TIMESLICE, 0x0101ffff); + + nv_wr32(priv, NV03_PFIFO_RAMHT, (0x03 << 24) /* search 128 */ | + ((priv->ramht->bits - 9) << 16) | + (priv->ramht->gpuobj.addr >> 8)); + nv_wr32(priv, NV03_PFIFO_RAMRO, priv->ramro->addr >> 8); + nv_wr32(priv, NV03_PFIFO_RAMFC, priv->ramfc->addr >> 8); + + nv_wr32(priv, NV03_PFIFO_CACHE1_PUSH1, priv->base.max); + + nv_wr32(priv, NV03_PFIFO_INTR_0, 0xffffffff); + nv_wr32(priv, NV03_PFIFO_INTR_EN_0, 0xffffffff); + + nv_wr32(priv, NV03_PFIFO_CACHE1_PUSH0, 1); + nv_wr32(priv, NV04_PFIFO_CACHE1_PULL0, 1); + nv_wr32(priv, NV03_PFIFO_CACHES, 1); + return 0; +} + +struct nvkm_oclass * +nv04_fifo_oclass = &(struct nvkm_oclass) { + .handle = NV_ENGINE(FIFO, 0x04), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_fifo_ctor, + .dtor = nv04_fifo_dtor, + .init = nv04_fifo_init, + .fini = _nvkm_fifo_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv04.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv04.h new file mode 100644 index 000000000..e0e0c47cb --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv04.h @@ -0,0 +1,175 @@ +#ifndef __NV04_FIFO_H__ +#define __NV04_FIFO_H__ +#include + +#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)) + +struct ramfc_desc { + unsigned bits:6; + unsigned ctxs:5; + unsigned ctxp:8; + unsigned regs:5; + unsigned regp; +}; + +struct nv04_fifo_priv { + struct nvkm_fifo base; + struct ramfc_desc *ramfc_desc; + struct nvkm_ramht *ramht; + struct nvkm_gpuobj *ramro; + struct nvkm_gpuobj *ramfc; +}; + +struct nv04_fifo_base { + struct nvkm_fifo_base base; +}; + +struct nv04_fifo_chan { + struct nvkm_fifo_chan base; + u32 subc[8]; + u32 ramfc; +}; + +int nv04_fifo_object_attach(struct nvkm_object *, struct nvkm_object *, u32); +void nv04_fifo_object_detach(struct nvkm_object *, int); + +void nv04_fifo_chan_dtor(struct nvkm_object *); +int nv04_fifo_chan_init(struct nvkm_object *); +int nv04_fifo_chan_fini(struct nvkm_object *, bool suspend); + +int nv04_fifo_context_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); + +void nv04_fifo_dtor(struct nvkm_object *); +int nv04_fifo_init(struct nvkm_object *); +void nv04_fifo_pause(struct nvkm_fifo *, unsigned long *); +void nv04_fifo_start(struct nvkm_fifo *, unsigned long *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv10.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv10.c new file mode 100644 index 000000000..48ce4af6f --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv10.c @@ -0,0 +1,178 @@ +/* + * 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 +#include +#include + +#include +#include + +static struct ramfc_desc +nv10_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 }, + {} +}; + +/******************************************************************************* + * FIFO channel objects + ******************************************************************************/ + +static int +nv10_fifo_chan_ctor(struct nvkm_object *parent, + struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + union { + struct nv03_channel_dma_v0 v0; + } *args = data; + struct nv04_fifo_priv *priv = (void *)engine; + struct nv04_fifo_chan *chan; + int ret; + + nv_ioctl(parent, "create channel dma size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(parent, "create channel dma vers %d pushbuf %08x " + "offset %016llx\n", args->v0.version, + args->v0.pushbuf, args->v0.offset); + } else + return ret; + + ret = nvkm_fifo_channel_create(parent, engine, oclass, 0, 0x800000, + 0x10000, args->v0.pushbuf, + (1ULL << NVDEV_ENGINE_DMAOBJ) | + (1ULL << NVDEV_ENGINE_SW) | + (1ULL << NVDEV_ENGINE_GR), &chan); + *pobject = nv_object(chan); + if (ret) + return ret; + + args->v0.chid = chan->base.chid; + + nv_parent(chan)->object_attach = nv04_fifo_object_attach; + nv_parent(chan)->object_detach = nv04_fifo_object_detach; + nv_parent(chan)->context_attach = nv04_fifo_context_attach; + chan->ramfc = chan->base.chid * 32; + + nv_wo32(priv->ramfc, chan->ramfc + 0x00, args->v0.offset); + nv_wo32(priv->ramfc, chan->ramfc + 0x04, args->v0.offset); + nv_wo32(priv->ramfc, chan->ramfc + 0x0c, chan->base.pushgpu->addr >> 4); + nv_wo32(priv->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); + return 0; +} + +static struct nvkm_ofuncs +nv10_fifo_ofuncs = { + .ctor = nv10_fifo_chan_ctor, + .dtor = nv04_fifo_chan_dtor, + .init = nv04_fifo_chan_init, + .fini = nv04_fifo_chan_fini, + .map = _nvkm_fifo_channel_map, + .rd32 = _nvkm_fifo_channel_rd32, + .wr32 = _nvkm_fifo_channel_wr32, + .ntfy = _nvkm_fifo_channel_ntfy +}; + +static struct nvkm_oclass +nv10_fifo_sclass[] = { + { NV10_CHANNEL_DMA, &nv10_fifo_ofuncs }, + {} +}; + +/******************************************************************************* + * FIFO context - basically just the instmem reserved for the channel + ******************************************************************************/ + +static struct nvkm_oclass +nv10_fifo_cclass = { + .handle = NV_ENGCTX(FIFO, 0x10), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_fifo_context_ctor, + .dtor = _nvkm_fifo_context_dtor, + .init = _nvkm_fifo_context_init, + .fini = _nvkm_fifo_context_fini, + .rd32 = _nvkm_fifo_context_rd32, + .wr32 = _nvkm_fifo_context_wr32, + }, +}; + +/******************************************************************************* + * PFIFO engine + ******************************************************************************/ + +static int +nv10_fifo_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv04_instmem_priv *imem = nv04_instmem(parent); + struct nv04_fifo_priv *priv; + int ret; + + ret = nvkm_fifo_create(parent, engine, oclass, 0, 31, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nvkm_ramht_ref(imem->ramht, &priv->ramht); + nvkm_gpuobj_ref(imem->ramro, &priv->ramro); + nvkm_gpuobj_ref(imem->ramfc, &priv->ramfc); + + nv_subdev(priv)->unit = 0x00000100; + nv_subdev(priv)->intr = nv04_fifo_intr; + nv_engine(priv)->cclass = &nv10_fifo_cclass; + nv_engine(priv)->sclass = nv10_fifo_sclass; + priv->base.pause = nv04_fifo_pause; + priv->base.start = nv04_fifo_start; + priv->ramfc_desc = nv10_ramfc; + return 0; +} + +struct nvkm_oclass * +nv10_fifo_oclass = &(struct nvkm_oclass) { + .handle = NV_ENGINE(FIFO, 0x10), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv10_fifo_ctor, + .dtor = nv04_fifo_dtor, + .init = nv04_fifo_init, + .fini = _nvkm_fifo_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv17.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv17.c new file mode 100644 index 000000000..4a20a6fd3 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv17.c @@ -0,0 +1,215 @@ +/* + * 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 +#include +#include + +#include +#include + +static struct ramfc_desc +nv17_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 }, + {} +}; + +/******************************************************************************* + * FIFO channel objects + ******************************************************************************/ + +static int +nv17_fifo_chan_ctor(struct nvkm_object *parent, + struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + union { + struct nv03_channel_dma_v0 v0; + } *args = data; + struct nv04_fifo_priv *priv = (void *)engine; + struct nv04_fifo_chan *chan; + int ret; + + nv_ioctl(parent, "create channel dma size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(parent, "create channel dma vers %d pushbuf %08x " + "offset %016llx\n", args->v0.version, + args->v0.pushbuf, args->v0.offset); + } else + return ret; + + ret = nvkm_fifo_channel_create(parent, engine, oclass, 0, 0x800000, + 0x10000, args->v0.pushbuf, + (1ULL << NVDEV_ENGINE_DMAOBJ) | + (1ULL << NVDEV_ENGINE_SW) | + (1ULL << NVDEV_ENGINE_GR) | + (1ULL << NVDEV_ENGINE_MPEG), /* NV31- */ + &chan); + *pobject = nv_object(chan); + if (ret) + return ret; + + args->v0.chid = chan->base.chid; + + nv_parent(chan)->object_attach = nv04_fifo_object_attach; + nv_parent(chan)->object_detach = nv04_fifo_object_detach; + nv_parent(chan)->context_attach = nv04_fifo_context_attach; + chan->ramfc = chan->base.chid * 64; + + nv_wo32(priv->ramfc, chan->ramfc + 0x00, args->v0.offset); + nv_wo32(priv->ramfc, chan->ramfc + 0x04, args->v0.offset); + nv_wo32(priv->ramfc, chan->ramfc + 0x0c, chan->base.pushgpu->addr >> 4); + nv_wo32(priv->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); + return 0; +} + +static struct nvkm_ofuncs +nv17_fifo_ofuncs = { + .ctor = nv17_fifo_chan_ctor, + .dtor = nv04_fifo_chan_dtor, + .init = nv04_fifo_chan_init, + .fini = nv04_fifo_chan_fini, + .map = _nvkm_fifo_channel_map, + .rd32 = _nvkm_fifo_channel_rd32, + .wr32 = _nvkm_fifo_channel_wr32, + .ntfy = _nvkm_fifo_channel_ntfy +}; + +static struct nvkm_oclass +nv17_fifo_sclass[] = { + { NV17_CHANNEL_DMA, &nv17_fifo_ofuncs }, + {} +}; + +/******************************************************************************* + * FIFO context - basically just the instmem reserved for the channel + ******************************************************************************/ + +static struct nvkm_oclass +nv17_fifo_cclass = { + .handle = NV_ENGCTX(FIFO, 0x17), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_fifo_context_ctor, + .dtor = _nvkm_fifo_context_dtor, + .init = _nvkm_fifo_context_init, + .fini = _nvkm_fifo_context_fini, + .rd32 = _nvkm_fifo_context_rd32, + .wr32 = _nvkm_fifo_context_wr32, + }, +}; + +/******************************************************************************* + * PFIFO engine + ******************************************************************************/ + +static int +nv17_fifo_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv04_instmem_priv *imem = nv04_instmem(parent); + struct nv04_fifo_priv *priv; + int ret; + + ret = nvkm_fifo_create(parent, engine, oclass, 0, 31, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nvkm_ramht_ref(imem->ramht, &priv->ramht); + nvkm_gpuobj_ref(imem->ramro, &priv->ramro); + nvkm_gpuobj_ref(imem->ramfc, &priv->ramfc); + + nv_subdev(priv)->unit = 0x00000100; + nv_subdev(priv)->intr = nv04_fifo_intr; + nv_engine(priv)->cclass = &nv17_fifo_cclass; + nv_engine(priv)->sclass = nv17_fifo_sclass; + priv->base.pause = nv04_fifo_pause; + priv->base.start = nv04_fifo_start; + priv->ramfc_desc = nv17_ramfc; + return 0; +} + +static int +nv17_fifo_init(struct nvkm_object *object) +{ + struct nv04_fifo_priv *priv = (void *)object; + int ret; + + ret = nvkm_fifo_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, NV04_PFIFO_DELAY_0, 0x000000ff); + nv_wr32(priv, NV04_PFIFO_DMA_TIMESLICE, 0x0101ffff); + + nv_wr32(priv, NV03_PFIFO_RAMHT, (0x03 << 24) /* search 128 */ | + ((priv->ramht->bits - 9) << 16) | + (priv->ramht->gpuobj.addr >> 8)); + nv_wr32(priv, NV03_PFIFO_RAMRO, priv->ramro->addr >> 8); + nv_wr32(priv, NV03_PFIFO_RAMFC, priv->ramfc->addr >> 8 | 0x00010000); + + nv_wr32(priv, NV03_PFIFO_CACHE1_PUSH1, priv->base.max); + + nv_wr32(priv, NV03_PFIFO_INTR_0, 0xffffffff); + nv_wr32(priv, NV03_PFIFO_INTR_EN_0, 0xffffffff); + + nv_wr32(priv, NV03_PFIFO_CACHE1_PUSH0, 1); + nv_wr32(priv, NV04_PFIFO_CACHE1_PULL0, 1); + nv_wr32(priv, NV03_PFIFO_CACHES, 1); + return 0; +} + +struct nvkm_oclass * +nv17_fifo_oclass = &(struct nvkm_oclass) { + .handle = NV_ENGINE(FIFO, 0x17), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv17_fifo_ctor, + .dtor = nv04_fifo_dtor, + .init = nv17_fifo_init, + .fini = _nvkm_fifo_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv40.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv40.c new file mode 100644 index 000000000..5bfc96265 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv40.c @@ -0,0 +1,356 @@ +/* + * 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 +#include +#include +#include +#include + +#include +#include + +static struct ramfc_desc +nv40_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 }, + {} +}; + +/******************************************************************************* + * FIFO channel objects + ******************************************************************************/ + +static int +nv40_fifo_object_attach(struct nvkm_object *parent, + struct nvkm_object *object, u32 handle) +{ + struct nv04_fifo_priv *priv = (void *)parent->engine; + struct nv04_fifo_chan *chan = (void *)parent; + u32 context, chid = chan->base.chid; + int ret; + + if (nv_iclass(object, NV_GPUOBJ_CLASS)) + context = nv_gpuobj(object)->addr >> 4; + else + context = 0x00000004; /* just non-zero */ + + switch (nv_engidx(object->engine)) { + case NVDEV_ENGINE_DMAOBJ: + case NVDEV_ENGINE_SW: + context |= 0x00000000; + break; + case NVDEV_ENGINE_GR: + context |= 0x00100000; + break; + case NVDEV_ENGINE_MPEG: + context |= 0x00200000; + break; + default: + return -EINVAL; + } + + context |= chid << 23; + + mutex_lock(&nv_subdev(priv)->mutex); + ret = nvkm_ramht_insert(priv->ramht, chid, handle, context); + mutex_unlock(&nv_subdev(priv)->mutex); + return ret; +} + +static int +nv40_fifo_context_attach(struct nvkm_object *parent, struct nvkm_object *engctx) +{ + struct nv04_fifo_priv *priv = (void *)parent->engine; + struct nv04_fifo_chan *chan = (void *)parent; + unsigned long flags; + u32 reg, ctx; + + switch (nv_engidx(engctx->engine)) { + case NVDEV_ENGINE_SW: + return 0; + case NVDEV_ENGINE_GR: + reg = 0x32e0; + ctx = 0x38; + break; + case NVDEV_ENGINE_MPEG: + reg = 0x330c; + ctx = 0x54; + break; + default: + return -EINVAL; + } + + spin_lock_irqsave(&priv->base.lock, flags); + nv_engctx(engctx)->addr = nv_gpuobj(engctx)->addr >> 4; + nv_mask(priv, 0x002500, 0x00000001, 0x00000000); + + if ((nv_rd32(priv, 0x003204) & priv->base.max) == chan->base.chid) + nv_wr32(priv, reg, nv_engctx(engctx)->addr); + nv_wo32(priv->ramfc, chan->ramfc + ctx, nv_engctx(engctx)->addr); + + nv_mask(priv, 0x002500, 0x00000001, 0x00000001); + spin_unlock_irqrestore(&priv->base.lock, flags); + return 0; +} + +static int +nv40_fifo_context_detach(struct nvkm_object *parent, bool suspend, + struct nvkm_object *engctx) +{ + struct nv04_fifo_priv *priv = (void *)parent->engine; + struct nv04_fifo_chan *chan = (void *)parent; + unsigned long flags; + u32 reg, ctx; + + switch (nv_engidx(engctx->engine)) { + case NVDEV_ENGINE_SW: + return 0; + case NVDEV_ENGINE_GR: + reg = 0x32e0; + ctx = 0x38; + break; + case NVDEV_ENGINE_MPEG: + reg = 0x330c; + ctx = 0x54; + break; + default: + return -EINVAL; + } + + spin_lock_irqsave(&priv->base.lock, flags); + nv_mask(priv, 0x002500, 0x00000001, 0x00000000); + + if ((nv_rd32(priv, 0x003204) & priv->base.max) == chan->base.chid) + nv_wr32(priv, reg, 0x00000000); + nv_wo32(priv->ramfc, chan->ramfc + ctx, 0x00000000); + + nv_mask(priv, 0x002500, 0x00000001, 0x00000001); + spin_unlock_irqrestore(&priv->base.lock, flags); + return 0; +} + +static int +nv40_fifo_chan_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + union { + struct nv03_channel_dma_v0 v0; + } *args = data; + struct nv04_fifo_priv *priv = (void *)engine; + struct nv04_fifo_chan *chan; + int ret; + + nv_ioctl(parent, "create channel dma size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(parent, "create channel dma vers %d pushbuf %08x " + "offset %016llx\n", args->v0.version, + args->v0.pushbuf, args->v0.offset); + } else + return ret; + + ret = nvkm_fifo_channel_create(parent, engine, oclass, 0, 0xc00000, + 0x1000, args->v0.pushbuf, + (1ULL << NVDEV_ENGINE_DMAOBJ) | + (1ULL << NVDEV_ENGINE_SW) | + (1ULL << NVDEV_ENGINE_GR) | + (1ULL << NVDEV_ENGINE_MPEG), &chan); + *pobject = nv_object(chan); + if (ret) + return ret; + + args->v0.chid = chan->base.chid; + + nv_parent(chan)->context_attach = nv40_fifo_context_attach; + nv_parent(chan)->context_detach = nv40_fifo_context_detach; + nv_parent(chan)->object_attach = nv40_fifo_object_attach; + nv_parent(chan)->object_detach = nv04_fifo_object_detach; + chan->ramfc = chan->base.chid * 128; + + nv_wo32(priv->ramfc, chan->ramfc + 0x00, args->v0.offset); + nv_wo32(priv->ramfc, chan->ramfc + 0x04, args->v0.offset); + nv_wo32(priv->ramfc, chan->ramfc + 0x0c, chan->base.pushgpu->addr >> 4); + nv_wo32(priv->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); + nv_wo32(priv->ramfc, chan->ramfc + 0x3c, 0x0001ffff); + return 0; +} + +static struct nvkm_ofuncs +nv40_fifo_ofuncs = { + .ctor = nv40_fifo_chan_ctor, + .dtor = nv04_fifo_chan_dtor, + .init = nv04_fifo_chan_init, + .fini = nv04_fifo_chan_fini, + .map = _nvkm_fifo_channel_map, + .rd32 = _nvkm_fifo_channel_rd32, + .wr32 = _nvkm_fifo_channel_wr32, + .ntfy = _nvkm_fifo_channel_ntfy +}; + +static struct nvkm_oclass +nv40_fifo_sclass[] = { + { NV40_CHANNEL_DMA, &nv40_fifo_ofuncs }, + {} +}; + +/******************************************************************************* + * FIFO context - basically just the instmem reserved for the channel + ******************************************************************************/ + +static struct nvkm_oclass +nv40_fifo_cclass = { + .handle = NV_ENGCTX(FIFO, 0x40), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_fifo_context_ctor, + .dtor = _nvkm_fifo_context_dtor, + .init = _nvkm_fifo_context_init, + .fini = _nvkm_fifo_context_fini, + .rd32 = _nvkm_fifo_context_rd32, + .wr32 = _nvkm_fifo_context_wr32, + }, +}; + +/******************************************************************************* + * PFIFO engine + ******************************************************************************/ + +static int +nv40_fifo_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv04_instmem_priv *imem = nv04_instmem(parent); + struct nv04_fifo_priv *priv; + int ret; + + ret = nvkm_fifo_create(parent, engine, oclass, 0, 31, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nvkm_ramht_ref(imem->ramht, &priv->ramht); + nvkm_gpuobj_ref(imem->ramro, &priv->ramro); + nvkm_gpuobj_ref(imem->ramfc, &priv->ramfc); + + nv_subdev(priv)->unit = 0x00000100; + nv_subdev(priv)->intr = nv04_fifo_intr; + nv_engine(priv)->cclass = &nv40_fifo_cclass; + nv_engine(priv)->sclass = nv40_fifo_sclass; + priv->base.pause = nv04_fifo_pause; + priv->base.start = nv04_fifo_start; + priv->ramfc_desc = nv40_ramfc; + return 0; +} + +static int +nv40_fifo_init(struct nvkm_object *object) +{ + struct nv04_fifo_priv *priv = (void *)object; + struct nvkm_fb *pfb = nvkm_fb(object); + int ret; + + ret = nvkm_fifo_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, 0x002040, 0x000000ff); + nv_wr32(priv, 0x002044, 0x2101ffff); + nv_wr32(priv, 0x002058, 0x00000001); + + nv_wr32(priv, NV03_PFIFO_RAMHT, (0x03 << 24) /* search 128 */ | + ((priv->ramht->bits - 9) << 16) | + (priv->ramht->gpuobj.addr >> 8)); + nv_wr32(priv, NV03_PFIFO_RAMRO, priv->ramro->addr >> 8); + + switch (nv_device(priv)->chipset) { + case 0x47: + case 0x49: + case 0x4b: + nv_wr32(priv, 0x002230, 0x00000001); + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x45: + case 0x48: + nv_wr32(priv, 0x002220, 0x00030002); + break; + default: + nv_wr32(priv, 0x002230, 0x00000000); + nv_wr32(priv, 0x002220, ((pfb->ram->size - 512 * 1024 + + priv->ramfc->addr) >> 16) | + 0x00030000); + break; + } + + nv_wr32(priv, NV03_PFIFO_CACHE1_PUSH1, priv->base.max); + + nv_wr32(priv, NV03_PFIFO_INTR_0, 0xffffffff); + nv_wr32(priv, NV03_PFIFO_INTR_EN_0, 0xffffffff); + + nv_wr32(priv, NV03_PFIFO_CACHE1_PUSH0, 1); + nv_wr32(priv, NV04_PFIFO_CACHE1_PULL0, 1); + nv_wr32(priv, NV03_PFIFO_CACHES, 1); + return 0; +} + +struct nvkm_oclass * +nv40_fifo_oclass = &(struct nvkm_oclass) { + .handle = NV_ENGINE(FIFO, 0x40), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv40_fifo_ctor, + .dtor = nv04_fifo_dtor, + .init = nv40_fifo_init, + .fini = _nvkm_fifo_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv50.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv50.c new file mode 100644 index 000000000..f25f0fd06 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv50.c @@ -0,0 +1,534 @@ +/* + * 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 "nv04.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +/******************************************************************************* + * FIFO channel objects + ******************************************************************************/ + +static void +nv50_fifo_playlist_update_locked(struct nv50_fifo_priv *priv) +{ + struct nvkm_bar *bar = nvkm_bar(priv); + struct nvkm_gpuobj *cur; + int i, p; + + cur = priv->playlist[priv->cur_playlist]; + priv->cur_playlist = !priv->cur_playlist; + + for (i = priv->base.min, p = 0; i < priv->base.max; i++) { + if (nv_rd32(priv, 0x002600 + (i * 4)) & 0x80000000) + nv_wo32(cur, p++ * 4, i); + } + + bar->flush(bar); + + nv_wr32(priv, 0x0032f4, cur->addr >> 12); + nv_wr32(priv, 0x0032ec, p); + nv_wr32(priv, 0x002500, 0x00000101); +} + +void +nv50_fifo_playlist_update(struct nv50_fifo_priv *priv) +{ + mutex_lock(&nv_subdev(priv)->mutex); + nv50_fifo_playlist_update_locked(priv); + mutex_unlock(&nv_subdev(priv)->mutex); +} + +static int +nv50_fifo_context_attach(struct nvkm_object *parent, struct nvkm_object *object) +{ + struct nvkm_bar *bar = nvkm_bar(parent); + struct nv50_fifo_base *base = (void *)parent->parent; + struct nvkm_gpuobj *ectx = (void *)object; + u64 limit = ectx->addr + ectx->size - 1; + u64 start = ectx->addr; + u32 addr; + + switch (nv_engidx(object->engine)) { + case NVDEV_ENGINE_SW : return 0; + case NVDEV_ENGINE_GR : addr = 0x0000; break; + case NVDEV_ENGINE_MPEG : addr = 0x0060; break; + default: + return -EINVAL; + } + + nv_engctx(ectx)->addr = nv_gpuobj(base)->addr >> 12; + nv_wo32(base->eng, addr + 0x00, 0x00190000); + nv_wo32(base->eng, addr + 0x04, lower_32_bits(limit)); + nv_wo32(base->eng, addr + 0x08, lower_32_bits(start)); + nv_wo32(base->eng, addr + 0x0c, upper_32_bits(limit) << 24 | + upper_32_bits(start)); + nv_wo32(base->eng, addr + 0x10, 0x00000000); + nv_wo32(base->eng, addr + 0x14, 0x00000000); + bar->flush(bar); + return 0; +} + +static int +nv50_fifo_context_detach(struct nvkm_object *parent, bool suspend, + struct nvkm_object *object) +{ + struct nvkm_bar *bar = nvkm_bar(parent); + struct nv50_fifo_priv *priv = (void *)parent->engine; + struct nv50_fifo_base *base = (void *)parent->parent; + struct nv50_fifo_chan *chan = (void *)parent; + u32 addr, me; + int ret = 0; + + switch (nv_engidx(object->engine)) { + case NVDEV_ENGINE_SW : return 0; + case NVDEV_ENGINE_GR : addr = 0x0000; break; + case NVDEV_ENGINE_MPEG : addr = 0x0060; break; + default: + return -EINVAL; + } + + /* 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 = nv_mask(priv, 0x00b860, 0x00000001, 0x00000001); + + /* do the kickoff... */ + nv_wr32(priv, 0x0032fc, nv_gpuobj(base)->addr >> 12); + if (!nv_wait_ne(priv, 0x0032fc, 0xffffffff, 0xffffffff)) { + nv_error(priv, "channel %d [%s] unload timeout\n", + chan->base.chid, nvkm_client_name(chan)); + if (suspend) + ret = -EBUSY; + } + nv_wr32(priv, 0x00b860, me); + + if (ret == 0) { + nv_wo32(base->eng, addr + 0x00, 0x00000000); + nv_wo32(base->eng, addr + 0x04, 0x00000000); + nv_wo32(base->eng, addr + 0x08, 0x00000000); + nv_wo32(base->eng, addr + 0x0c, 0x00000000); + nv_wo32(base->eng, addr + 0x10, 0x00000000); + nv_wo32(base->eng, addr + 0x14, 0x00000000); + bar->flush(bar); + } + + return ret; +} + +static int +nv50_fifo_object_attach(struct nvkm_object *parent, + struct nvkm_object *object, u32 handle) +{ + struct nv50_fifo_chan *chan = (void *)parent; + u32 context; + + if (nv_iclass(object, NV_GPUOBJ_CLASS)) + context = nv_gpuobj(object)->node->offset >> 4; + else + context = 0x00000004; /* just non-zero */ + + switch (nv_engidx(object->engine)) { + case NVDEV_ENGINE_DMAOBJ: + case NVDEV_ENGINE_SW : context |= 0x00000000; break; + case NVDEV_ENGINE_GR : context |= 0x00100000; break; + case NVDEV_ENGINE_MPEG : context |= 0x00200000; break; + default: + return -EINVAL; + } + + return nvkm_ramht_insert(chan->ramht, 0, handle, context); +} + +void +nv50_fifo_object_detach(struct nvkm_object *parent, int cookie) +{ + struct nv50_fifo_chan *chan = (void *)parent; + nvkm_ramht_remove(chan->ramht, cookie); +} + +static int +nv50_fifo_chan_ctor_dma(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + union { + struct nv03_channel_dma_v0 v0; + } *args = data; + struct nvkm_bar *bar = nvkm_bar(parent); + struct nv50_fifo_base *base = (void *)parent; + struct nv50_fifo_chan *chan; + int ret; + + nv_ioctl(parent, "create channel dma size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(parent, "create channel dma vers %d pushbuf %08x " + "offset %016llx\n", args->v0.version, + args->v0.pushbuf, args->v0.offset); + } else + return ret; + + ret = nvkm_fifo_channel_create(parent, engine, oclass, 0, 0xc00000, + 0x2000, args->v0.pushbuf, + (1ULL << NVDEV_ENGINE_DMAOBJ) | + (1ULL << NVDEV_ENGINE_SW) | + (1ULL << NVDEV_ENGINE_GR) | + (1ULL << NVDEV_ENGINE_MPEG), &chan); + *pobject = nv_object(chan); + if (ret) + return ret; + + args->v0.chid = chan->base.chid; + + nv_parent(chan)->context_attach = nv50_fifo_context_attach; + nv_parent(chan)->context_detach = nv50_fifo_context_detach; + nv_parent(chan)->object_attach = nv50_fifo_object_attach; + nv_parent(chan)->object_detach = nv50_fifo_object_detach; + + ret = nvkm_ramht_new(nv_object(chan), nv_object(chan), 0x8000, 16, + &chan->ramht); + if (ret) + return ret; + + nv_wo32(base->ramfc, 0x08, lower_32_bits(args->v0.offset)); + nv_wo32(base->ramfc, 0x0c, upper_32_bits(args->v0.offset)); + nv_wo32(base->ramfc, 0x10, lower_32_bits(args->v0.offset)); + nv_wo32(base->ramfc, 0x14, upper_32_bits(args->v0.offset)); + nv_wo32(base->ramfc, 0x3c, 0x003f6078); + nv_wo32(base->ramfc, 0x44, 0x01003fff); + nv_wo32(base->ramfc, 0x48, chan->base.pushgpu->node->offset >> 4); + nv_wo32(base->ramfc, 0x4c, 0xffffffff); + nv_wo32(base->ramfc, 0x60, 0x7fffffff); + nv_wo32(base->ramfc, 0x78, 0x00000000); + nv_wo32(base->ramfc, 0x7c, 0x30000001); + nv_wo32(base->ramfc, 0x80, ((chan->ramht->bits - 9) << 27) | + (4 << 24) /* SEARCH_FULL */ | + (chan->ramht->gpuobj.node->offset >> 4)); + bar->flush(bar); + return 0; +} + +static int +nv50_fifo_chan_ctor_ind(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + union { + struct nv50_channel_gpfifo_v0 v0; + } *args = data; + struct nvkm_bar *bar = nvkm_bar(parent); + struct nv50_fifo_base *base = (void *)parent; + struct nv50_fifo_chan *chan; + u64 ioffset, ilength; + int ret; + + nv_ioctl(parent, "create channel gpfifo size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(parent, "create channel gpfifo vers %d pushbuf %08x " + "ioffset %016llx ilength %08x\n", + args->v0.version, args->v0.pushbuf, args->v0.ioffset, + args->v0.ilength); + } else + return ret; + + ret = nvkm_fifo_channel_create(parent, engine, oclass, 0, 0xc00000, + 0x2000, args->v0.pushbuf, + (1ULL << NVDEV_ENGINE_DMAOBJ) | + (1ULL << NVDEV_ENGINE_SW) | + (1ULL << NVDEV_ENGINE_GR) | + (1ULL << NVDEV_ENGINE_MPEG), &chan); + *pobject = nv_object(chan); + if (ret) + return ret; + + args->v0.chid = chan->base.chid; + + nv_parent(chan)->context_attach = nv50_fifo_context_attach; + nv_parent(chan)->context_detach = nv50_fifo_context_detach; + nv_parent(chan)->object_attach = nv50_fifo_object_attach; + nv_parent(chan)->object_detach = nv50_fifo_object_detach; + + ret = nvkm_ramht_new(nv_object(chan), nv_object(chan), 0x8000, 16, + &chan->ramht); + if (ret) + return ret; + + ioffset = args->v0.ioffset; + ilength = order_base_2(args->v0.ilength / 8); + + nv_wo32(base->ramfc, 0x3c, 0x403f6078); + nv_wo32(base->ramfc, 0x44, 0x01003fff); + nv_wo32(base->ramfc, 0x48, chan->base.pushgpu->node->offset >> 4); + nv_wo32(base->ramfc, 0x50, lower_32_bits(ioffset)); + nv_wo32(base->ramfc, 0x54, upper_32_bits(ioffset) | (ilength << 16)); + nv_wo32(base->ramfc, 0x60, 0x7fffffff); + nv_wo32(base->ramfc, 0x78, 0x00000000); + nv_wo32(base->ramfc, 0x7c, 0x30000001); + nv_wo32(base->ramfc, 0x80, ((chan->ramht->bits - 9) << 27) | + (4 << 24) /* SEARCH_FULL */ | + (chan->ramht->gpuobj.node->offset >> 4)); + bar->flush(bar); + return 0; +} + +void +nv50_fifo_chan_dtor(struct nvkm_object *object) +{ + struct nv50_fifo_chan *chan = (void *)object; + nvkm_ramht_ref(NULL, &chan->ramht); + nvkm_fifo_channel_destroy(&chan->base); +} + +static int +nv50_fifo_chan_init(struct nvkm_object *object) +{ + struct nv50_fifo_priv *priv = (void *)object->engine; + struct nv50_fifo_base *base = (void *)object->parent; + struct nv50_fifo_chan *chan = (void *)object; + struct nvkm_gpuobj *ramfc = base->ramfc; + u32 chid = chan->base.chid; + int ret; + + ret = nvkm_fifo_channel_init(&chan->base); + if (ret) + return ret; + + nv_wr32(priv, 0x002600 + (chid * 4), 0x80000000 | ramfc->addr >> 12); + nv50_fifo_playlist_update(priv); + return 0; +} + +int +nv50_fifo_chan_fini(struct nvkm_object *object, bool suspend) +{ + struct nv50_fifo_priv *priv = (void *)object->engine; + struct nv50_fifo_chan *chan = (void *)object; + u32 chid = chan->base.chid; + + /* remove channel from playlist, fifo will unload context */ + nv_mask(priv, 0x002600 + (chid * 4), 0x80000000, 0x00000000); + nv50_fifo_playlist_update(priv); + nv_wr32(priv, 0x002600 + (chid * 4), 0x00000000); + + return nvkm_fifo_channel_fini(&chan->base, suspend); +} + +static struct nvkm_ofuncs +nv50_fifo_ofuncs_dma = { + .ctor = nv50_fifo_chan_ctor_dma, + .dtor = nv50_fifo_chan_dtor, + .init = nv50_fifo_chan_init, + .fini = nv50_fifo_chan_fini, + .map = _nvkm_fifo_channel_map, + .rd32 = _nvkm_fifo_channel_rd32, + .wr32 = _nvkm_fifo_channel_wr32, + .ntfy = _nvkm_fifo_channel_ntfy +}; + +static struct nvkm_ofuncs +nv50_fifo_ofuncs_ind = { + .ctor = nv50_fifo_chan_ctor_ind, + .dtor = nv50_fifo_chan_dtor, + .init = nv50_fifo_chan_init, + .fini = nv50_fifo_chan_fini, + .map = _nvkm_fifo_channel_map, + .rd32 = _nvkm_fifo_channel_rd32, + .wr32 = _nvkm_fifo_channel_wr32, + .ntfy = _nvkm_fifo_channel_ntfy +}; + +static struct nvkm_oclass +nv50_fifo_sclass[] = { + { NV50_CHANNEL_DMA, &nv50_fifo_ofuncs_dma }, + { NV50_CHANNEL_GPFIFO, &nv50_fifo_ofuncs_ind }, + {} +}; + +/******************************************************************************* + * FIFO context - basically just the instmem reserved for the channel + ******************************************************************************/ + +static int +nv50_fifo_context_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv50_fifo_base *base; + int ret; + + ret = nvkm_fifo_context_create(parent, engine, oclass, NULL, 0x10000, + 0x1000, NVOBJ_FLAG_HEAP, &base); + *pobject = nv_object(base); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(nv_object(base), nv_object(base), 0x0200, + 0x1000, NVOBJ_FLAG_ZERO_ALLOC, &base->ramfc); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(nv_object(base), nv_object(base), 0x1200, 0, + NVOBJ_FLAG_ZERO_ALLOC, &base->eng); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(nv_object(base), nv_object(base), 0x4000, 0, 0, + &base->pgd); + if (ret) + return ret; + + ret = nvkm_vm_ref(nvkm_client(parent)->vm, &base->vm, base->pgd); + if (ret) + return ret; + + return 0; +} + +void +nv50_fifo_context_dtor(struct nvkm_object *object) +{ + struct nv50_fifo_base *base = (void *)object; + nvkm_vm_ref(NULL, &base->vm, base->pgd); + nvkm_gpuobj_ref(NULL, &base->pgd); + nvkm_gpuobj_ref(NULL, &base->eng); + nvkm_gpuobj_ref(NULL, &base->ramfc); + nvkm_gpuobj_ref(NULL, &base->cache); + nvkm_fifo_context_destroy(&base->base); +} + +static struct nvkm_oclass +nv50_fifo_cclass = { + .handle = NV_ENGCTX(FIFO, 0x50), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_fifo_context_ctor, + .dtor = nv50_fifo_context_dtor, + .init = _nvkm_fifo_context_init, + .fini = _nvkm_fifo_context_fini, + .rd32 = _nvkm_fifo_context_rd32, + .wr32 = _nvkm_fifo_context_wr32, + }, +}; + +/******************************************************************************* + * PFIFO engine + ******************************************************************************/ + +static int +nv50_fifo_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv50_fifo_priv *priv; + int ret; + + ret = nvkm_fifo_create(parent, engine, oclass, 1, 127, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(nv_object(priv), NULL, 128 * 4, 0x1000, 0, + &priv->playlist[0]); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(nv_object(priv), NULL, 128 * 4, 0x1000, 0, + &priv->playlist[1]); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00000100; + nv_subdev(priv)->intr = nv04_fifo_intr; + nv_engine(priv)->cclass = &nv50_fifo_cclass; + nv_engine(priv)->sclass = nv50_fifo_sclass; + priv->base.pause = nv04_fifo_pause; + priv->base.start = nv04_fifo_start; + return 0; +} + +void +nv50_fifo_dtor(struct nvkm_object *object) +{ + struct nv50_fifo_priv *priv = (void *)object; + + nvkm_gpuobj_ref(NULL, &priv->playlist[1]); + nvkm_gpuobj_ref(NULL, &priv->playlist[0]); + + nvkm_fifo_destroy(&priv->base); +} + +int +nv50_fifo_init(struct nvkm_object *object) +{ + struct nv50_fifo_priv *priv = (void *)object; + int ret, i; + + ret = nvkm_fifo_init(&priv->base); + if (ret) + return ret; + + nv_mask(priv, 0x000200, 0x00000100, 0x00000000); + nv_mask(priv, 0x000200, 0x00000100, 0x00000100); + nv_wr32(priv, 0x00250c, 0x6f3cfc34); + nv_wr32(priv, 0x002044, 0x01003fff); + + nv_wr32(priv, 0x002100, 0xffffffff); + nv_wr32(priv, 0x002140, 0xbfffffff); + + for (i = 0; i < 128; i++) + nv_wr32(priv, 0x002600 + (i * 4), 0x00000000); + nv50_fifo_playlist_update_locked(priv); + + nv_wr32(priv, 0x003200, 0x00000001); + nv_wr32(priv, 0x003250, 0x00000001); + nv_wr32(priv, 0x002500, 0x00000001); + return 0; +} + +struct nvkm_oclass * +nv50_fifo_oclass = &(struct nvkm_oclass) { + .handle = NV_ENGINE(FIFO, 0x50), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_fifo_ctor, + .dtor = nv50_fifo_dtor, + .init = nv50_fifo_init, + .fini = _nvkm_fifo_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv50.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv50.h new file mode 100644 index 000000000..09ed93c66 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv50.h @@ -0,0 +1,36 @@ +#ifndef __NV50_FIFO_H__ +#define __NV50_FIFO_H__ +#include + +struct nv50_fifo_priv { + struct nvkm_fifo base; + struct nvkm_gpuobj *playlist[2]; + int cur_playlist; +}; + +struct nv50_fifo_base { + struct nvkm_fifo_base base; + struct nvkm_gpuobj *ramfc; + struct nvkm_gpuobj *cache; + struct nvkm_gpuobj *eng; + struct nvkm_gpuobj *pgd; + struct nvkm_vm *vm; +}; + +struct nv50_fifo_chan { + struct nvkm_fifo_chan base; + u32 subc[8]; + struct nvkm_ramht *ramht; +}; + +void nv50_fifo_playlist_update(struct nv50_fifo_priv *); + +void nv50_fifo_object_detach(struct nvkm_object *, int); +void nv50_fifo_chan_dtor(struct nvkm_object *); +int nv50_fifo_chan_fini(struct nvkm_object *, bool); + +void nv50_fifo_context_dtor(struct nvkm_object *); + +void nv50_fifo_dtor(struct nvkm_object *); +int nv50_fifo_init(struct nvkm_object *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/Kbuild new file mode 100644 index 000000000..2e1b92f71 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/Kbuild @@ -0,0 +1,40 @@ +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/ctxgf108.o +nvkm-y += nvkm/engine/gr/ctxgf104.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/ctxgk20a.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/ctxgm107.o +nvkm-y += nvkm/engine/gr/ctxgm204.o +nvkm-y += nvkm/engine/gr/ctxgm206.o +nvkm-y += nvkm/engine/gr/nv04.o +nvkm-y += nvkm/engine/gr/nv10.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/nv50.o +nvkm-y += nvkm/engine/gr/gf100.o +nvkm-y += nvkm/engine/gr/gf108.o +nvkm-y += nvkm/engine/gr/gf104.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/gk20a.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/gm107.o +nvkm-y += nvkm/engine/gr/gm204.o +nvkm-y += nvkm/engine/gr/gm206.o diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf100.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf100.c new file mode 100644 index 000000000..57e2c5b13 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf100.c @@ -0,0 +1,1390 @@ +/* + * 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 +#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[] = { + { 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 }, + { 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, u32 access) +{ + 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->access = access; + 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) +{ + 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; + } + + nv_wr32(info->priv, addr, data); +} + +void +gf100_grctx_generate_bundle(struct gf100_grctx *info) +{ + const struct gf100_grctx_oclass *impl = gf100_grctx_impl(info->priv); + const u32 access = NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS; + const int s = 8; + const int b = mmio_vram(info, impl->bundle_size, (1 << s), access); + mmio_refn(info, 0x408004, 0x00000000, s, b); + mmio_wr32(info, 0x408008, 0x80000000 | (impl->bundle_size >> s)); + mmio_refn(info, 0x418808, 0x00000000, s, b); + mmio_wr32(info, 0x41880c, 0x80000000 | (impl->bundle_size >> s)); +} + +void +gf100_grctx_generate_pagepool(struct gf100_grctx *info) +{ + const struct gf100_grctx_oclass *impl = gf100_grctx_impl(info->priv); + const u32 access = NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS; + const int s = 8; + const int b = mmio_vram(info, impl->pagepool_size, (1 << s), access); + 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_priv *priv = info->priv; + const struct gf100_grctx_oclass *impl = gf100_grctx_impl(priv); + const u32 attrib = impl->attrib_nr; + const u32 size = 0x20 * (impl->attrib_nr_max + impl->alpha_nr_max); + const u32 access = NV_MEM_ACCESS_RW; + const int s = 12; + const int b = mmio_vram(info, size * priv->tpc_total, (1 << s), access); + 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 < priv->gpc_nr; gpc++) { + for (tpc = 0; tpc < priv->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 += impl->attrib_nr_max; + } + } +} + +void +gf100_grctx_generate_unkn(struct gf100_gr_priv *priv) +{ +} + +void +gf100_grctx_generate_tpcid(struct gf100_gr_priv *priv) +{ + int gpc, tpc, id; + + for (tpc = 0, id = 0; tpc < 4; tpc++) { + for (gpc = 0; gpc < priv->gpc_nr; gpc++) { + if (tpc < priv->tpc_nr[gpc]) { + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x698), id); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x4e8), id); + nv_wr32(priv, GPC_UNIT(gpc, 0x0c10 + tpc * 4), id); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x088), id); + id++; + } + + nv_wr32(priv, GPC_UNIT(gpc, 0x0c08), priv->tpc_nr[gpc]); + nv_wr32(priv, GPC_UNIT(gpc, 0x0c8c), priv->tpc_nr[gpc]); + } + } +} + +void +gf100_grctx_generate_r406028(struct gf100_gr_priv *priv) +{ + u32 tmp[GPC_MAX / 8] = {}, i = 0; + for (i = 0; i < priv->gpc_nr; i++) + tmp[i / 8] |= priv->tpc_nr[i] << ((i % 8) * 4); + for (i = 0; i < 4; i++) { + nv_wr32(priv, 0x406028 + (i * 4), tmp[i]); + nv_wr32(priv, 0x405870 + (i * 4), tmp[i]); + } +} + +void +gf100_grctx_generate_r4060a8(struct gf100_gr_priv *priv) +{ + u8 tpcnr[GPC_MAX], data[TPC_MAX]; + int gpc, tpc, i; + + memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr)); + memset(data, 0x1f, sizeof(data)); + + gpc = -1; + for (tpc = 0; tpc < priv->tpc_total; tpc++) { + do { + gpc = (gpc + 1) % priv->gpc_nr; + } while (!tpcnr[gpc]); + tpcnr[gpc]--; + data[tpc] = gpc; + } + + for (i = 0; i < 4; i++) + nv_wr32(priv, 0x4060a8 + (i * 4), ((u32 *)data)[i]); +} + +void +gf100_grctx_generate_r418bb8(struct gf100_gr_priv *priv) +{ + u32 data[6] = {}, data2[2] = {}; + u8 tpcnr[GPC_MAX]; + u8 shift, ntpcv; + int gpc, tpc, i; + + /* calculate first set of magics */ + memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr)); + + gpc = -1; + for (tpc = 0; tpc < priv->tpc_total; tpc++) { + do { + gpc = (gpc + 1) % priv->gpc_nr; + } while (!tpcnr[gpc]); + tpcnr[gpc]--; + + data[tpc / 6] |= gpc << ((tpc % 6) * 5); + } + + for (; tpc < 32; tpc++) + data[tpc / 6] |= 7 << ((tpc % 6) * 5); + + /* and the second... */ + shift = 0; + ntpcv = priv->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 */ + nv_wr32(priv, 0x418bb8, (priv->tpc_total << 8) | + priv->magic_not_rop_nr); + for (i = 0; i < 6; i++) + nv_wr32(priv, 0x418b08 + (i * 4), data[i]); + + /* GPC_BROADCAST.TP_BROADCAST */ + nv_wr32(priv, 0x419bd0, (priv->tpc_total << 8) | + priv->magic_not_rop_nr | data2[0]); + nv_wr32(priv, 0x419be4, data2[1]); + for (i = 0; i < 6; i++) + nv_wr32(priv, 0x419b00 + (i * 4), data[i]); + + /* UNK78xx */ + nv_wr32(priv, 0x4078bc, (priv->tpc_total << 8) | + priv->magic_not_rop_nr); + for (i = 0; i < 6; i++) + nv_wr32(priv, 0x40780c + (i * 4), data[i]); +} + +void +gf100_grctx_generate_r406800(struct gf100_gr_priv *priv) +{ + u64 tpc_mask = 0, tpc_set = 0; + u8 tpcnr[GPC_MAX]; + int gpc, tpc; + int i, a, b; + + memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr)); + for (gpc = 0; gpc < priv->gpc_nr; gpc++) + tpc_mask |= ((1ULL << priv->tpc_nr[gpc]) - 1) << (gpc * 8); + + for (i = 0, gpc = -1, b = -1; i < 32; i++) { + a = (i * (priv->tpc_total - 1)) / 32; + if (a != b) { + b = a; + do { + gpc = (gpc + 1) % priv->gpc_nr; + } while (!tpcnr[gpc]); + tpc = priv->tpc_nr[gpc] - tpcnr[gpc]--; + + tpc_set |= 1ULL << ((gpc * 8) + tpc); + } + + nv_wr32(priv, 0x406800 + (i * 0x20), lower_32_bits(tpc_set)); + nv_wr32(priv, 0x406c00 + (i * 0x20), lower_32_bits(tpc_set ^ tpc_mask)); + if (priv->gpc_nr > 4) { + nv_wr32(priv, 0x406804 + (i * 0x20), upper_32_bits(tpc_set)); + nv_wr32(priv, 0x406c04 + (i * 0x20), upper_32_bits(tpc_set ^ tpc_mask)); + } + } +} + +void +gf100_grctx_generate_main(struct gf100_gr_priv *priv, struct gf100_grctx *info) +{ + struct gf100_grctx_oclass *oclass = (void *)nv_engine(priv)->cclass; + + nvkm_mc(priv)->unk260(nvkm_mc(priv), 0); + + gf100_gr_mmio(priv, oclass->hub); + gf100_gr_mmio(priv, oclass->gpc); + gf100_gr_mmio(priv, oclass->zcull); + gf100_gr_mmio(priv, oclass->tpc); + gf100_gr_mmio(priv, oclass->ppc); + + nv_wr32(priv, 0x404154, 0x00000000); + + oclass->bundle(info); + oclass->pagepool(info); + oclass->attrib(info); + oclass->unkn(priv); + + gf100_grctx_generate_tpcid(priv); + gf100_grctx_generate_r406028(priv); + gf100_grctx_generate_r4060a8(priv); + gf100_grctx_generate_r418bb8(priv); + gf100_grctx_generate_r406800(priv); + + gf100_gr_icmd(priv, oclass->icmd); + nv_wr32(priv, 0x404154, 0x00000400); + gf100_gr_mthd(priv, oclass->mthd); + nvkm_mc(priv)->unk260(nvkm_mc(priv), 1); +} + +int +gf100_grctx_generate(struct gf100_gr_priv *priv) +{ + struct gf100_grctx_oclass *oclass = (void *)nv_engine(priv)->cclass; + struct nvkm_bar *bar = nvkm_bar(priv); + struct nvkm_gpuobj *chan; + struct gf100_grctx info; + int ret, i; + + /* allocate memory to for a "channel", which we'll use to generate + * the default context values + */ + ret = nvkm_gpuobj_new(nv_object(priv), NULL, 0x80000 + priv->size, + 0x1000, NVOBJ_FLAG_ZERO_ALLOC, &chan); + if (ret) { + nv_error(priv, "failed to allocate channel memory, %d\n", ret); + return ret; + } + + /* PGD pointer */ + nv_wo32(chan, 0x0200, lower_32_bits(chan->addr + 0x1000)); + nv_wo32(chan, 0x0204, upper_32_bits(chan->addr + 0x1000)); + nv_wo32(chan, 0x0208, 0xffffffff); + nv_wo32(chan, 0x020c, 0x000000ff); + + /* PGT[0] pointer */ + nv_wo32(chan, 0x1000, 0x00000000); + nv_wo32(chan, 0x1004, 0x00000001 | (chan->addr + 0x2000) >> 8); + + /* identity-map the whole "channel" into its own vm */ + for (i = 0; i < chan->size / 4096; i++) { + u64 addr = ((chan->addr + (i * 4096)) >> 8) | 1; + nv_wo32(chan, 0x2000 + (i * 8), lower_32_bits(addr)); + nv_wo32(chan, 0x2004 + (i * 8), upper_32_bits(addr)); + } + + /* context pointer (virt) */ + nv_wo32(chan, 0x0210, 0x00080004); + nv_wo32(chan, 0x0214, 0x00000000); + + bar->flush(bar); + + nv_wr32(priv, 0x100cb8, (chan->addr + 0x1000) >> 8); + nv_wr32(priv, 0x100cbc, 0x80000001); + nv_wait(priv, 0x100c80, 0x00008000, 0x00008000); + + /* setup default state for mmio list construction */ + info.priv = priv; + info.data = priv->mmio_data; + info.mmio = priv->mmio_list; + info.addr = 0x2000 + (i * 8); + info.buffer_nr = 0; + + /* make channel current */ + if (priv->firmware) { + nv_wr32(priv, 0x409840, 0x00000030); + nv_wr32(priv, 0x409500, 0x80000000 | chan->addr >> 12); + nv_wr32(priv, 0x409504, 0x00000003); + if (!nv_wait(priv, 0x409800, 0x00000010, 0x00000010)) + nv_error(priv, "load_ctx timeout\n"); + + nv_wo32(chan, 0x8001c, 1); + nv_wo32(chan, 0x80020, 0); + nv_wo32(chan, 0x80028, 0); + nv_wo32(chan, 0x8002c, 0); + bar->flush(bar); + } else { + nv_wr32(priv, 0x409840, 0x80000000); + nv_wr32(priv, 0x409500, 0x80000000 | chan->addr >> 12); + nv_wr32(priv, 0x409504, 0x00000001); + if (!nv_wait(priv, 0x409800, 0x80000000, 0x80000000)) + nv_error(priv, "HUB_SET_CHAN timeout\n"); + } + + oclass->main(priv, &info); + + /* trigger a context unload by unsetting the "next channel valid" bit + * and faking a context switch interrupt + */ + nv_mask(priv, 0x409b04, 0x80000000, 0x00000000); + nv_wr32(priv, 0x409000, 0x00000100); + if (!nv_wait(priv, 0x409b00, 0x80000000, 0x00000000)) { + nv_error(priv, "grctx template channel unload timeout\n"); + ret = -EBUSY; + goto done; + } + + priv->data = kmalloc(priv->size, GFP_KERNEL); + if (priv->data) { + for (i = 0; i < priv->size; i += 4) + priv->data[i / 4] = nv_ro32(chan, 0x80000 + i); + ret = 0; + } else { + ret = -ENOMEM; + } + +done: + nvkm_gpuobj_ref(NULL, &chan); + return ret; +} + +struct nvkm_oclass * +gf100_grctx_oclass = &(struct gf100_grctx_oclass) { + .base.handle = NV_ENGCTX(GR, 0xc0), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_gr_context_ctor, + .dtor = gf100_gr_context_dtor, + .init = _nvkm_gr_context_init, + .fini = _nvkm_gr_context_fini, + .rd32 = _nvkm_gr_context_rd32, + .wr32 = _nvkm_gr_context_wr32, + }, + .main = gf100_grctx_generate_main, + .unkn = gf100_grctx_generate_unkn, + .hub = gf100_grctx_pack_hub, + .gpc = gf100_grctx_pack_gpc, + .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, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf100.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf100.h new file mode 100644 index 000000000..3676a3342 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf100.h @@ -0,0 +1,228 @@ +#ifndef __NVKM_GRCTX_NVC0_H__ +#define __NVKM_GRCTX_NVC0_H__ +#include "gf100.h" + +struct gf100_grctx { + struct gf100_gr_priv *priv; + 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, u32 access); +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_oclass { + struct nvkm_oclass base; + /* main context generation function */ + void (*main)(struct gf100_gr_priv *, struct gf100_grctx *); + /* context-specific modify-on-first-load list generation function */ + void (*unkn)(struct gf100_gr_priv *); + /* mmio context data */ + const struct gf100_gr_pack *hub; + const struct gf100_gr_pack *gpc; + 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; + /* 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; +}; + +static inline const struct gf100_grctx_oclass * +gf100_grctx_impl(struct gf100_gr_priv *priv) +{ + return (void *)nv_engine(priv)->cclass; +} + +extern struct nvkm_oclass *gf100_grctx_oclass; +int gf100_grctx_generate(struct gf100_gr_priv *); +void gf100_grctx_generate_main(struct gf100_gr_priv *, 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_priv *); +void gf100_grctx_generate_tpcid(struct gf100_gr_priv *); +void gf100_grctx_generate_r406028(struct gf100_gr_priv *); +void gf100_grctx_generate_r4060a8(struct gf100_gr_priv *); +void gf100_grctx_generate_r418bb8(struct gf100_gr_priv *); +void gf100_grctx_generate_r406800(struct gf100_gr_priv *); + +extern struct nvkm_oclass *gf108_grctx_oclass; +void gf108_grctx_generate_attrib(struct gf100_grctx *); +void gf108_grctx_generate_unkn(struct gf100_gr_priv *); + +extern struct nvkm_oclass *gf104_grctx_oclass; +extern struct nvkm_oclass *gf110_grctx_oclass; + +extern struct nvkm_oclass *gf117_grctx_oclass; +void gf117_grctx_generate_attrib(struct gf100_grctx *); + +extern struct nvkm_oclass *gf119_grctx_oclass; + +extern struct nvkm_oclass *gk104_grctx_oclass; +extern struct nvkm_oclass *gk20a_grctx_oclass; +void gk104_grctx_generate_main(struct gf100_gr_priv *, struct gf100_grctx *); +void gk104_grctx_generate_bundle(struct gf100_grctx *); +void gk104_grctx_generate_pagepool(struct gf100_grctx *); +void gk104_grctx_generate_unkn(struct gf100_gr_priv *); +void gk104_grctx_generate_r418bb8(struct gf100_gr_priv *); +void gk104_grctx_generate_rop_active_fbps(struct gf100_gr_priv *); + + +extern struct nvkm_oclass *gk110_grctx_oclass; +extern struct nvkm_oclass *gk110b_grctx_oclass; +extern struct nvkm_oclass *gk208_grctx_oclass; + +extern struct nvkm_oclass *gm107_grctx_oclass; +void gm107_grctx_generate_bundle(struct gf100_grctx *); +void gm107_grctx_generate_pagepool(struct gf100_grctx *); +void gm107_grctx_generate_attrib(struct gf100_grctx *); + +extern struct nvkm_oclass *gm204_grctx_oclass; +void gm204_grctx_generate_main(struct gf100_gr_priv *, struct gf100_grctx *); + +extern struct nvkm_oclass *gm206_grctx_oclass; + +/* 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[]; +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_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_gpc[]; +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[]; +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[]; + +extern const struct gf100_gr_pack gm204_grctx_pack_icmd[]; + +extern const struct gf100_gr_pack gm204_grctx_pack_mthd[]; + +extern const struct gf100_gr_pack gm204_grctx_pack_hub[]; + +extern const struct gf100_gr_init gm204_grctx_init_prop_0[]; +extern const struct gf100_gr_init gm204_grctx_init_setup_0[]; +extern const struct gf100_gr_init gm204_grctx_init_gpm_0[]; +extern const struct gf100_gr_init gm204_grctx_init_gpc_unk_2[]; + +extern const struct gf100_gr_pack gm204_grctx_pack_tpc[]; + +extern const struct gf100_gr_pack gm204_grctx_pack_ppc[]; +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf104.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf104.c new file mode 100644 index 000000000..c5a8d55e2 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf104.c @@ -0,0 +1,108 @@ +/* + * 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 + ******************************************************************************/ + +struct nvkm_oclass * +gf104_grctx_oclass = &(struct gf100_grctx_oclass) { + .base.handle = NV_ENGCTX(GR, 0xc3), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_gr_context_ctor, + .dtor = gf100_gr_context_dtor, + .init = _nvkm_gr_context_init, + .fini = _nvkm_gr_context_fini, + .rd32 = _nvkm_gr_context_rd32, + .wr32 = _nvkm_gr_context_wr32, + }, + .main = gf100_grctx_generate_main, + .unkn = gf100_grctx_generate_unkn, + .hub = gf100_grctx_pack_hub, + .gpc = gf100_grctx_pack_gpc, + .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, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf108.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf108.c new file mode 100644 index 000000000..87c844a5f --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf108.c @@ -0,0 +1,806 @@ +/* + * 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[] = { + { 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 }, + { 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_priv *priv = info->priv; + const struct gf100_grctx_oclass *impl = gf100_grctx_impl(priv); + const u32 alpha = impl->alpha_nr; + const u32 beta = impl->attrib_nr; + const u32 size = 0x20 * (impl->attrib_nr_max + impl->alpha_nr_max); + const u32 access = NV_MEM_ACCESS_RW; + const int s = 12; + const int b = mmio_vram(info, size * priv->tpc_total, (1 << s), access); + const int timeslice_mode = 1; + const int max_batches = 0xffff; + u32 bo = 0; + u32 ao = bo + impl->attrib_nr_max * priv->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 < priv->gpc_nr; gpc++) { + for (tpc = 0; tpc < priv->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 += impl->attrib_nr_max; + mmio_wr32(info, o + 0x44, (a << 16) | ao); + ao += impl->alpha_nr_max; + } + } +} + +void +gf108_grctx_generate_unkn(struct gf100_gr_priv *priv) +{ + nv_mask(priv, 0x418c6c, 0x00000001, 0x00000001); + nv_mask(priv, 0x41980c, 0x00000010, 0x00000010); + nv_mask(priv, 0x419814, 0x00000004, 0x00000004); + nv_mask(priv, 0x4064c0, 0x80000000, 0x80000000); + nv_mask(priv, 0x405800, 0x08000000, 0x08000000); + nv_mask(priv, 0x419c00, 0x00000008, 0x00000008); +} + +struct nvkm_oclass * +gf108_grctx_oclass = &(struct gf100_grctx_oclass) { + .base.handle = NV_ENGCTX(GR, 0xc1), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_gr_context_ctor, + .dtor = gf100_gr_context_dtor, + .init = _nvkm_gr_context_init, + .fini = _nvkm_gr_context_fini, + .rd32 = _nvkm_gr_context_rd32, + .wr32 = _nvkm_gr_context_wr32, + }, + .main = gf100_grctx_generate_main, + .unkn = gf108_grctx_generate_unkn, + .hub = gf108_grctx_pack_hub, + .gpc = gf108_grctx_pack_gpc, + .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, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf110.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf110.c new file mode 100644 index 000000000..b3acd931b --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf110.c @@ -0,0 +1,359 @@ +/* + * 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[] = { + { 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 }, + { gf100_grctx_init_crstr_0 }, + { gf100_grctx_init_gpm_0 }, + { gf100_grctx_init_gcc_0 }, + {} +}; + +/******************************************************************************* + * PGRAPH context implementation + ******************************************************************************/ + +struct nvkm_oclass * +gf110_grctx_oclass = &(struct gf100_grctx_oclass) { + .base.handle = NV_ENGCTX(GR, 0xc8), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_gr_context_ctor, + .dtor = gf100_gr_context_dtor, + .init = _nvkm_gr_context_init, + .fini = _nvkm_gr_context_fini, + .rd32 = _nvkm_gr_context_rd32, + .wr32 = _nvkm_gr_context_wr32, + }, + .main = gf100_grctx_generate_main, + .unkn = gf100_grctx_generate_unkn, + .hub = gf100_grctx_pack_hub, + .gpc = gf110_grctx_pack_gpc, + .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, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf117.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf117.c new file mode 100644 index 000000000..9bbe2c975 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf117.c @@ -0,0 +1,284 @@ +/* + * 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[] = { + { 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 }, + { 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_attrib(struct gf100_grctx *info) +{ + struct gf100_gr_priv *priv = info->priv; + const struct gf100_grctx_oclass *impl = gf100_grctx_impl(priv); + const u32 alpha = impl->alpha_nr; + const u32 beta = impl->attrib_nr; + const u32 size = 0x20 * (impl->attrib_nr_max + impl->alpha_nr_max); + const u32 access = NV_MEM_ACCESS_RW; + const int s = 12; + const int b = mmio_vram(info, size * priv->tpc_total, (1 << s), access); + const int timeslice_mode = 1; + const int max_batches = 0xffff; + u32 bo = 0; + u32 ao = bo + impl->attrib_nr_max * priv->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 < priv->gpc_nr; gpc++) { + for (ppc = 0; ppc < priv->ppc_nr[gpc]; ppc++) { + const u32 a = alpha * priv->ppc_tpc_nr[gpc][ppc]; + const u32 b = beta * priv->ppc_tpc_nr[gpc][ppc]; + const u32 t = timeslice_mode; + const u32 o = PPC_UNIT(gpc, ppc, 0); + mmio_skip(info, o + 0xc0, (t << 28) | (b << 16) | ++bo); + mmio_wr32(info, o + 0xc0, (t << 28) | (b << 16) | --bo); + bo += impl->attrib_nr_max * priv->ppc_tpc_nr[gpc][ppc]; + mmio_wr32(info, o + 0xe4, (a << 16) | ao); + ao += impl->alpha_nr_max * priv->ppc_tpc_nr[gpc][ppc]; + } + } +} + +void +gf117_grctx_generate_main(struct gf100_gr_priv *priv, struct gf100_grctx *info) +{ + struct gf100_grctx_oclass *oclass = (void *)nv_engine(priv)->cclass; + int i; + + nvkm_mc(priv)->unk260(nvkm_mc(priv), 0); + + gf100_gr_mmio(priv, oclass->hub); + gf100_gr_mmio(priv, oclass->gpc); + gf100_gr_mmio(priv, oclass->zcull); + gf100_gr_mmio(priv, oclass->tpc); + gf100_gr_mmio(priv, oclass->ppc); + + nv_wr32(priv, 0x404154, 0x00000000); + + oclass->bundle(info); + oclass->pagepool(info); + oclass->attrib(info); + oclass->unkn(priv); + + gf100_grctx_generate_tpcid(priv); + gf100_grctx_generate_r406028(priv); + gf100_grctx_generate_r4060a8(priv); + gk104_grctx_generate_r418bb8(priv); + gf100_grctx_generate_r406800(priv); + + for (i = 0; i < 8; i++) + nv_wr32(priv, 0x4064d0 + (i * 0x04), 0x00000000); + + gf100_gr_icmd(priv, oclass->icmd); + nv_wr32(priv, 0x404154, 0x00000400); + gf100_gr_mthd(priv, oclass->mthd); + nvkm_mc(priv)->unk260(nvkm_mc(priv), 1); +} + +struct nvkm_oclass * +gf117_grctx_oclass = &(struct gf100_grctx_oclass) { + .base.handle = NV_ENGCTX(GR, 0xd7), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_gr_context_ctor, + .dtor = gf100_gr_context_dtor, + .init = _nvkm_gr_context_init, + .fini = _nvkm_gr_context_fini, + .rd32 = _nvkm_gr_context_rd32, + .wr32 = _nvkm_gr_context_wr32, + }, + .main = gf117_grctx_generate_main, + .unkn = gk104_grctx_generate_unkn, + .hub = gf117_grctx_pack_hub, + .gpc = gf117_grctx_pack_gpc, + .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, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf119.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf119.c new file mode 100644 index 000000000..8d8761443 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf119.c @@ -0,0 +1,529 @@ +/* + * 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[] = { + { 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 }, + { gf119_grctx_init_crstr_0 }, + { gf108_grctx_init_gpm_0 }, + { gf100_grctx_init_gcc_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 + ******************************************************************************/ + +struct nvkm_oclass * +gf119_grctx_oclass = &(struct gf100_grctx_oclass) { + .base.handle = NV_ENGCTX(GR, 0xd9), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_gr_context_ctor, + .dtor = gf100_gr_context_dtor, + .init = _nvkm_gr_context_init, + .fini = _nvkm_gr_context_fini, + .rd32 = _nvkm_gr_context_rd32, + .wr32 = _nvkm_gr_context_wr32, + }, + .main = gf100_grctx_generate_main, + .unkn = gf108_grctx_generate_unkn, + .hub = gf119_grctx_pack_hub, + .gpc = gf119_grctx_pack_gpc, + .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, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk104.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk104.c new file mode 100644 index 000000000..b12f6a9fd --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk104.c @@ -0,0 +1,1024 @@ +/* + * 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 }, + {} +}; + +const struct gf100_gr_pack +gk104_grctx_pack_gpc[] = { + { 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 }, + { 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_bundle(struct gf100_grctx *info) +{ + const struct gf100_grctx_oclass *impl = gf100_grctx_impl(info->priv); + const u32 state_limit = min(impl->bundle_min_gpm_fifo_depth, + impl->bundle_size / 0x20); + const u32 token_limit = impl->bundle_token_limit; + const u32 access = NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS; + const int s = 8; + const int b = mmio_vram(info, impl->bundle_size, (1 << s), access); + mmio_refn(info, 0x408004, 0x00000000, s, b); + mmio_wr32(info, 0x408008, 0x80000000 | (impl->bundle_size >> s)); + mmio_refn(info, 0x418808, 0x00000000, s, b); + mmio_wr32(info, 0x41880c, 0x80000000 | (impl->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_oclass *impl = gf100_grctx_impl(info->priv); + const u32 access = NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS; + const int s = 8; + const int b = mmio_vram(info, impl->pagepool_size, (1 << s), access); + 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_priv *priv) +{ + nv_mask(priv, 0x418c6c, 0x00000001, 0x00000001); + nv_mask(priv, 0x41980c, 0x00000010, 0x00000010); + nv_mask(priv, 0x41be08, 0x00000004, 0x00000004); + nv_mask(priv, 0x4064c0, 0x80000000, 0x80000000); + nv_mask(priv, 0x405800, 0x08000000, 0x08000000); + nv_mask(priv, 0x419c00, 0x00000008, 0x00000008); +} + +void +gk104_grctx_generate_r418bb8(struct gf100_gr_priv *priv) +{ + u32 data[6] = {}, data2[2] = {}; + u8 tpcnr[GPC_MAX]; + u8 shift, ntpcv; + int gpc, tpc, i; + + /* calculate first set of magics */ + memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr)); + + gpc = -1; + for (tpc = 0; tpc < priv->tpc_total; tpc++) { + do { + gpc = (gpc + 1) % priv->gpc_nr; + } while (!tpcnr[gpc]); + tpcnr[gpc]--; + + data[tpc / 6] |= gpc << ((tpc % 6) * 5); + } + + for (; tpc < 32; tpc++) + data[tpc / 6] |= 7 << ((tpc % 6) * 5); + + /* and the second... */ + shift = 0; + ntpcv = priv->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 */ + nv_wr32(priv, 0x418bb8, (priv->tpc_total << 8) | + priv->magic_not_rop_nr); + for (i = 0; i < 6; i++) + nv_wr32(priv, 0x418b08 + (i * 4), data[i]); + + /* GPC_BROADCAST.TP_BROADCAST */ + nv_wr32(priv, 0x41bfd0, (priv->tpc_total << 8) | + priv->magic_not_rop_nr | data2[0]); + nv_wr32(priv, 0x41bfe4, data2[1]); + for (i = 0; i < 6; i++) + nv_wr32(priv, 0x41bf00 + (i * 4), data[i]); + + /* UNK78xx */ + nv_wr32(priv, 0x4078bc, (priv->tpc_total << 8) | + priv->magic_not_rop_nr); + for (i = 0; i < 6; i++) + nv_wr32(priv, 0x40780c + (i * 4), data[i]); +} + +void +gk104_grctx_generate_rop_active_fbps(struct gf100_gr_priv *priv) +{ + const u32 fbp_count = nv_rd32(priv, 0x120074); + nv_mask(priv, 0x408850, 0x0000000f, fbp_count); /* zrop */ + nv_mask(priv, 0x408958, 0x0000000f, fbp_count); /* crop */ +} + +void +gk104_grctx_generate_main(struct gf100_gr_priv *priv, struct gf100_grctx *info) +{ + struct gf100_grctx_oclass *oclass = (void *)nv_engine(priv)->cclass; + int i; + + nvkm_mc(priv)->unk260(nvkm_mc(priv), 0); + + gf100_gr_mmio(priv, oclass->hub); + gf100_gr_mmio(priv, oclass->gpc); + gf100_gr_mmio(priv, oclass->zcull); + gf100_gr_mmio(priv, oclass->tpc); + gf100_gr_mmio(priv, oclass->ppc); + + nv_wr32(priv, 0x404154, 0x00000000); + + oclass->bundle(info); + oclass->pagepool(info); + oclass->attrib(info); + oclass->unkn(priv); + + gf100_grctx_generate_tpcid(priv); + gf100_grctx_generate_r406028(priv); + gk104_grctx_generate_r418bb8(priv); + gf100_grctx_generate_r406800(priv); + + for (i = 0; i < 8; i++) + nv_wr32(priv, 0x4064d0 + (i * 0x04), 0x00000000); + + nv_wr32(priv, 0x405b00, (priv->tpc_total << 8) | priv->gpc_nr); + gk104_grctx_generate_rop_active_fbps(priv); + nv_mask(priv, 0x419f78, 0x00000001, 0x00000000); + + gf100_gr_icmd(priv, oclass->icmd); + nv_wr32(priv, 0x404154, 0x00000400); + gf100_gr_mthd(priv, oclass->mthd); + nvkm_mc(priv)->unk260(nvkm_mc(priv), 1); + + nv_mask(priv, 0x418800, 0x00200000, 0x00200000); + nv_mask(priv, 0x41be10, 0x00800000, 0x00800000); +} + +struct nvkm_oclass * +gk104_grctx_oclass = &(struct gf100_grctx_oclass) { + .base.handle = NV_ENGCTX(GR, 0xe4), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_gr_context_ctor, + .dtor = gf100_gr_context_dtor, + .init = _nvkm_gr_context_init, + .fini = _nvkm_gr_context_fini, + .rd32 = _nvkm_gr_context_rd32, + .wr32 = _nvkm_gr_context_wr32, + }, + .main = gk104_grctx_generate_main, + .unkn = gk104_grctx_generate_unkn, + .hub = gk104_grctx_pack_hub, + .gpc = gk104_grctx_pack_gpc, + .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, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk110.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk110.c new file mode 100644 index 000000000..b3f58be04 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk110.c @@ -0,0 +1,842 @@ +/* + * 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[] = { + { 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 }, + { 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 + ******************************************************************************/ + +struct nvkm_oclass * +gk110_grctx_oclass = &(struct gf100_grctx_oclass) { + .base.handle = NV_ENGCTX(GR, 0xf0), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_gr_context_ctor, + .dtor = gf100_gr_context_dtor, + .init = _nvkm_gr_context_init, + .fini = _nvkm_gr_context_fini, + .rd32 = _nvkm_gr_context_rd32, + .wr32 = _nvkm_gr_context_wr32, + }, + .main = gk104_grctx_generate_main, + .unkn = gk104_grctx_generate_unkn, + .hub = gk110_grctx_pack_hub, + .gpc = gk110_grctx_pack_gpc, + .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, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk110b.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk110b.c new file mode 100644 index 000000000..b11c26794 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk110b.c @@ -0,0 +1,103 @@ +/* + * 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 + ******************************************************************************/ + +struct nvkm_oclass * +gk110b_grctx_oclass = &(struct gf100_grctx_oclass) { + .base.handle = NV_ENGCTX(GR, 0xf1), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_gr_context_ctor, + .dtor = gf100_gr_context_dtor, + .init = _nvkm_gr_context_init, + .fini = _nvkm_gr_context_fini, + .rd32 = _nvkm_gr_context_rd32, + .wr32 = _nvkm_gr_context_wr32, + }, + .main = gk104_grctx_generate_main, + .unkn = gk104_grctx_generate_unkn, + .hub = gk110_grctx_pack_hub, + .gpc = gk110_grctx_pack_gpc, + .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, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk208.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk208.c new file mode 100644 index 000000000..6e8ce9fc3 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk208.c @@ -0,0 +1,564 @@ +/* + * 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[] = { + { 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 }, + { 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 + ******************************************************************************/ + +struct nvkm_oclass * +gk208_grctx_oclass = &(struct gf100_grctx_oclass) { + .base.handle = NV_ENGCTX(GR, 0x08), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_gr_context_ctor, + .dtor = gf100_gr_context_dtor, + .init = _nvkm_gr_context_init, + .fini = _nvkm_gr_context_fini, + .rd32 = _nvkm_gr_context_rd32, + .wr32 = _nvkm_gr_context_wr32, + }, + .main = gk104_grctx_generate_main, + .unkn = gk104_grctx_generate_unkn, + .hub = gk208_grctx_pack_hub, + .gpc = gk208_grctx_pack_gpc, + .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, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk20a.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk20a.c new file mode 100644 index 000000000..2f241f6f0 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk20a.c @@ -0,0 +1,62 @@ +/* + * 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 "ctxgf100.h" + +static const struct gf100_gr_pack +gk20a_grctx_pack_mthd[] = { + { gk104_grctx_init_a097_0, 0xa297 }, + { gf100_grctx_init_902d_0, 0x902d }, + {} +}; + +struct nvkm_oclass * +gk20a_grctx_oclass = &(struct gf100_grctx_oclass) { + .base.handle = NV_ENGCTX(GR, 0xea), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_gr_context_ctor, + .dtor = gf100_gr_context_dtor, + .init = _nvkm_gr_context_init, + .fini = _nvkm_gr_context_fini, + .rd32 = _nvkm_gr_context_rd32, + .wr32 = _nvkm_gr_context_wr32, + }, + .main = gk104_grctx_generate_main, + .unkn = gk104_grctx_generate_unkn, + .hub = gk104_grctx_pack_hub, + .gpc = gk104_grctx_pack_gpc, + .zcull = gf100_grctx_pack_zcull, + .tpc = gk104_grctx_pack_tpc, + .ppc = gk104_grctx_pack_ppc, + .icmd = gk104_grctx_pack_icmd, + .mthd = gk20a_grctx_pack_mthd, + .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, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgm107.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgm107.c new file mode 100644 index 000000000..fbeaae3ae --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgm107.c @@ -0,0 +1,1028 @@ +/* + * 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[] = { + { 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 }, + { 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 + ******************************************************************************/ + +void +gm107_grctx_generate_bundle(struct gf100_grctx *info) +{ + const struct gf100_grctx_oclass *impl = gf100_grctx_impl(info->priv); + const u32 state_limit = min(impl->bundle_min_gpm_fifo_depth, + impl->bundle_size / 0x20); + const u32 token_limit = impl->bundle_token_limit; + const u32 access = NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS; + const int s = 8; + const int b = mmio_vram(info, impl->bundle_size, (1 << s), access); + mmio_refn(info, 0x408004, 0x00000000, s, b); + mmio_wr32(info, 0x408008, 0x80000000 | (impl->bundle_size >> s)); + mmio_refn(info, 0x418e24, 0x00000000, s, b); + mmio_wr32(info, 0x418e28, 0x80000000 | (impl->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_oclass *impl = gf100_grctx_impl(info->priv); + const u32 access = NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS; + const int s = 8; + const int b = mmio_vram(info, impl->pagepool_size, (1 << s), access); + 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_priv *priv = info->priv; + const struct gf100_grctx_oclass *impl = (void *)gf100_grctx_impl(priv); + const u32 alpha = impl->alpha_nr; + const u32 attrib = impl->attrib_nr; + const u32 size = 0x20 * (impl->attrib_nr_max + impl->alpha_nr_max); + const u32 access = NV_MEM_ACCESS_RW; + const int s = 12; + const int b = mmio_vram(info, size * priv->tpc_total, (1 << s), access); + const int max_batches = 0xffff; + u32 bo = 0; + u32 ao = bo + impl->attrib_nr_max * priv->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 < priv->gpc_nr; gpc++) { + for (ppc = 0; ppc < priv->ppc_nr[gpc]; ppc++, n++) { + const u32 as = alpha * priv->ppc_tpc_nr[gpc][ppc]; + const u32 bs = attrib * priv->ppc_tpc_nr[gpc][ppc]; + const u32 u = 0x418ea0 + (n * 0x04); + const u32 o = PPC_UNIT(gpc, ppc, 0); + mmio_wr32(info, o + 0xc0, bs); + mmio_wr32(info, o + 0xf4, bo); + bo += impl->attrib_nr_max * priv->ppc_tpc_nr[gpc][ppc]; + mmio_wr32(info, o + 0xe4, as); + mmio_wr32(info, o + 0xf8, ao); + ao += impl->alpha_nr_max * priv->ppc_tpc_nr[gpc][ppc]; + mmio_wr32(info, u, ((bs / 3 /*XXX*/) << 16) | bs); + } + } +} + +static void +gm107_grctx_generate_tpcid(struct gf100_gr_priv *priv) +{ + int gpc, tpc, id; + + for (tpc = 0, id = 0; tpc < 4; tpc++) { + for (gpc = 0; gpc < priv->gpc_nr; gpc++) { + if (tpc < priv->tpc_nr[gpc]) { + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x698), id); + nv_wr32(priv, GPC_UNIT(gpc, 0x0c10 + tpc * 4), id); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x088), id); + id++; + } + + nv_wr32(priv, GPC_UNIT(gpc, 0x0c08), priv->tpc_nr[gpc]); + nv_wr32(priv, GPC_UNIT(gpc, 0x0c8c), priv->tpc_nr[gpc]); + } + } +} + +static void +gm107_grctx_generate_main(struct gf100_gr_priv *priv, struct gf100_grctx *info) +{ + struct gf100_grctx_oclass *oclass = (void *)nv_engine(priv)->cclass; + int i; + + gf100_gr_mmio(priv, oclass->hub); + gf100_gr_mmio(priv, oclass->gpc); + gf100_gr_mmio(priv, oclass->zcull); + gf100_gr_mmio(priv, oclass->tpc); + gf100_gr_mmio(priv, oclass->ppc); + + nv_wr32(priv, 0x404154, 0x00000000); + + oclass->bundle(info); + oclass->pagepool(info); + oclass->attrib(info); + oclass->unkn(priv); + + gm107_grctx_generate_tpcid(priv); + gf100_grctx_generate_r406028(priv); + gk104_grctx_generate_r418bb8(priv); + gf100_grctx_generate_r406800(priv); + + nv_wr32(priv, 0x4064d0, 0x00000001); + for (i = 1; i < 8; i++) + nv_wr32(priv, 0x4064d0 + (i * 0x04), 0x00000000); + nv_wr32(priv, 0x406500, 0x00000001); + + nv_wr32(priv, 0x405b00, (priv->tpc_total << 8) | priv->gpc_nr); + + gk104_grctx_generate_rop_active_fbps(priv); + + gf100_gr_icmd(priv, oclass->icmd); + nv_wr32(priv, 0x404154, 0x00000400); + gf100_gr_mthd(priv, oclass->mthd); + + nv_mask(priv, 0x419e00, 0x00808080, 0x00808080); + nv_mask(priv, 0x419ccc, 0x80000000, 0x80000000); + nv_mask(priv, 0x419f80, 0x80000000, 0x80000000); + nv_mask(priv, 0x419f88, 0x80000000, 0x80000000); +} + +struct nvkm_oclass * +gm107_grctx_oclass = &(struct gf100_grctx_oclass) { + .base.handle = NV_ENGCTX(GR, 0x08), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_gr_context_ctor, + .dtor = gf100_gr_context_dtor, + .init = _nvkm_gr_context_init, + .fini = _nvkm_gr_context_fini, + .rd32 = _nvkm_gr_context_rd32, + .wr32 = _nvkm_gr_context_wr32, + }, + .main = gm107_grctx_generate_main, + .unkn = gk104_grctx_generate_unkn, + .hub = gm107_grctx_pack_hub, + .gpc = gm107_grctx_pack_gpc, + .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, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgm204.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgm204.c new file mode 100644 index 000000000..ea8e66151 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgm204.c @@ -0,0 +1,1054 @@ +/* + * 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 register lists + ******************************************************************************/ + +static const struct gf100_gr_init +gm204_grctx_init_icmd_0[] = { + { 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 }, + { 0x000374, 1, 0x01, 0x00000100 }, + { 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 }, + { 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 }, + { 0x0000b4, 4, 0x01, 0x88888888 }, + { 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 }, + { 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 }, + { 0x000374, 1, 0x01, 0x00000100 }, + { 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 }, + { 0x0005aa, 1, 0x01, 0x00000002 }, + { 0x0005cb, 1, 0x01, 0x00000004 }, + { 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 }, + { 0x0005e0, 16, 0x01, 0x00000d10 }, + { 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 }, + { 0x0005d4, 1, 0x01, 0x00000001 }, + { 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 }, + { 0x000674, 1, 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 }, + { 0x00c413, 4, 0x01, 0x88888888 }, + { 0x00c423, 1, 0x01, 0x0000ff00 }, + { 0x00c420, 1, 0x01, 0x00880101 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + {} +}; + +const struct gf100_gr_pack +gm204_grctx_pack_icmd[] = { + { gm204_grctx_init_icmd_0 }, + {} +}; + +static const struct gf100_gr_init +gm204_grctx_init_b197_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 }, + { 0x000a18, 16, 0x20, 0x00006420 }, + { 0x000a1c, 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 }, + { 0x001208, 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 }, + { 0x0011e0, 4, 0x04, 0x88888888 }, + { 0x00153c, 1, 0x04, 0x00000000 }, + { 0x0016b4, 1, 0x04, 0x00000003 }, + { 0x000fa4, 1, 0x04, 0x00000001 }, + { 0x000fbc, 4, 0x04, 0x0000ffff }, + { 0x000fa8, 1, 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 }, + { 0x00113c, 1, 0x04, 0x00000000 }, + { 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 }, + { 0x001004, 1, 0x04, 0x00000000 }, + { 0x001240, 8, 0x04, 0x00000000 }, + { 0x00037c, 1, 0x04, 0x00000001 }, + { 0x000740, 1, 0x04, 0x00000000 }, + { 0x001148, 1, 0x04, 0x00000000 }, + { 0x000fb4, 1, 0x04, 0x00000000 }, + { 0x000fb8, 1, 0x04, 0x00000002 }, + { 0x001130, 1, 0x04, 0x00000002 }, + { 0x000fd4, 2, 0x04, 0x00000000 }, + { 0x001030, 1, 0x04, 0x20181008 }, + { 0x001034, 1, 0x04, 0x40383028 }, + { 0x001038, 1, 0x04, 0x60585048 }, + { 0x00103c, 1, 0x04, 0x80787068 }, + { 0x000744, 1, 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 }, + { 0x001134, 1, 0x04, 0x00000000 }, + { 0x000f1c, 1, 0x04, 0x00000000 }, + { 0x0011f8, 1, 0x04, 0x00000000 }, + { 0x001138, 1, 0x04, 0x00000001 }, + { 0x000300, 1, 0x04, 0x00000001 }, + { 0x0013a8, 1, 0x04, 0x00000000 }, + { 0x001224, 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 }, + { 0x00111c, 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 }, + {} +}; + +const struct gf100_gr_pack +gm204_grctx_pack_mthd[] = { + { gm204_grctx_init_b197_0, 0xb197 }, + { gf100_grctx_init_902d_0, 0x902d }, + {} +}; + +static const struct gf100_gr_init +gm204_grctx_init_fe_0[] = { + { 0x404004, 8, 0x04, 0x00000000 }, + { 0x404024, 1, 0x04, 0x0000e000 }, + { 0x404028, 8, 0x04, 0x00000000 }, + { 0x4040a8, 8, 0x04, 0x00000000 }, + { 0x4040c8, 1, 0x04, 0xf801008f }, + { 0x4040d0, 6, 0x04, 0x00000000 }, + { 0x4040f8, 1, 0x04, 0x00000000 }, + { 0x404100, 10, 0x04, 0x00000000 }, + { 0x404130, 2, 0x04, 0x00000000 }, + { 0x404150, 1, 0x04, 0x0000002e }, + { 0x404154, 2, 0x04, 0x00000800 }, + { 0x404164, 1, 0x04, 0x00000045 }, + { 0x40417c, 2, 0x04, 0x00000000 }, + { 0x404194, 1, 0x04, 0x33000700 }, + { 0x4041a0, 4, 0x04, 0x00000000 }, + { 0x4041c4, 2, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gm204_grctx_init_ds_0[] = { + { 0x405800, 1, 0x04, 0x8f8001bf }, + { 0x405830, 1, 0x04, 0x04001000 }, + { 0x405834, 1, 0x04, 0x08000000 }, + { 0x405838, 1, 0x04, 0x00010000 }, + { 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 +gm204_grctx_init_cwd_0[] = { + { 0x405b00, 1, 0x04, 0x00000000 }, + { 0x405b10, 1, 0x04, 0x00001000 }, + { 0x405b20, 1, 0x04, 0x04000000 }, + { 0x405b60, 6, 0x04, 0x00000000 }, + { 0x405ba0, 6, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gm204_grctx_init_pd_0[] = { + { 0x406020, 1, 0x04, 0x17410001 }, + { 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, 0x01800780 }, + { 0x4064cc, 9, 0x04, 0x00000000 }, + { 0x4064fc, 1, 0x04, 0x0000022a }, + { 0x406500, 1, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gm204_grctx_init_be_0[] = { + { 0x408800, 1, 0x04, 0x32882a3c }, + { 0x408804, 1, 0x04, 0x00000040 }, + { 0x408808, 1, 0x04, 0x1003e005 }, + { 0x408840, 1, 0x04, 0x00000e0b }, + { 0x408900, 1, 0x04, 0xb080b801 }, + { 0x408904, 1, 0x04, 0x63038001 }, + { 0x408908, 1, 0x04, 0x12c8502f }, + { 0x408980, 1, 0x04, 0x0000011d }, + {} +}; + +const struct gf100_gr_pack +gm204_grctx_pack_hub[] = { + { gf100_grctx_init_main_0 }, + { gm204_grctx_init_fe_0 }, + { gk110_grctx_init_pri_0 }, + { gk104_grctx_init_memfmt_0 }, + { gm204_grctx_init_ds_0 }, + { gm204_grctx_init_cwd_0 }, + { gm204_grctx_init_pd_0 }, + { gk208_grctx_init_rstr2d_0 }, + { gk104_grctx_init_scc_0 }, + { gm204_grctx_init_be_0 }, + {} +}; + +const struct gf100_gr_init +gm204_grctx_init_prop_0[] = { + { 0x418400, 1, 0x04, 0x38e01e00 }, + { 0x418404, 1, 0x04, 0x70001fff }, + { 0x41840c, 1, 0x04, 0x20001008 }, + { 0x418410, 2, 0x04, 0x0fff0fff }, + { 0x418418, 1, 0x04, 0x07ff07ff }, + { 0x41841c, 1, 0x04, 0x3feffbff }, + { 0x418450, 6, 0x04, 0x00000000 }, + { 0x418468, 1, 0x04, 0x00000001 }, + { 0x41846c, 2, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gm204_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 }, + { 0x418728, 1, 0x04, 0x00010000 }, + {} +}; + +const struct gf100_gr_init +gm204_grctx_init_setup_0[] = { + { 0x418800, 1, 0x04, 0x7006863a }, + { 0x418808, 1, 0x04, 0x00000000 }, + { 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 +gm204_grctx_init_gpm_0[] = { + { 0x418c10, 8, 0x04, 0x00000000 }, + { 0x418c40, 1, 0x04, 0xffffffff }, + { 0x418c6c, 1, 0x04, 0x00000001 }, + { 0x418c80, 1, 0x04, 0x20200000 }, + {} +}; + +const struct gf100_gr_init +gm204_grctx_init_gpc_unk_2[] = { + { 0x418e00, 1, 0x04, 0x90040000 }, + { 0x418e24, 1, 0x04, 0x00000000 }, + { 0x418e28, 1, 0x04, 0x00000030 }, + { 0x418e2c, 1, 0x04, 0x00000100 }, + { 0x418e30, 3, 0x04, 0x00000000 }, + { 0x418e40, 22, 0x04, 0x00000000 }, + { 0x418ea0, 12, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_pack +gm204_grctx_pack_gpc[] = { + { gm107_grctx_init_gpc_unk_0 }, + { gm204_grctx_init_prop_0 }, + { gm204_grctx_init_gpc_unk_1 }, + { gm204_grctx_init_setup_0 }, + { gf100_grctx_init_zcull_0 }, + { gk208_grctx_init_crstr_0 }, + { gm204_grctx_init_gpm_0 }, + { gm204_grctx_init_gpc_unk_2 }, + { gf100_grctx_init_gcc_0 }, + {} +}; + +static const struct gf100_gr_init +gm204_grctx_init_pe_0[] = { + { 0x419848, 1, 0x04, 0x00000000 }, + { 0x419864, 1, 0x04, 0x00000029 }, + { 0x419888, 1, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gm204_grctx_init_tex_0[] = { + { 0x419a00, 1, 0x04, 0x000100f0 }, + { 0x419a04, 1, 0x04, 0x00000005 }, + { 0x419a08, 1, 0x04, 0x00000621 }, + { 0x419a0c, 1, 0x04, 0x00320000 }, + { 0x419a10, 1, 0x04, 0x00000000 }, + { 0x419a14, 1, 0x04, 0x00000200 }, + { 0x419a1c, 1, 0x04, 0x0010c000 }, + { 0x419a20, 1, 0x04, 0x20008a00 }, + { 0x419a30, 1, 0x04, 0x00000001 }, + { 0x419a3c, 1, 0x04, 0x0000181e }, + { 0x419ac4, 1, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gm204_grctx_init_mpc_0[] = { + { 0x419c00, 1, 0x04, 0x0000009a }, + { 0x419c04, 1, 0x04, 0x80000bd6 }, + { 0x419c08, 1, 0x04, 0x00000002 }, + { 0x419c20, 1, 0x04, 0x00000000 }, + { 0x419c24, 1, 0x04, 0x00084210 }, + { 0x419c28, 1, 0x04, 0x3efbefbe }, + { 0x419c2c, 1, 0x04, 0x00000000 }, + { 0x419c34, 1, 0x04, 0x71ff1ff3 }, + { 0x419c3c, 1, 0x04, 0x00001919 }, + { 0x419c50, 1, 0x04, 0x00000005 }, + {} +}; + +static const struct gf100_gr_init +gm204_grctx_init_l1c_0[] = { + { 0x419c84, 1, 0x04, 0x0000003e }, + { 0x419c90, 1, 0x04, 0x0000000a }, + {} +}; + +static const struct gf100_gr_init +gm204_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 }, + { 0x419e58, 6, 0x04, 0x00000000 }, + { 0x419e74, 10, 0x04, 0x00000000 }, + { 0x419eac, 1, 0x04, 0x0001cf8b }, + { 0x419eb0, 1, 0x04, 0x00030300 }, + { 0x419eb8, 1, 0x04, 0x40000000 }, + { 0x419ef0, 24, 0x04, 0x00000000 }, + { 0x419f68, 2, 0x04, 0x00000000 }, + { 0x419f70, 1, 0x04, 0x00000020 }, + { 0x419f78, 1, 0x04, 0x00010beb }, + { 0x419f7c, 1, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_pack +gm204_grctx_pack_tpc[] = { + { gm204_grctx_init_pe_0 }, + { gm204_grctx_init_tex_0 }, + { gm204_grctx_init_mpc_0 }, + { gm204_grctx_init_l1c_0 }, + { gm204_grctx_init_sm_0 }, + {} +}; + +static const struct gf100_gr_init +gm204_grctx_init_pes_0[] = { + { 0x41be24, 1, 0x04, 0x0000000e }, + {} +}; + +static const struct gf100_gr_init +gm204_grctx_init_cbm_0[] = { + { 0x41bec0, 1, 0x04, 0x00000000 }, + { 0x41bec4, 1, 0x04, 0x01030000 }, + { 0x41bee4, 1, 0x04, 0x00000000 }, + { 0x41bef0, 1, 0x04, 0x000003ff }, + { 0x41bef4, 2, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_pack +gm204_grctx_pack_ppc[] = { + { gm204_grctx_init_pes_0 }, + { gm204_grctx_init_cbm_0 }, + { gm107_grctx_init_wwdx_0 }, + {} +}; + +/******************************************************************************* + * PGRAPH context implementation + ******************************************************************************/ + +static void +gm204_grctx_generate_tpcid(struct gf100_gr_priv *priv) +{ + int gpc, tpc, id; + + for (tpc = 0, id = 0; tpc < 4; tpc++) { + for (gpc = 0; gpc < priv->gpc_nr; gpc++) { + if (tpc < priv->tpc_nr[gpc]) { + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x698), id); + nv_wr32(priv, GPC_UNIT(gpc, 0x0c10 + tpc * 4), id); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x088), id); + id++; + } + } + } +} + +static void +gm204_grctx_generate_rop_active_fbps(struct gf100_gr_priv *priv) +{ + const u32 fbp_count = nv_rd32(priv, 0x12006c); + nv_mask(priv, 0x408850, 0x0000000f, fbp_count); /* zrop */ + nv_mask(priv, 0x408958, 0x0000000f, fbp_count); /* crop */ +} + +static void +gm204_grctx_generate_405b60(struct gf100_gr_priv *priv) +{ + const u32 dist_nr = DIV_ROUND_UP(priv->tpc_total, 4); + u32 dist[TPC_MAX] = {}; + u32 gpcs[GPC_MAX] = {}; + u8 tpcnr[GPC_MAX]; + int tpc, gpc, i; + + memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr)); + + /* won't result in the same distribution as the binary driver where + * some of the gpcs have more tpcs than others, but this shall do + * for the moment. the code for earlier gpus has this issue too. + */ + for (gpc = -1, i = 0; i < priv->tpc_total; i++) { + do { + gpc = (gpc + 1) % priv->gpc_nr; + } while(!tpcnr[gpc]); + tpc = priv->tpc_nr[gpc] - tpcnr[gpc]--; + + dist[i / 4] |= ((gpc << 4) | tpc) << ((i % 4) * 8); + gpcs[gpc] |= i << (tpc * 8); + } + + for (i = 0; i < dist_nr; i++) + nv_wr32(priv, 0x405b60 + (i * 4), dist[i]); + for (i = 0; i < priv->gpc_nr; i++) + nv_wr32(priv, 0x405ba0 + (i * 4), gpcs[i]); +} + +void +gm204_grctx_generate_main(struct gf100_gr_priv *priv, struct gf100_grctx *info) +{ + struct gf100_grctx_oclass *oclass = (void *)nv_engine(priv)->cclass; + u32 tmp; + int i; + + gf100_gr_mmio(priv, oclass->hub); + gf100_gr_mmio(priv, oclass->gpc); + gf100_gr_mmio(priv, oclass->zcull); + gf100_gr_mmio(priv, oclass->tpc); + gf100_gr_mmio(priv, oclass->ppc); + + nv_wr32(priv, 0x404154, 0x00000000); + + oclass->bundle(info); + oclass->pagepool(info); + oclass->attrib(info); + oclass->unkn(priv); + + gm204_grctx_generate_tpcid(priv); + gf100_grctx_generate_r406028(priv); + gk104_grctx_generate_r418bb8(priv); + + for (i = 0; i < 8; i++) + nv_wr32(priv, 0x4064d0 + (i * 0x04), 0x00000000); + nv_wr32(priv, 0x406500, 0x00000000); + + nv_wr32(priv, 0x405b00, (priv->tpc_total << 8) | priv->gpc_nr); + + gm204_grctx_generate_rop_active_fbps(priv); + + for (tmp = 0, i = 0; i < priv->gpc_nr; i++) + tmp |= ((1 << priv->tpc_nr[i]) - 1) << (i * 4); + nv_wr32(priv, 0x4041c4, tmp); + + gm204_grctx_generate_405b60(priv); + + gf100_gr_icmd(priv, oclass->icmd); + nv_wr32(priv, 0x404154, 0x00000800); + gf100_gr_mthd(priv, oclass->mthd); + + nv_mask(priv, 0x418e94, 0xffffffff, 0xc4230000); + nv_mask(priv, 0x418e4c, 0xffffffff, 0x70000000); +} + +struct nvkm_oclass * +gm204_grctx_oclass = &(struct gf100_grctx_oclass) { + .base.handle = NV_ENGCTX(GR, 0x24), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_gr_context_ctor, + .dtor = gf100_gr_context_dtor, + .init = _nvkm_gr_context_init, + .fini = _nvkm_gr_context_fini, + .rd32 = _nvkm_gr_context_rd32, + .wr32 = _nvkm_gr_context_wr32, + }, + .main = gm204_grctx_generate_main, + .unkn = gk104_grctx_generate_unkn, + .hub = gm204_grctx_pack_hub, + .gpc = gm204_grctx_pack_gpc, + .zcull = gf100_grctx_pack_zcull, + .tpc = gm204_grctx_pack_tpc, + .ppc = gm204_grctx_pack_ppc, + .icmd = gm204_grctx_pack_icmd, + .mthd = gm204_grctx_pack_mthd, + .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, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgm206.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgm206.c new file mode 100644 index 000000000..91ec41617 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgm206.c @@ -0,0 +1,83 @@ +/* + * 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" + +static const struct gf100_gr_init +gm206_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 }, + { 0x418728, 1, 0x04, 0x00300020 }, + {} +}; + +static const struct gf100_gr_pack +gm206_grctx_pack_gpc[] = { + { gm107_grctx_init_gpc_unk_0 }, + { gm204_grctx_init_prop_0 }, + { gm206_grctx_init_gpc_unk_1 }, + { gm204_grctx_init_setup_0 }, + { gf100_grctx_init_zcull_0 }, + { gk208_grctx_init_crstr_0 }, + { gm204_grctx_init_gpm_0 }, + { gm204_grctx_init_gpc_unk_2 }, + { gf100_grctx_init_gcc_0 }, + {} +}; + +struct nvkm_oclass * +gm206_grctx_oclass = &(struct gf100_grctx_oclass) { + .base.handle = NV_ENGCTX(GR, 0x26), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_gr_context_ctor, + .dtor = gf100_gr_context_dtor, + .init = _nvkm_gr_context_init, + .fini = _nvkm_gr_context_fini, + .rd32 = _nvkm_gr_context_rd32, + .wr32 = _nvkm_gr_context_wr32, + }, + .main = gm204_grctx_generate_main, + .unkn = gk104_grctx_generate_unkn, + .hub = gm204_grctx_pack_hub, + .gpc = gm206_grctx_pack_gpc, + .zcull = gf100_grctx_pack_zcull, + .tpc = gm204_grctx_pack_tpc, + .ppc = gm204_grctx_pack_ppc, + .icmd = gm204_grctx_pack_icmd, + .mthd = gm204_grctx_pack_mthd, + .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, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxnv40.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxnv40.c new file mode 100644 index 000000000..dc31462af --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxnv40.c @@ -0,0 +1,694 @@ +/* + * 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" +#include + +/* 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) + nv_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) + nv_wo32(obj, (offset + b0_offset + i) * 4, 0x00000001); + for (i = 0; i < vs_nr_b1 * 4; i += 4) + nv_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, + .data = ctxprog, + .ctxprog_max = 256, + }; + + if (!ctxprog) + return -ENOMEM; + + nv40_grctx_generate(&ctx); + + nv_wr32(device, 0x400324, 0); + for (i = 0; i < ctx.ctxprog_len; i++) + nv_wr32(device, 0x400328, ctxprog[i]); + *size = ctx.ctxvals_pos * 4; + + kfree(ctxprog); + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxnv40.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxnv40.h new file mode 100644 index 000000000..8a8996195 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxnv40.h @@ -0,0 +1,129 @@ +#ifndef __NVKM_GRCTX_H__ +#define __NVKM_GRCTX_H__ +#include + +struct nvkm_grctx { + struct nvkm_device *device; + + enum { + NVKM_GRCTX_PROG, + NVKM_GRCTX_VALS + } mode; + void *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->data; + + 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->data; + 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; + + nv_wo32(ctx->data, reg * 4, val); +} +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxnv50.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxnv50.c new file mode 100644 index 000000000..9c9528d2c --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxnv50.c @@ -0,0 +1,3345 @@ +/* + * 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 +#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, + .data = ctxprog, + .ctxprog_max = 512, + }; + + if (!ctxprog) + return -ENOMEM; + nv50_grctx_generate(&ctx); + + nv_wr32(device, 0x400324, 0); + for (i = 0; i < ctx.ctxprog_len; i++) + nv_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 = nv_rd32 (ctx->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 (nvkm_fb(device)->ram->type != NV_MEM_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++) + nv_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++) + nv_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 = nv_rd32 (ctx->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 = nv_rd32 (ctx->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/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/com.fuc b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/com.fuc new file mode 100644 index 000000000..64208bf95 --- /dev/null +++ b/kernel/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/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpc.fuc b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpc.fuc new file mode 100644 index 000000000..194afe910 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpc.fuc @@ -0,0 +1,490 @@ +/* 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_wr32(addr,reg) /* +*/ mov b32 $r15 reg /* +*/ imm32($r14, addr) /* +*/ or $r14 NV_PGRAPH_GPCX_GPCCS_MMIO_CTRL_BASE_ENABLE /* +*/ 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 + imm32($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] + mov $r5 NV_PGRAPH_GPC0_TPC0 + ld b32 $r6 D[$r0 + #gpc_id] + shl b32 $r6 15 + add b32 $r5 $r6 + 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 +// +main: + bset $flags $p0 + sleep $p0 + mov $r13 #cmd_queue + call(queue_get) + bra $p1 #main + + // 0x0000-0x0003 are all context transfers + cmpu b32 $r14 0x04 + bra nc #main_not_ctx_xfer + // 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 $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 + 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/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgf100.fuc3 b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgf100.fuc3 new file mode 100644 index 000000000..7cf2bf9d9 --- /dev/null +++ b/kernel/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/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgf100.fuc3.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgf100.fuc3.h new file mode 100644 index 000000000..231f696d1 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgf100.fuc3.h @@ -0,0 +1,530 @@ +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, +}; + +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: main */ + 0xf404bd00, + 0x28f40031, + 0x1cd7f000, + 0xf43921f4, + 0xe4b0f401, + 0x1e18f404, + 0xf00181fe, + 0x20bd0627, + 0xb60412fd, + 0x1efd01e4, + 0x0018fe05, + 0x05b021f5, +/* 0x04eb: main_not_ctx_xfer */ + 0x94d30ef4, + 0xf5f010ef, + 0x7e21f501, + 0xc60ef403, +/* 0x04f8: ih */ + 0x88fe80f9, + 0xf980f901, + 0xf9a0f990, + 0xf9d0f9b0, + 0xbdf0f9e0, + 0x00a7f104, + 0x00a3f002, + 0xc400aacf, + 0x0bf404ab, + 0x1cd7f02c, + 0x1a00e7f1, + 0xcf00e3f0, + 0xf7f100ee, + 0xf3f01900, + 0x00ffcf00, + 0xf00421f4, + 0x07f101e7, + 0x03f01d00, + 0x000ed000, +/* 0x0546: ih_no_fifo */ + 0x07f104bd, + 0x03f00100, + 0x000ad000, + 0xf0fc04bd, + 0xd0fce0fc, + 0xa0fcb0fc, + 0x80fc90fc, + 0xfc0088fe, + 0x0032f480, +/* 0x056a: hub_barrier_done */ + 0xf7f001f8, + 0x040e9801, + 0xb904febb, + 0xe7f102ff, + 0xe3f09418, + 0x9d21f440, +/* 0x0582: ctx_redswitch */ + 0xf7f000f8, + 0x0007f120, + 0x0103f085, + 0xbd000fd0, + 0x08e7f004, +/* 0x0594: ctx_redswitch_delay */ + 0xf401e2b6, + 0xf5f1fd1b, + 0xf5f10800, + 0x07f10200, + 0x03f08500, + 0x000fd001, + 0x00f804bd, +/* 0x05b0: ctx_xfer */ + 0x810007f1, + 0xd00203f0, + 0x04bd000f, + 0xf50711f4, +/* 0x05c3: ctx_xfer_not_load */ + 0xf5058221, + 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, +/* 0x063b: ctx_xfer_post */ + 0xf50712f4, +/* 0x063f: ctx_xfer_done */ + 0xf5027f21, + 0xf8056a21, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 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/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgf117.fuc3 b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgf117.fuc3 new file mode 100644 index 000000000..c918f7d60 --- /dev/null +++ b/kernel/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/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgf117.fuc3.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgf117.fuc3.h new file mode 100644 index 000000000..64d07df4b --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgf117.fuc3.h @@ -0,0 +1,537 @@ +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, +}; + +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, + 0x10fe0542, + 0x0007f100, + 0x0003f007, + 0xbd0000d0, + 0x0427f004, + 0x040007f1, + 0xd00003f0, + 0x04bd0002, + 0xf11031f4, + 0xf0820027, + 0x22cf0123, + 0x0137f000, + 0xbb1f24f0, + 0x32b60432, + 0x05028001, + 0xf1060380, + 0xf0860027, + 0x22cf0123, + 0x04028000, + 0xf10f24b6, + 0xf0c90007, + 0x02d00103, + 0xf104bd00, + 0xf00c30e7, + 0x24bd50e3, + 0x44bd34bd, +/* 0x0430: init_unk_loop */ + 0xb06821f4, + 0x0bf400f6, + 0x01f7f00f, + 0xfd04f2bb, + 0x30b6054f, +/* 0x0445: init_unk_next */ + 0x0120b601, + 0xb004e0b6, + 0x1bf40126, +/* 0x0451: init_unk_done */ + 0x070380e2, + 0xf1080480, + 0xf0010027, + 0x22cf0223, + 0x9534bd00, + 0x07f10825, + 0x03f0c000, + 0x0005d001, + 0x07f104bd, + 0x03f0c100, + 0x0005d001, + 0x0e9804bd, + 0x010f9800, + 0x015021f5, + 0xbb002fbb, + 0x0e98003f, + 0x020f9801, + 0x015021f5, + 0xfd050e98, + 0x2ebb00ef, + 0x003ebb00, + 0x98020e98, + 0x21f5030f, + 0x0e980150, + 0x00effd07, + 0xbb002ebb, + 0x35b6003e, + 0x0007f102, + 0x0103f0d3, + 0xbd0003d0, + 0x0825b604, + 0xb60635b6, + 0x30b60120, + 0x0824b601, + 0xb90834b6, + 0x21f5022f, + 0x2fbb02d3, + 0x003fbb00, + 0x010007f1, + 0xd00203f0, + 0x04bd0003, + 0x29f024bd, + 0x0007f11f, + 0x0203f008, + 0xbd0002d0, +/* 0x0505: main */ + 0x0031f404, + 0xf00028f4, + 0x21f424d7, + 0xf401f439, + 0xf404e4b0, + 0x81fe1e18, + 0x0627f001, + 0x12fd20bd, + 0x01e4b604, + 0xfe051efd, + 0x21f50018, + 0x0ef405fa, +/* 0x0535: main_not_ctx_xfer */ + 0x10ef94d3, + 0xf501f5f0, + 0xf4037e21, +/* 0x0542: ih */ + 0x80f9c60e, + 0xf90188fe, + 0xf990f980, + 0xf9b0f9a0, + 0xf9e0f9d0, + 0xf104bdf0, + 0xf00200a7, + 0xaacf00a3, + 0x04abc400, + 0xf02c0bf4, + 0xe7f124d7, + 0xe3f01a00, + 0x00eecf00, + 0x1900f7f1, + 0xcf00f3f0, + 0x21f400ff, + 0x01e7f004, + 0x1d0007f1, + 0xd00003f0, + 0x04bd000e, +/* 0x0590: ih_no_fifo */ + 0x010007f1, + 0xd00003f0, + 0x04bd000a, + 0xe0fcf0fc, + 0xb0fcd0fc, + 0x90fca0fc, + 0x88fe80fc, + 0xf480fc00, + 0x01f80032, +/* 0x05b4: hub_barrier_done */ + 0x9801f7f0, + 0xfebb040e, + 0x02ffb904, + 0x9418e7f1, + 0xf440e3f0, + 0x00f89d21, +/* 0x05cc: ctx_redswitch */ + 0xf120f7f0, + 0xf0850007, + 0x0fd00103, + 0xf004bd00, +/* 0x05de: ctx_redswitch_delay */ + 0xe2b608e7, + 0xfd1bf401, + 0x0800f5f1, + 0x0200f5f1, + 0x850007f1, + 0xd00103f0, + 0x04bd000f, +/* 0x05fa: ctx_xfer */ + 0x07f100f8, + 0x03f08100, + 0x000fd002, + 0x11f404bd, + 0xcc21f507, +/* 0x060d: ctx_xfer_not_load */ + 0x6a21f505, + 0xf124bd02, + 0xf047fc07, + 0x02d00203, + 0xf004bd00, + 0x20b6012c, + 0xfc07f103, + 0x0203f04a, + 0xbd0002d0, + 0x01acf004, + 0xf102a5f0, + 0xf00000b7, + 0x0c9850b3, + 0x0fc4b604, + 0x9800bcbb, + 0x0d98000c, + 0x00e7f001, + 0x016f21f5, + 0xf101acf0, + 0xf04000b7, + 0x0c9850b3, + 0x0fc4b604, + 0x9800bcbb, + 0x0d98010c, + 0x060f9802, + 0x0800e7f1, + 0x016f21f5, + 0xf001acf0, + 0xb7f104a5, + 0xb3f03000, + 0x040c9850, + 0xbb0fc4b6, + 0x0c9800bc, + 0x030d9802, + 0xf1080f98, + 0xf50200e7, + 0xf5016f21, + 0xf4025e21, + 0x12f40601, +/* 0x06a9: ctx_xfer_post */ + 0x7f21f507, +/* 0x06ad: ctx_xfer_done */ + 0xb421f502, + 0x0000f805, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk104.fuc3 b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk104.fuc3 new file mode 100644 index 000000000..b80cdfd33 --- /dev/null +++ b/kernel/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/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk104.fuc3.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk104.fuc3.h new file mode 100644 index 000000000..2f596433c --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk104.fuc3.h @@ -0,0 +1,537 @@ +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, +}; + +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, + 0x10fe0542, + 0x0007f100, + 0x0003f007, + 0xbd0000d0, + 0x0427f004, + 0x040007f1, + 0xd00003f0, + 0x04bd0002, + 0xf11031f4, + 0xf0820027, + 0x22cf0123, + 0x0137f000, + 0xbb1f24f0, + 0x32b60432, + 0x05028001, + 0xf1060380, + 0xf0860027, + 0x22cf0123, + 0x04028000, + 0xf10f24b6, + 0xf0c90007, + 0x02d00103, + 0xf104bd00, + 0xf00c30e7, + 0x24bd50e3, + 0x44bd34bd, +/* 0x0430: init_unk_loop */ + 0xb06821f4, + 0x0bf400f6, + 0x01f7f00f, + 0xfd04f2bb, + 0x30b6054f, +/* 0x0445: init_unk_next */ + 0x0120b601, + 0xb004e0b6, + 0x1bf40126, +/* 0x0451: init_unk_done */ + 0x070380e2, + 0xf1080480, + 0xf0010027, + 0x22cf0223, + 0x9534bd00, + 0x07f10825, + 0x03f0c000, + 0x0005d001, + 0x07f104bd, + 0x03f0c100, + 0x0005d001, + 0x0e9804bd, + 0x010f9800, + 0x015021f5, + 0xbb002fbb, + 0x0e98003f, + 0x020f9801, + 0x015021f5, + 0xfd050e98, + 0x2ebb00ef, + 0x003ebb00, + 0x98020e98, + 0x21f5030f, + 0x0e980150, + 0x00effd07, + 0xbb002ebb, + 0x35b6003e, + 0x0007f102, + 0x0103f0d3, + 0xbd0003d0, + 0x0825b604, + 0xb60635b6, + 0x30b60120, + 0x0824b601, + 0xb90834b6, + 0x21f5022f, + 0x2fbb02d3, + 0x003fbb00, + 0x010007f1, + 0xd00203f0, + 0x04bd0003, + 0x29f024bd, + 0x0007f11f, + 0x0203f008, + 0xbd0002d0, +/* 0x0505: main */ + 0x0031f404, + 0xf00028f4, + 0x21f424d7, + 0xf401f439, + 0xf404e4b0, + 0x81fe1e18, + 0x0627f001, + 0x12fd20bd, + 0x01e4b604, + 0xfe051efd, + 0x21f50018, + 0x0ef405fa, +/* 0x0535: main_not_ctx_xfer */ + 0x10ef94d3, + 0xf501f5f0, + 0xf4037e21, +/* 0x0542: ih */ + 0x80f9c60e, + 0xf90188fe, + 0xf990f980, + 0xf9b0f9a0, + 0xf9e0f9d0, + 0xf104bdf0, + 0xf00200a7, + 0xaacf00a3, + 0x04abc400, + 0xf02c0bf4, + 0xe7f124d7, + 0xe3f01a00, + 0x00eecf00, + 0x1900f7f1, + 0xcf00f3f0, + 0x21f400ff, + 0x01e7f004, + 0x1d0007f1, + 0xd00003f0, + 0x04bd000e, +/* 0x0590: ih_no_fifo */ + 0x010007f1, + 0xd00003f0, + 0x04bd000a, + 0xe0fcf0fc, + 0xb0fcd0fc, + 0x90fca0fc, + 0x88fe80fc, + 0xf480fc00, + 0x01f80032, +/* 0x05b4: hub_barrier_done */ + 0x9801f7f0, + 0xfebb040e, + 0x02ffb904, + 0x9418e7f1, + 0xf440e3f0, + 0x00f89d21, +/* 0x05cc: ctx_redswitch */ + 0xf120f7f0, + 0xf0850007, + 0x0fd00103, + 0xf004bd00, +/* 0x05de: ctx_redswitch_delay */ + 0xe2b608e7, + 0xfd1bf401, + 0x0800f5f1, + 0x0200f5f1, + 0x850007f1, + 0xd00103f0, + 0x04bd000f, +/* 0x05fa: ctx_xfer */ + 0x07f100f8, + 0x03f08100, + 0x000fd002, + 0x11f404bd, + 0xcc21f507, +/* 0x060d: ctx_xfer_not_load */ + 0x6a21f505, + 0xf124bd02, + 0xf047fc07, + 0x02d00203, + 0xf004bd00, + 0x20b6012c, + 0xfc07f103, + 0x0203f04a, + 0xbd0002d0, + 0x01acf004, + 0xf102a5f0, + 0xf00000b7, + 0x0c9850b3, + 0x0fc4b604, + 0x9800bcbb, + 0x0d98000c, + 0x00e7f001, + 0x016f21f5, + 0xf101acf0, + 0xf04000b7, + 0x0c9850b3, + 0x0fc4b604, + 0x9800bcbb, + 0x0d98010c, + 0x060f9802, + 0x0800e7f1, + 0x016f21f5, + 0xf001acf0, + 0xb7f104a5, + 0xb3f03000, + 0x040c9850, + 0xbb0fc4b6, + 0x0c9800bc, + 0x030d9802, + 0xf1080f98, + 0xf50200e7, + 0xf5016f21, + 0xf4025e21, + 0x12f40601, +/* 0x06a9: ctx_xfer_post */ + 0x7f21f507, +/* 0x06ad: ctx_xfer_done */ + 0xb421f502, + 0x0000f805, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk110.fuc3 b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk110.fuc3 new file mode 100644 index 000000000..98d85fe21 --- /dev/null +++ b/kernel/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/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk110.fuc3.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk110.fuc3.h new file mode 100644 index 000000000..ee8e54db8 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk110.fuc3.h @@ -0,0 +1,537 @@ +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, +}; + +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, + 0x10fe0542, + 0x0007f100, + 0x0003f007, + 0xbd0000d0, + 0x0427f004, + 0x040007f1, + 0xd00003f0, + 0x04bd0002, + 0xf11031f4, + 0xf0820027, + 0x22cf0123, + 0x0137f000, + 0xbb1f24f0, + 0x32b60432, + 0x05028001, + 0xf1060380, + 0xf0860027, + 0x22cf0123, + 0x04028000, + 0xf10f24b6, + 0xf0c90007, + 0x02d00103, + 0xf104bd00, + 0xf00c30e7, + 0x24bd50e3, + 0x44bd34bd, +/* 0x0430: init_unk_loop */ + 0xb06821f4, + 0x0bf400f6, + 0x01f7f00f, + 0xfd04f2bb, + 0x30b6054f, +/* 0x0445: init_unk_next */ + 0x0120b601, + 0xb004e0b6, + 0x1bf40226, +/* 0x0451: init_unk_done */ + 0x070380e2, + 0xf1080480, + 0xf0010027, + 0x22cf0223, + 0x9534bd00, + 0x07f10825, + 0x03f0c000, + 0x0005d001, + 0x07f104bd, + 0x03f0c100, + 0x0005d001, + 0x0e9804bd, + 0x010f9800, + 0x015021f5, + 0xbb002fbb, + 0x0e98003f, + 0x020f9801, + 0x015021f5, + 0xfd050e98, + 0x2ebb00ef, + 0x003ebb00, + 0x98020e98, + 0x21f5030f, + 0x0e980150, + 0x00effd07, + 0xbb002ebb, + 0x35b6003e, + 0x0007f102, + 0x0103f0d3, + 0xbd0003d0, + 0x0825b604, + 0xb60635b6, + 0x30b60120, + 0x0824b601, + 0xb90834b6, + 0x21f5022f, + 0x2fbb02d3, + 0x003fbb00, + 0x010007f1, + 0xd00203f0, + 0x04bd0003, + 0x29f024bd, + 0x0007f11f, + 0x0203f030, + 0xbd0002d0, +/* 0x0505: main */ + 0x0031f404, + 0xf00028f4, + 0x21f424d7, + 0xf401f439, + 0xf404e4b0, + 0x81fe1e18, + 0x0627f001, + 0x12fd20bd, + 0x01e4b604, + 0xfe051efd, + 0x21f50018, + 0x0ef405fa, +/* 0x0535: main_not_ctx_xfer */ + 0x10ef94d3, + 0xf501f5f0, + 0xf4037e21, +/* 0x0542: ih */ + 0x80f9c60e, + 0xf90188fe, + 0xf990f980, + 0xf9b0f9a0, + 0xf9e0f9d0, + 0xf104bdf0, + 0xf00200a7, + 0xaacf00a3, + 0x04abc400, + 0xf02c0bf4, + 0xe7f124d7, + 0xe3f01a00, + 0x00eecf00, + 0x1900f7f1, + 0xcf00f3f0, + 0x21f400ff, + 0x01e7f004, + 0x1d0007f1, + 0xd00003f0, + 0x04bd000e, +/* 0x0590: ih_no_fifo */ + 0x010007f1, + 0xd00003f0, + 0x04bd000a, + 0xe0fcf0fc, + 0xb0fcd0fc, + 0x90fca0fc, + 0x88fe80fc, + 0xf480fc00, + 0x01f80032, +/* 0x05b4: hub_barrier_done */ + 0x9801f7f0, + 0xfebb040e, + 0x02ffb904, + 0x9418e7f1, + 0xf440e3f0, + 0x00f89d21, +/* 0x05cc: ctx_redswitch */ + 0xf120f7f0, + 0xf0850007, + 0x0fd00103, + 0xf004bd00, +/* 0x05de: ctx_redswitch_delay */ + 0xe2b608e7, + 0xfd1bf401, + 0x0800f5f1, + 0x0200f5f1, + 0x850007f1, + 0xd00103f0, + 0x04bd000f, +/* 0x05fa: ctx_xfer */ + 0x07f100f8, + 0x03f08100, + 0x000fd002, + 0x11f404bd, + 0xcc21f507, +/* 0x060d: ctx_xfer_not_load */ + 0x6a21f505, + 0xf124bd02, + 0xf047fc07, + 0x02d00203, + 0xf004bd00, + 0x20b6012c, + 0xfc07f103, + 0x0203f04a, + 0xbd0002d0, + 0x01acf004, + 0xf102a5f0, + 0xf00000b7, + 0x0c9850b3, + 0x0fc4b604, + 0x9800bcbb, + 0x0d98000c, + 0x00e7f001, + 0x016f21f5, + 0xf101acf0, + 0xf04000b7, + 0x0c9850b3, + 0x0fc4b604, + 0x9800bcbb, + 0x0d98010c, + 0x060f9802, + 0x0800e7f1, + 0x016f21f5, + 0xf001acf0, + 0xb7f104a5, + 0xb3f03000, + 0x040c9850, + 0xbb0fc4b6, + 0x0c9800bc, + 0x030d9802, + 0xf1080f98, + 0xf50200e7, + 0xf5016f21, + 0xf4025e21, + 0x12f40601, +/* 0x06a9: ctx_xfer_post */ + 0x7f21f507, +/* 0x06ad: ctx_xfer_done */ + 0xb421f502, + 0x0000f805, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk208.fuc5 b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk208.fuc5 new file mode 100644 index 000000000..8f64299a3 --- /dev/null +++ b/kernel/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/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk208.fuc5.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk208.fuc5.h new file mode 100644 index 000000000..fbcc342f8 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk208.fuc5.h @@ -0,0 +1,473 @@ +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, +}; + +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, + 0xfe048141, + 0x00400010, + 0x0000f607, + 0x040204bd, + 0xf6040040, + 0x04bd0002, + 0x821031f4, + 0xcf018200, + 0x01030022, + 0xbb1f24f0, + 0x32b60432, + 0x0502b501, + 0x820603b5, + 0xcf018600, + 0x02b50022, + 0x0f24b604, + 0x01c90080, + 0xbd0002f6, + 0x0c308e04, + 0xbd24bd50, +/* 0x0383: init_unk_loop */ + 0x7e44bd34, + 0xb0000065, + 0x0bf400f6, + 0xbb010f0e, + 0x4ffd04f2, + 0x0130b605, +/* 0x0398: init_unk_next */ + 0xb60120b6, + 0x26b004e0, + 0xe21bf401, +/* 0x03a4: 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, + 0x0080003f, + 0x03f60201, + 0xbd04bd00, + 0x1f29f024, + 0x02300080, + 0xbd0002f6, +/* 0x0445: main */ + 0x0031f404, + 0x0d0028f4, + 0x00377e24, + 0xf401f400, + 0xf404e4b0, + 0x81fe1d18, + 0xbd060201, + 0x0412fd20, + 0xfd01e4b6, + 0x18fe051e, + 0x05187e00, + 0xd40ef400, +/* 0x0474: main_not_ctx_xfer */ + 0xf010ef94, + 0xf87e01f5, + 0x0ef40002, +/* 0x0481: ih */ + 0xfe80f9c7, + 0x80f90188, + 0xa0f990f9, + 0xd0f9b0f9, + 0xf0f9e0f9, + 0x004a04bd, + 0x00aacf02, + 0xf404abc4, + 0x240d1f0b, + 0xcf1a004e, + 0x004f00ee, + 0x00ffcf19, + 0x0000047e, + 0x0040010e, + 0x000ef61d, +/* 0x04be: ih_no_fifo */ + 0x004004bd, + 0x000af601, + 0xf0fc04bd, + 0xd0fce0fc, + 0xa0fcb0fc, + 0x80fc90fc, + 0xfc0088fe, + 0x0032f480, +/* 0x04de: hub_barrier_done */ + 0x010f01f8, + 0xbb040e98, + 0xffb204fe, + 0x4094188e, + 0x00008f7e, +/* 0x04f2: ctx_redswitch */ + 0x200f00f8, + 0x01850080, + 0xbd000ff6, +/* 0x04ff: ctx_redswitch_delay */ + 0xb6080e04, + 0x1bf401e2, + 0x00f5f1fd, + 0x00f5f108, + 0x85008002, + 0x000ff601, + 0x00f804bd, +/* 0x0518: ctx_xfer */ + 0x02810080, + 0xbd000ff6, + 0x0711f404, + 0x0004f27e, +/* 0x0528: ctx_xfer_not_load */ + 0x0002167e, + 0xfc8024bd, + 0x02f60247, + 0xf004bd00, + 0x20b6012c, + 0x4afc8003, + 0x0002f602, + 0xacf004bd, + 0x02a5f001, + 0x5000008b, + 0xb6040c98, + 0xbcbb0fc4, + 0x000c9800, + 0x0e010d98, + 0x013d7e00, + 0x01acf000, + 0x5040008b, + 0xb6040c98, + 0xbcbb0fc4, + 0x010c9800, + 0x98020d98, + 0x004e060f, + 0x013d7e08, + 0x01acf000, + 0x8b04a5f0, + 0x98503000, + 0xc4b6040c, + 0x00bcbb0f, + 0x98020c98, + 0x0f98030d, + 0x02004e08, + 0x00013d7e, + 0x00020a7e, + 0xf40601f4, +/* 0x05b2: ctx_xfer_post */ + 0x277e0712, +/* 0x05b6: ctx_xfer_done */ + 0xde7e0002, + 0x00f80004, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgm107.fuc5 b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgm107.fuc5 new file mode 100644 index 000000000..47802c7ec --- /dev/null +++ b/kernel/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/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgm107.fuc5.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgm107.fuc5.h new file mode 100644 index 000000000..51f5c3c6e --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgm107.fuc5.h @@ -0,0 +1,605 @@ +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, +}; + +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, + 0x05b04104, + 0x400010fe, + 0x00f60700, + 0x0204bd00, + 0x04004004, + 0xbd0002f6, + 0x1031f404, + 0x01820082, + 0x030022cf, + 0x1f24f001, + 0xb60432bb, + 0x02b50132, + 0x0603b505, + 0x01860082, + 0xb50022cf, + 0x24b60402, + 0xc900800f, + 0x0002f601, + 0x308e04bd, + 0x24bd500c, + 0x44bd34bd, +/* 0x03b0: init_unk_loop */ + 0x0000657e, + 0xf400f6b0, + 0x010f0e0b, + 0xfd04f2bb, + 0x30b6054f, +/* 0x03c5: init_unk_next */ + 0x0120b601, + 0xb004e0b6, + 0x1bf40226, +/* 0x03d1: init_unk_done */ + 0x0703b5e2, + 0x820804b5, + 0xcf020100, + 0x34bd0022, + 0x80082595, + 0xf601c000, + 0x04bd0005, + 0x01c10080, + 0xbd0005f6, + 0x000e9804, + 0x7e010f98, + 0xbb000120, + 0x3fbb002f, + 0x010e9800, + 0x7e020f98, + 0x98000120, + 0xeffd050e, + 0x002ebb00, + 0x98003ebb, + 0x0f98020e, + 0x01207e03, + 0x070e9800, + 0xbb00effd, + 0x3ebb002e, + 0x0235b600, + 0x01d30080, + 0xbd0003f6, + 0x0825b604, + 0xb60635b6, + 0x30b60120, + 0x0824b601, + 0xb20834b6, + 0x02687e2f, + 0x002fbb00, + 0x0f003fbb, + 0x8effb23f, + 0xf0501d60, + 0x8f7e01e5, + 0x0c0f0000, + 0xa88effb2, + 0xe5f0501d, + 0x008f7e01, + 0x03147e00, + 0xb23f0f00, + 0x1d608eff, + 0x01e5f050, + 0x00008f7e, + 0xffb2000f, + 0x501d9c8e, + 0x7e01e5f0, + 0x0f00008f, + 0x03147e01, + 0x8effb200, + 0xf0501da8, + 0x8f7e01e5, + 0xff0f0000, + 0x988effb2, + 0xe5f0501d, + 0x008f7e01, + 0xb2020f00, + 0x1da88eff, + 0x01e5f050, + 0x00008f7e, + 0x0003147e, + 0x85050498, + 0x98504000, + 0x64b60406, + 0x0056bb0f, +/* 0x04e0: tpc_strand_init_tpc_loop */ + 0x05705eb8, + 0x00657e00, + 0xbdf6b200, +/* 0x04ed: tpc_strand_init_idx_loop */ + 0x605eb874, + 0x7fb20005, + 0x00008f7e, + 0x05885eb8, + 0x082f9500, + 0x00008f7e, + 0x058c5eb8, + 0x082f9500, + 0x00008f7e, + 0x05905eb8, + 0x00657e00, + 0x06f5b600, + 0xb601f0b6, + 0x2fbb08f4, + 0x003fbb00, + 0xb60170b6, + 0x1bf40162, + 0x0050b7bf, + 0x0142b608, + 0x0fa81bf4, + 0x8effb23f, + 0xf0501d60, + 0x8f7e01e5, + 0x0d0f0000, + 0xa88effb2, + 0xe5f0501d, + 0x008f7e01, + 0x03147e00, + 0x01008000, + 0x0003f602, + 0x24bd04bd, + 0x801f29f0, + 0xf6023000, + 0x04bd0002, +/* 0x0574: main */ + 0xf40031f4, + 0x240d0028, + 0x0000377e, + 0xb0f401f4, + 0x18f404e4, + 0x0181fe1d, + 0x20bd0602, + 0xb60412fd, + 0x1efd01e4, + 0x0018fe05, + 0x0006477e, +/* 0x05a3: main_not_ctx_xfer */ + 0x94d40ef4, + 0xf5f010ef, + 0x02f87e01, + 0xc70ef400, +/* 0x05b0: ih */ + 0x88fe80f9, + 0xf980f901, + 0xf9a0f990, + 0xf9d0f9b0, + 0xbdf0f9e0, + 0x02004a04, + 0xc400aacf, + 0x0bf404ab, + 0x4e240d1f, + 0xeecf1a00, + 0x19004f00, + 0x7e00ffcf, + 0x0e000004, + 0x1d004001, + 0xbd000ef6, +/* 0x05ed: ih_no_fifo */ + 0x01004004, + 0xbd000af6, + 0xfcf0fc04, + 0xfcd0fce0, + 0xfca0fcb0, + 0xfe80fc90, + 0x80fc0088, + 0xf80032f4, +/* 0x060d: hub_barrier_done */ + 0x98010f01, + 0xfebb040e, + 0x8effb204, + 0x7e409418, + 0xf800008f, +/* 0x0621: ctx_redswitch */ + 0x80200f00, + 0xf6018500, + 0x04bd000f, +/* 0x062e: ctx_redswitch_delay */ + 0xe2b6080e, + 0xfd1bf401, + 0x0800f5f1, + 0x0200f5f1, + 0x01850080, + 0xbd000ff6, +/* 0x0647: ctx_xfer */ + 0x8000f804, + 0xf6028100, + 0x04bd000f, + 0xc48effb2, + 0xe5f0501d, + 0x008f7e01, + 0x0711f400, + 0x0006217e, +/* 0x0664: ctx_xfer_not_load */ + 0x0002167e, + 0xfc8024bd, + 0x02f60247, + 0xf004bd00, + 0x20b6012c, + 0x4afc8003, + 0x0002f602, + 0x0c0f04bd, + 0xa88effb2, + 0xe5f0501d, + 0x008f7e01, + 0x03147e00, + 0xb23f0f00, + 0x1d608eff, + 0x01e5f050, + 0x00008f7e, + 0xffb2000f, + 0x501d9c8e, + 0x7e01e5f0, + 0x0f00008f, + 0x03147e01, + 0x01fcf000, + 0xb203f0b6, + 0x1da88eff, + 0x01e5f050, + 0x00008f7e, + 0xf001acf0, + 0x008b02a5, + 0x0c985000, + 0x0fc4b604, + 0x9800bcbb, + 0x0d98000c, + 0x7e000e01, + 0xf000013d, + 0x008b01ac, + 0x0c985040, + 0x0fc4b604, + 0x9800bcbb, + 0x0d98010c, + 0x060f9802, + 0x7e08004e, + 0xf000013d, + 0xa5f001ac, + 0x30008b04, + 0x040c9850, + 0xbb0fc4b6, + 0x0c9800bc, + 0x030d9802, + 0x4e080f98, + 0x3d7e0200, + 0x0a7e0001, + 0x147e0002, + 0x01f40003, + 0x1a12f406, +/* 0x073c: ctx_xfer_post */ + 0x0002277e, + 0xffb20d0f, + 0x501da88e, + 0x7e01e5f0, + 0x7e00008f, +/* 0x0753: ctx_xfer_done */ + 0x7e000314, + 0xf800060d, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 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/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hub.fuc b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hub.fuc new file mode 100644 index 000000000..87f99e38a --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hub.fuc @@ -0,0 +1,696 @@ +/* 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 +// +main: + // sleep until we have something to do + bset $flags $p0 + sleep $p0 + mov $r13 #cmd_queue + call(queue_get) + bra $p1 #main + + // context switch, requested by GPU? + cmpu b32 $r14 0x4001 + bra ne #main_not_ctx_switch + 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 $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 + 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/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgf100.fuc3 b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgf100.fuc3 new file mode 100644 index 000000000..2c28e7199 --- /dev/null +++ b/kernel/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/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgf100.fuc3.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgf100.fuc3.h new file mode 100644 index 000000000..f6acda505 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgf100.fuc3.h @@ -0,0 +1,1047 @@ +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, +}; + +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, + 0x0d21f502, + 0x1f21f508, + 0x10f7f008, + 0x086c21f5, + 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, + 0x086c21f5, + 0xf500f7f0, + 0xf1080d21, + 0xf0010007, + 0x01d00203, + 0xbd04bd00, + 0x1f19f014, + 0x080007f1, + 0xd00203f0, + 0x04bd0001, +/* 0x0564: main */ + 0xf40031f4, + 0xd7f00028, + 0x3921f410, + 0xb1f401f4, + 0xf54001e4, + 0xbd00e91b, + 0x0499f094, + 0x0f0007f1, + 0xd00203f0, + 0x04bd0009, + 0xc00017f1, + 0xcf0213f0, + 0x27f10011, + 0x23f0c100, + 0x0022cf02, + 0xf51f13c8, + 0xc800890b, + 0x0bf41f23, + 0xb920f962, + 0x94bd0212, + 0xf10799f0, + 0xf00f0007, + 0x09d00203, + 0xf404bd00, + 0x31f40132, + 0x4021f502, + 0xf094bd0a, + 0x07f10799, + 0x03f01700, + 0x0009d002, + 0x20fc04bd, + 0x99f094bd, + 0x0007f106, + 0x0203f00f, + 0xbd0009d0, + 0x0131f404, + 0x0a4021f5, + 0x99f094bd, + 0x0007f106, + 0x0203f017, + 0xbd0009d0, + 0x330ef404, +/* 0x060c: chsw_prev_no_next */ + 0x12b920f9, + 0x0132f402, + 0xf50232f4, + 0xfc0a4021, + 0x0007f120, + 0x0203f0c0, + 0xbd0002d0, + 0x130ef404, +/* 0x062c: chsw_no_prev */ + 0xf41f23c8, + 0x31f40d0b, + 0x0232f401, + 0x0a4021f5, +/* 0x063c: chsw_done */ + 0xf10127f0, + 0xf0c30007, + 0x02d00203, + 0xbd04bd00, + 0x0499f094, + 0x170007f1, + 0xd00203f0, + 0x04bd0009, + 0xff080ef5, +/* 0x0660: main_not_ctx_switch */ + 0xf401e4b0, + 0xf2b90d1b, + 0xd021f502, + 0x460ef409, +/* 0x0670: main_not_ctx_chan */ + 0xf402e4b0, + 0x94bd321b, + 0xf10799f0, + 0xf00f0007, + 0x09d00203, + 0xf404bd00, + 0x32f40132, + 0x4021f502, + 0xf094bd0a, + 0x07f10799, + 0x03f01700, + 0x0009d002, + 0x0ef404bd, +/* 0x06a5: main_not_ctx_save */ + 0x10ef9411, + 0xf501f5f0, + 0xf5037e21, +/* 0x06b3: main_done */ + 0xbdfeb50e, + 0x1f29f024, + 0x080007f1, + 0xd00203f0, + 0x04bd0002, + 0xfea00ef5, +/* 0x06c8: ih */ + 0x88fe80f9, + 0xf980f901, + 0xf9a0f990, + 0xf9d0f9b0, + 0xbdf0f9e0, + 0x00a7f104, + 0x00a3f002, + 0xc400aacf, + 0x0bf404ab, + 0x10d7f030, + 0x1a00e7f1, + 0xcf00e3f0, + 0xf7f100ee, + 0xf3f01900, + 0x00ffcf00, + 0xb70421f4, + 0xf00400b0, + 0x07f101e7, + 0x03f01d00, + 0x000ed000, +/* 0x071a: ih_no_fifo */ + 0xabe404bd, + 0x0bf40100, + 0x10d7f00d, + 0x4001e7f1, +/* 0x072b: ih_no_ctxsw */ + 0xe40421f4, + 0xf40400ab, + 0xe7f16c0b, + 0xe3f00708, + 0x6821f440, + 0xf102ffb9, + 0xf0040007, + 0x0fd00203, + 0xf104bd00, + 0xf00704e7, + 0x21f440e3, + 0x02ffb968, + 0x030007f1, + 0xd00203f0, + 0x04bd000f, + 0x9450fec7, + 0xf7f102ee, + 0xf3f00700, + 0x00efbb40, + 0xf16821f4, + 0xf0020007, + 0x0fd00203, + 0xf004bd00, + 0x21f503f7, + 0xb7f1037e, + 0xbfb90100, + 0x44e7f102, + 0x40e3f001, +/* 0x079b: ih_no_fwmthd */ + 0xf19d21f4, + 0xbd0504b7, + 0xb4abffb0, + 0xf10f0bf4, + 0xf0070007, + 0x0bd00303, +/* 0x07b3: ih_no_other */ + 0xf104bd00, + 0xf0010007, + 0x0ad00003, + 0xfc04bd00, + 0xfce0fcf0, + 0xfcb0fcd0, + 0xfc90fca0, + 0x0088fe80, + 0x32f480fc, +/* 0x07d7: ctx_4160s */ + 0xf001f800, + 0xffb901f7, + 0x60e7f102, + 0x40e3f041, +/* 0x07e7: ctx_4160s_wait */ + 0xf19d21f4, + 0xf04160e7, + 0x21f440e3, + 0x02ffb968, + 0xf404ffc8, + 0x00f8f00b, +/* 0x07fc: ctx_4160c */ + 0xffb9f4bd, + 0x60e7f102, + 0x40e3f041, + 0xf89d21f4, +/* 0x080d: ctx_4170s */ + 0x10f5f000, + 0xf102ffb9, + 0xf04170e7, + 0x21f440e3, +/* 0x081f: ctx_4170w */ + 0xf100f89d, + 0xf04170e7, + 0x21f440e3, + 0x02ffb968, + 0xf410f4f0, + 0x00f8f01b, +/* 0x0834: ctx_redswitch */ + 0x0200e7f1, + 0xf040e5f0, + 0xe5f020e5, + 0x0007f110, + 0x0103f085, + 0xbd000ed0, + 0x08f7f004, +/* 0x0850: ctx_redswitch_delay */ + 0xf401f2b6, + 0xe5f1fd1b, + 0xe5f10400, + 0x07f10100, + 0x03f08500, + 0x000ed001, + 0x00f804bd, +/* 0x086c: ctx_86c */ + 0x1b0007f1, + 0xd00203f0, + 0x04bd000f, + 0xf102ffb9, + 0xf08a14e7, + 0x21f440e3, + 0x02ffb99d, + 0xa86ce7f1, + 0xf441e3f0, + 0x00f89d21, +/* 0x0894: ctx_mem */ + 0x840007f1, + 0xd00203f0, + 0x04bd000f, +/* 0x08a0: ctx_mem_wait */ + 0x8400f7f1, + 0xcf02f3f0, + 0xfffd00ff, + 0xf31bf405, +/* 0x08b2: ctx_load */ + 0x94bd00f8, + 0xf10599f0, + 0xf00f0007, + 0x09d00203, + 0xf004bd00, + 0x21f40ca7, + 0xf1f4bdd0, + 0xf0890007, + 0x0fd00203, + 0xf104bd00, + 0xf0c10007, + 0x02d00203, + 0xf104bd00, + 0xf0830007, + 0x02d00203, + 0xf004bd00, + 0x21f507f7, + 0x07f10894, + 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, +/* 0x09d0: ctx_chan */ + 0x07d721f5, + 0x08b221f5, + 0xf40ca7f0, + 0xf7f0d021, + 0x9421f505, + 0xfc21f508, +/* 0x09eb: ctx_mmio_exec */ + 0x9800f807, + 0x07f14103, + 0x03f08100, + 0x0003d002, + 0x34bd04bd, +/* 0x09fc: ctx_mmio_loop */ + 0xf4ff34c4, + 0x57f10f1b, + 0x53f00200, + 0x0535fa06, +/* 0x0a0e: ctx_mmio_pull */ + 0x4e9803f8, + 0x814f9880, + 0xb69d21f4, + 0x12b60830, + 0xdf1bf401, +/* 0x0a20: ctx_mmio_done */ + 0xf1160398, + 0xf0810007, + 0x03d00203, + 0x8004bd00, + 0x17f14000, + 0x13f00100, + 0x0601fa06, + 0x00f803f8, +/* 0x0a40: ctx_xfer */ + 0xf104e7f0, + 0xf0020007, + 0x0ed00303, +/* 0x0a4f: ctx_xfer_idle */ + 0xf104bd00, + 0xf00000e7, + 0xeecf03e3, + 0x00e4f100, + 0xf21bf420, + 0xf40611f4, +/* 0x0a66: ctx_xfer_pre */ + 0xf7f01102, + 0x6c21f510, + 0xd721f508, + 0x1c11f407, +/* 0x0a74: ctx_xfer_pre_load */ + 0xf502f7f0, + 0xf5080d21, + 0xf5081f21, + 0xbd083421, + 0x0d21f5f4, + 0xb221f508, +/* 0x0a8d: 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, + 0x9421f505, + 0x3202f408, +/* 0x0b1c: ctx_xfer_post */ + 0xf502f7f0, + 0xbd080d21, + 0x6c21f5f4, + 0x7f21f508, + 0x1f21f502, + 0xf5f4bd08, + 0xf4080d21, + 0x01981011, + 0x0511fd40, + 0xf5070bf4, +/* 0x0b47: ctx_xfer_no_post_mmio */ + 0xf509eb21, +/* 0x0b4b: ctx_xfer_done */ + 0xf807fc21, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 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/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgf117.fuc3 b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgf117.fuc3 new file mode 100644 index 000000000..581b2d53a --- /dev/null +++ b/kernel/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/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgf117.fuc3.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgf117.fuc3.h new file mode 100644 index 000000000..7cb14e59d --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgf117.fuc3.h @@ -0,0 +1,1047 @@ +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, +}; + +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, + 0x0d21f502, + 0x1f21f508, + 0x10f7f008, + 0x086c21f5, + 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, + 0x086c21f5, + 0xf500f7f0, + 0xf1080d21, + 0xf0010007, + 0x01d00203, + 0xbd04bd00, + 0x1f19f014, + 0x080007f1, + 0xd00203f0, + 0x04bd0001, +/* 0x0564: main */ + 0xf40031f4, + 0xd7f00028, + 0x3921f410, + 0xb1f401f4, + 0xf54001e4, + 0xbd00e91b, + 0x0499f094, + 0x0f0007f1, + 0xd00203f0, + 0x04bd0009, + 0xc00017f1, + 0xcf0213f0, + 0x27f10011, + 0x23f0c100, + 0x0022cf02, + 0xf51f13c8, + 0xc800890b, + 0x0bf41f23, + 0xb920f962, + 0x94bd0212, + 0xf10799f0, + 0xf00f0007, + 0x09d00203, + 0xf404bd00, + 0x31f40132, + 0x4021f502, + 0xf094bd0a, + 0x07f10799, + 0x03f01700, + 0x0009d002, + 0x20fc04bd, + 0x99f094bd, + 0x0007f106, + 0x0203f00f, + 0xbd0009d0, + 0x0131f404, + 0x0a4021f5, + 0x99f094bd, + 0x0007f106, + 0x0203f017, + 0xbd0009d0, + 0x330ef404, +/* 0x060c: chsw_prev_no_next */ + 0x12b920f9, + 0x0132f402, + 0xf50232f4, + 0xfc0a4021, + 0x0007f120, + 0x0203f0c0, + 0xbd0002d0, + 0x130ef404, +/* 0x062c: chsw_no_prev */ + 0xf41f23c8, + 0x31f40d0b, + 0x0232f401, + 0x0a4021f5, +/* 0x063c: chsw_done */ + 0xf10127f0, + 0xf0c30007, + 0x02d00203, + 0xbd04bd00, + 0x0499f094, + 0x170007f1, + 0xd00203f0, + 0x04bd0009, + 0xff080ef5, +/* 0x0660: main_not_ctx_switch */ + 0xf401e4b0, + 0xf2b90d1b, + 0xd021f502, + 0x460ef409, +/* 0x0670: main_not_ctx_chan */ + 0xf402e4b0, + 0x94bd321b, + 0xf10799f0, + 0xf00f0007, + 0x09d00203, + 0xf404bd00, + 0x32f40132, + 0x4021f502, + 0xf094bd0a, + 0x07f10799, + 0x03f01700, + 0x0009d002, + 0x0ef404bd, +/* 0x06a5: main_not_ctx_save */ + 0x10ef9411, + 0xf501f5f0, + 0xf5037e21, +/* 0x06b3: main_done */ + 0xbdfeb50e, + 0x1f29f024, + 0x080007f1, + 0xd00203f0, + 0x04bd0002, + 0xfea00ef5, +/* 0x06c8: ih */ + 0x88fe80f9, + 0xf980f901, + 0xf9a0f990, + 0xf9d0f9b0, + 0xbdf0f9e0, + 0x00a7f104, + 0x00a3f002, + 0xc400aacf, + 0x0bf404ab, + 0x10d7f030, + 0x1a00e7f1, + 0xcf00e3f0, + 0xf7f100ee, + 0xf3f01900, + 0x00ffcf00, + 0xb70421f4, + 0xf00400b0, + 0x07f101e7, + 0x03f01d00, + 0x000ed000, +/* 0x071a: ih_no_fifo */ + 0xabe404bd, + 0x0bf40100, + 0x10d7f00d, + 0x4001e7f1, +/* 0x072b: ih_no_ctxsw */ + 0xe40421f4, + 0xf40400ab, + 0xe7f16c0b, + 0xe3f00708, + 0x6821f440, + 0xf102ffb9, + 0xf0040007, + 0x0fd00203, + 0xf104bd00, + 0xf00704e7, + 0x21f440e3, + 0x02ffb968, + 0x030007f1, + 0xd00203f0, + 0x04bd000f, + 0x9450fec7, + 0xf7f102ee, + 0xf3f00700, + 0x00efbb40, + 0xf16821f4, + 0xf0020007, + 0x0fd00203, + 0xf004bd00, + 0x21f503f7, + 0xb7f1037e, + 0xbfb90100, + 0x44e7f102, + 0x40e3f001, +/* 0x079b: ih_no_fwmthd */ + 0xf19d21f4, + 0xbd0504b7, + 0xb4abffb0, + 0xf10f0bf4, + 0xf0070007, + 0x0bd00303, +/* 0x07b3: ih_no_other */ + 0xf104bd00, + 0xf0010007, + 0x0ad00003, + 0xfc04bd00, + 0xfce0fcf0, + 0xfcb0fcd0, + 0xfc90fca0, + 0x0088fe80, + 0x32f480fc, +/* 0x07d7: ctx_4160s */ + 0xf001f800, + 0xffb901f7, + 0x60e7f102, + 0x40e3f041, +/* 0x07e7: ctx_4160s_wait */ + 0xf19d21f4, + 0xf04160e7, + 0x21f440e3, + 0x02ffb968, + 0xf404ffc8, + 0x00f8f00b, +/* 0x07fc: ctx_4160c */ + 0xffb9f4bd, + 0x60e7f102, + 0x40e3f041, + 0xf89d21f4, +/* 0x080d: ctx_4170s */ + 0x10f5f000, + 0xf102ffb9, + 0xf04170e7, + 0x21f440e3, +/* 0x081f: ctx_4170w */ + 0xf100f89d, + 0xf04170e7, + 0x21f440e3, + 0x02ffb968, + 0xf410f4f0, + 0x00f8f01b, +/* 0x0834: ctx_redswitch */ + 0x0200e7f1, + 0xf040e5f0, + 0xe5f020e5, + 0x0007f110, + 0x0103f085, + 0xbd000ed0, + 0x08f7f004, +/* 0x0850: ctx_redswitch_delay */ + 0xf401f2b6, + 0xe5f1fd1b, + 0xe5f10400, + 0x07f10100, + 0x03f08500, + 0x000ed001, + 0x00f804bd, +/* 0x086c: ctx_86c */ + 0x1b0007f1, + 0xd00203f0, + 0x04bd000f, + 0xf102ffb9, + 0xf08a14e7, + 0x21f440e3, + 0x02ffb99d, + 0xa86ce7f1, + 0xf441e3f0, + 0x00f89d21, +/* 0x0894: ctx_mem */ + 0x840007f1, + 0xd00203f0, + 0x04bd000f, +/* 0x08a0: ctx_mem_wait */ + 0x8400f7f1, + 0xcf02f3f0, + 0xfffd00ff, + 0xf31bf405, +/* 0x08b2: ctx_load */ + 0x94bd00f8, + 0xf10599f0, + 0xf00f0007, + 0x09d00203, + 0xf004bd00, + 0x21f40ca7, + 0xf1f4bdd0, + 0xf0890007, + 0x0fd00203, + 0xf104bd00, + 0xf0c10007, + 0x02d00203, + 0xf104bd00, + 0xf0830007, + 0x02d00203, + 0xf004bd00, + 0x21f507f7, + 0x07f10894, + 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, +/* 0x09d0: ctx_chan */ + 0x07d721f5, + 0x08b221f5, + 0xf40ca7f0, + 0xf7f0d021, + 0x9421f505, + 0xfc21f508, +/* 0x09eb: ctx_mmio_exec */ + 0x9800f807, + 0x07f14103, + 0x03f08100, + 0x0003d002, + 0x34bd04bd, +/* 0x09fc: ctx_mmio_loop */ + 0xf4ff34c4, + 0x57f10f1b, + 0x53f00200, + 0x0535fa06, +/* 0x0a0e: ctx_mmio_pull */ + 0x4e9803f8, + 0x814f9880, + 0xb69d21f4, + 0x12b60830, + 0xdf1bf401, +/* 0x0a20: ctx_mmio_done */ + 0xf1160398, + 0xf0810007, + 0x03d00203, + 0x8004bd00, + 0x17f14000, + 0x13f00100, + 0x0601fa06, + 0x00f803f8, +/* 0x0a40: ctx_xfer */ + 0xf104e7f0, + 0xf0020007, + 0x0ed00303, +/* 0x0a4f: ctx_xfer_idle */ + 0xf104bd00, + 0xf00000e7, + 0xeecf03e3, + 0x00e4f100, + 0xf21bf420, + 0xf40611f4, +/* 0x0a66: ctx_xfer_pre */ + 0xf7f01102, + 0x6c21f510, + 0xd721f508, + 0x1c11f407, +/* 0x0a74: ctx_xfer_pre_load */ + 0xf502f7f0, + 0xf5080d21, + 0xf5081f21, + 0xbd083421, + 0x0d21f5f4, + 0xb221f508, +/* 0x0a8d: 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, + 0x9421f505, + 0x3202f408, +/* 0x0b1c: ctx_xfer_post */ + 0xf502f7f0, + 0xbd080d21, + 0x6c21f5f4, + 0x7f21f508, + 0x1f21f502, + 0xf5f4bd08, + 0xf4080d21, + 0x01981011, + 0x0511fd40, + 0xf5070bf4, +/* 0x0b47: ctx_xfer_no_post_mmio */ + 0xf509eb21, +/* 0x0b4b: ctx_xfer_done */ + 0xf807fc21, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 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/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk104.fuc3 b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk104.fuc3 new file mode 100644 index 000000000..d977d393b --- /dev/null +++ b/kernel/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/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk104.fuc3.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk104.fuc3.h new file mode 100644 index 000000000..95ac15110 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk104.fuc3.h @@ -0,0 +1,1044 @@ +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, +}; + +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, + 0xd721f502, + 0xe921f507, + 0x10f7f007, + 0x083621f5, + 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, + 0x083621f5, + 0xf500f7f0, + 0xf107d721, + 0xf0010007, + 0x01d00203, + 0xbd04bd00, + 0x1f19f014, + 0x080007f1, + 0xd00203f0, + 0x04bd0001, +/* 0x0564: main */ + 0xf40031f4, + 0xd7f00028, + 0x3921f410, + 0xb1f401f4, + 0xf54001e4, + 0xbd00e91b, + 0x0499f094, + 0x0f0007f1, + 0xd00203f0, + 0x04bd0009, + 0xc00017f1, + 0xcf0213f0, + 0x27f10011, + 0x23f0c100, + 0x0022cf02, + 0xf51f13c8, + 0xc800890b, + 0x0bf41f23, + 0xb920f962, + 0x94bd0212, + 0xf10799f0, + 0xf00f0007, + 0x09d00203, + 0xf404bd00, + 0x31f40132, + 0x0221f502, + 0xf094bd0a, + 0x07f10799, + 0x03f01700, + 0x0009d002, + 0x20fc04bd, + 0x99f094bd, + 0x0007f106, + 0x0203f00f, + 0xbd0009d0, + 0x0131f404, + 0x0a0221f5, + 0x99f094bd, + 0x0007f106, + 0x0203f017, + 0xbd0009d0, + 0x330ef404, +/* 0x060c: chsw_prev_no_next */ + 0x12b920f9, + 0x0132f402, + 0xf50232f4, + 0xfc0a0221, + 0x0007f120, + 0x0203f0c0, + 0xbd0002d0, + 0x130ef404, +/* 0x062c: chsw_no_prev */ + 0xf41f23c8, + 0x31f40d0b, + 0x0232f401, + 0x0a0221f5, +/* 0x063c: chsw_done */ + 0xf10127f0, + 0xf0c30007, + 0x02d00203, + 0xbd04bd00, + 0x0499f094, + 0x170007f1, + 0xd00203f0, + 0x04bd0009, + 0xff080ef5, +/* 0x0660: main_not_ctx_switch */ + 0xf401e4b0, + 0xf2b90d1b, + 0x9a21f502, + 0x460ef409, +/* 0x0670: main_not_ctx_chan */ + 0xf402e4b0, + 0x94bd321b, + 0xf10799f0, + 0xf00f0007, + 0x09d00203, + 0xf404bd00, + 0x32f40132, + 0x0221f502, + 0xf094bd0a, + 0x07f10799, + 0x03f01700, + 0x0009d002, + 0x0ef404bd, +/* 0x06a5: main_not_ctx_save */ + 0x10ef9411, + 0xf501f5f0, + 0xf5037e21, +/* 0x06b3: main_done */ + 0xbdfeb50e, + 0x1f29f024, + 0x080007f1, + 0xd00203f0, + 0x04bd0002, + 0xfea00ef5, +/* 0x06c8: ih */ + 0x88fe80f9, + 0xf980f901, + 0xf9a0f990, + 0xf9d0f9b0, + 0xbdf0f9e0, + 0x00a7f104, + 0x00a3f002, + 0xc400aacf, + 0x0bf404ab, + 0x10d7f030, + 0x1a00e7f1, + 0xcf00e3f0, + 0xf7f100ee, + 0xf3f01900, + 0x00ffcf00, + 0xb70421f4, + 0xf00400b0, + 0x07f101e7, + 0x03f01d00, + 0x000ed000, +/* 0x071a: ih_no_fifo */ + 0xabe404bd, + 0x0bf40100, + 0x10d7f00d, + 0x4001e7f1, +/* 0x072b: ih_no_ctxsw */ + 0xe40421f4, + 0xf40400ab, + 0xe7f16c0b, + 0xe3f00708, + 0x6821f440, + 0xf102ffb9, + 0xf0040007, + 0x0fd00203, + 0xf104bd00, + 0xf00704e7, + 0x21f440e3, + 0x02ffb968, + 0x030007f1, + 0xd00203f0, + 0x04bd000f, + 0x9450fec7, + 0xf7f102ee, + 0xf3f00700, + 0x00efbb40, + 0xf16821f4, + 0xf0020007, + 0x0fd00203, + 0xf004bd00, + 0x21f503f7, + 0xb7f1037e, + 0xbfb90100, + 0x44e7f102, + 0x40e3f001, +/* 0x079b: ih_no_fwmthd */ + 0xf19d21f4, + 0xbd0504b7, + 0xb4abffb0, + 0xf10f0bf4, + 0xf0070007, + 0x0bd00303, +/* 0x07b3: ih_no_other */ + 0xf104bd00, + 0xf0010007, + 0x0ad00003, + 0xfc04bd00, + 0xfce0fcf0, + 0xfcb0fcd0, + 0xfc90fca0, + 0x0088fe80, + 0x32f480fc, +/* 0x07d7: ctx_4170s */ + 0xf001f800, + 0xffb910f5, + 0x70e7f102, + 0x40e3f041, + 0xf89d21f4, +/* 0x07e9: ctx_4170w */ + 0x70e7f100, + 0x40e3f041, + 0xb96821f4, + 0xf4f002ff, + 0xf01bf410, +/* 0x07fe: ctx_redswitch */ + 0xe7f100f8, + 0xe5f00200, + 0x20e5f040, + 0xf110e5f0, + 0xf0850007, + 0x0ed00103, + 0xf004bd00, +/* 0x081a: ctx_redswitch_delay */ + 0xf2b608f7, + 0xfd1bf401, + 0x0400e5f1, + 0x0100e5f1, + 0x850007f1, + 0xd00103f0, + 0x04bd000e, +/* 0x0836: ctx_86c */ + 0x07f100f8, + 0x03f01b00, + 0x000fd002, + 0xffb904bd, + 0x14e7f102, + 0x40e3f08a, + 0xb99d21f4, + 0xe7f102ff, + 0xe3f0a86c, + 0x9d21f441, +/* 0x085e: ctx_mem */ + 0x07f100f8, + 0x03f08400, + 0x000fd002, +/* 0x086a: ctx_mem_wait */ + 0xf7f104bd, + 0xf3f08400, + 0x00ffcf02, + 0xf405fffd, + 0x00f8f31b, +/* 0x087c: ctx_load */ + 0x99f094bd, + 0x0007f105, + 0x0203f00f, + 0xbd0009d0, + 0x0ca7f004, + 0xbdd021f4, + 0x0007f1f4, + 0x0203f089, + 0xbd000fd0, + 0x0007f104, + 0x0203f0c1, + 0xbd0002d0, + 0x0007f104, + 0x0203f083, + 0xbd0002d0, + 0x07f7f004, + 0x085e21f5, + 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, +/* 0x099a: ctx_chan */ + 0x21f500f8, + 0xa7f0087c, + 0xd021f40c, + 0xf505f7f0, + 0xf8085e21, +/* 0x09ad: ctx_mmio_exec */ + 0x41039800, + 0x810007f1, + 0xd00203f0, + 0x04bd0003, +/* 0x09be: ctx_mmio_loop */ + 0x34c434bd, + 0x0f1bf4ff, + 0x020057f1, + 0xfa0653f0, + 0x03f80535, +/* 0x09d0: ctx_mmio_pull */ + 0x98804e98, + 0x21f4814f, + 0x0830b69d, + 0xf40112b6, +/* 0x09e2: ctx_mmio_done */ + 0x0398df1b, + 0x0007f116, + 0x0203f081, + 0xbd0003d0, + 0x40008004, + 0x010017f1, + 0xfa0613f0, + 0x03f80601, +/* 0x0a02: ctx_xfer */ + 0xe7f000f8, + 0x0007f104, + 0x0303f002, + 0xbd000ed0, +/* 0x0a11: ctx_xfer_idle */ + 0x00e7f104, + 0x03e3f000, + 0xf100eecf, + 0xf42000e4, + 0x11f4f21b, + 0x0d02f406, +/* 0x0a28: ctx_xfer_pre */ + 0xf510f7f0, + 0xf4083621, +/* 0x0a32: ctx_xfer_pre_load */ + 0xf7f01c11, + 0xd721f502, + 0xe921f507, + 0xfe21f507, + 0xf5f4bd07, + 0xf507d721, +/* 0x0a4b: ctx_xfer_exec */ + 0x98087c21, + 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, + 0xf4085e21, +/* 0x0ada: ctx_xfer_post */ + 0xf7f02e02, + 0xd721f502, + 0xf5f4bd07, + 0xf5083621, + 0xf5027f21, + 0xbd07e921, + 0xd721f5f4, + 0x1011f407, + 0xfd400198, + 0x0bf40511, + 0xad21f507, +/* 0x0b05: ctx_xfer_no_post_mmio */ +/* 0x0b05: 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, + 0x00000000, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk110.fuc3 b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk110.fuc3 new file mode 100644 index 000000000..760b4632f --- /dev/null +++ b/kernel/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/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk110.fuc3.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk110.fuc3.h new file mode 100644 index 000000000..899868784 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk110.fuc3.h @@ -0,0 +1,1044 @@ +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, +}; + +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, + 0xd721f502, + 0xe921f507, + 0x10f7f007, + 0x083621f5, + 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, + 0x083621f5, + 0xf500f7f0, + 0xf107d721, + 0xf0010007, + 0x01d00203, + 0xbd04bd00, + 0x1f19f014, + 0x300007f1, + 0xd00203f0, + 0x04bd0001, +/* 0x0564: main */ + 0xf40031f4, + 0xd7f00028, + 0x3921f410, + 0xb1f401f4, + 0xf54001e4, + 0xbd00e91b, + 0x0499f094, + 0x370007f1, + 0xd00203f0, + 0x04bd0009, + 0xc00017f1, + 0xcf0213f0, + 0x27f10011, + 0x23f0c100, + 0x0022cf02, + 0xf51f13c8, + 0xc800890b, + 0x0bf41f23, + 0xb920f962, + 0x94bd0212, + 0xf10799f0, + 0xf0370007, + 0x09d00203, + 0xf404bd00, + 0x31f40132, + 0x0221f502, + 0xf094bd0a, + 0x07f10799, + 0x03f01700, + 0x0009d002, + 0x20fc04bd, + 0x99f094bd, + 0x0007f106, + 0x0203f037, + 0xbd0009d0, + 0x0131f404, + 0x0a0221f5, + 0x99f094bd, + 0x0007f106, + 0x0203f017, + 0xbd0009d0, + 0x330ef404, +/* 0x060c: chsw_prev_no_next */ + 0x12b920f9, + 0x0132f402, + 0xf50232f4, + 0xfc0a0221, + 0x0007f120, + 0x0203f0c0, + 0xbd0002d0, + 0x130ef404, +/* 0x062c: chsw_no_prev */ + 0xf41f23c8, + 0x31f40d0b, + 0x0232f401, + 0x0a0221f5, +/* 0x063c: chsw_done */ + 0xf10127f0, + 0xf0c30007, + 0x02d00203, + 0xbd04bd00, + 0x0499f094, + 0x170007f1, + 0xd00203f0, + 0x04bd0009, + 0xff080ef5, +/* 0x0660: main_not_ctx_switch */ + 0xf401e4b0, + 0xf2b90d1b, + 0x9a21f502, + 0x460ef409, +/* 0x0670: main_not_ctx_chan */ + 0xf402e4b0, + 0x94bd321b, + 0xf10799f0, + 0xf0370007, + 0x09d00203, + 0xf404bd00, + 0x32f40132, + 0x0221f502, + 0xf094bd0a, + 0x07f10799, + 0x03f01700, + 0x0009d002, + 0x0ef404bd, +/* 0x06a5: main_not_ctx_save */ + 0x10ef9411, + 0xf501f5f0, + 0xf5037e21, +/* 0x06b3: main_done */ + 0xbdfeb50e, + 0x1f29f024, + 0x300007f1, + 0xd00203f0, + 0x04bd0002, + 0xfea00ef5, +/* 0x06c8: ih */ + 0x88fe80f9, + 0xf980f901, + 0xf9a0f990, + 0xf9d0f9b0, + 0xbdf0f9e0, + 0x00a7f104, + 0x00a3f002, + 0xc400aacf, + 0x0bf404ab, + 0x10d7f030, + 0x1a00e7f1, + 0xcf00e3f0, + 0xf7f100ee, + 0xf3f01900, + 0x00ffcf00, + 0xb70421f4, + 0xf00400b0, + 0x07f101e7, + 0x03f01d00, + 0x000ed000, +/* 0x071a: ih_no_fifo */ + 0xabe404bd, + 0x0bf40100, + 0x10d7f00d, + 0x4001e7f1, +/* 0x072b: ih_no_ctxsw */ + 0xe40421f4, + 0xf40400ab, + 0xe7f16c0b, + 0xe3f00708, + 0x6821f440, + 0xf102ffb9, + 0xf0040007, + 0x0fd00203, + 0xf104bd00, + 0xf00704e7, + 0x21f440e3, + 0x02ffb968, + 0x030007f1, + 0xd00203f0, + 0x04bd000f, + 0x9450fec7, + 0xf7f102ee, + 0xf3f00700, + 0x00efbb40, + 0xf16821f4, + 0xf0020007, + 0x0fd00203, + 0xf004bd00, + 0x21f503f7, + 0xb7f1037e, + 0xbfb90100, + 0x44e7f102, + 0x40e3f001, +/* 0x079b: ih_no_fwmthd */ + 0xf19d21f4, + 0xbd0504b7, + 0xb4abffb0, + 0xf10f0bf4, + 0xf0070007, + 0x0bd00303, +/* 0x07b3: ih_no_other */ + 0xf104bd00, + 0xf0010007, + 0x0ad00003, + 0xfc04bd00, + 0xfce0fcf0, + 0xfcb0fcd0, + 0xfc90fca0, + 0x0088fe80, + 0x32f480fc, +/* 0x07d7: ctx_4170s */ + 0xf001f800, + 0xffb910f5, + 0x70e7f102, + 0x40e3f041, + 0xf89d21f4, +/* 0x07e9: ctx_4170w */ + 0x70e7f100, + 0x40e3f041, + 0xb96821f4, + 0xf4f002ff, + 0xf01bf410, +/* 0x07fe: ctx_redswitch */ + 0xe7f100f8, + 0xe5f00200, + 0x20e5f040, + 0xf110e5f0, + 0xf0850007, + 0x0ed00103, + 0xf004bd00, +/* 0x081a: ctx_redswitch_delay */ + 0xf2b608f7, + 0xfd1bf401, + 0x0400e5f1, + 0x0100e5f1, + 0x850007f1, + 0xd00103f0, + 0x04bd000e, +/* 0x0836: ctx_86c */ + 0x07f100f8, + 0x03f02300, + 0x000fd002, + 0xffb904bd, + 0x14e7f102, + 0x40e3f08a, + 0xb99d21f4, + 0xe7f102ff, + 0xe3f0a88c, + 0x9d21f441, +/* 0x085e: ctx_mem */ + 0x07f100f8, + 0x03f08400, + 0x000fd002, +/* 0x086a: ctx_mem_wait */ + 0xf7f104bd, + 0xf3f08400, + 0x00ffcf02, + 0xf405fffd, + 0x00f8f31b, +/* 0x087c: ctx_load */ + 0x99f094bd, + 0x0007f105, + 0x0203f037, + 0xbd0009d0, + 0x0ca7f004, + 0xbdd021f4, + 0x0007f1f4, + 0x0203f089, + 0xbd000fd0, + 0x0007f104, + 0x0203f0c1, + 0xbd0002d0, + 0x0007f104, + 0x0203f083, + 0xbd0002d0, + 0x07f7f004, + 0x085e21f5, + 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, +/* 0x099a: ctx_chan */ + 0x21f500f8, + 0xa7f0087c, + 0xd021f40c, + 0xf505f7f0, + 0xf8085e21, +/* 0x09ad: ctx_mmio_exec */ + 0x41039800, + 0x810007f1, + 0xd00203f0, + 0x04bd0003, +/* 0x09be: ctx_mmio_loop */ + 0x34c434bd, + 0x0f1bf4ff, + 0x020057f1, + 0xfa0653f0, + 0x03f80535, +/* 0x09d0: ctx_mmio_pull */ + 0x98804e98, + 0x21f4814f, + 0x0830b69d, + 0xf40112b6, +/* 0x09e2: ctx_mmio_done */ + 0x0398df1b, + 0x0007f116, + 0x0203f081, + 0xbd0003d0, + 0x40008004, + 0x010017f1, + 0xfa0613f0, + 0x03f80601, +/* 0x0a02: ctx_xfer */ + 0xe7f000f8, + 0x0007f104, + 0x0303f002, + 0xbd000ed0, +/* 0x0a11: ctx_xfer_idle */ + 0x00e7f104, + 0x03e3f000, + 0xf100eecf, + 0xf42000e4, + 0x11f4f21b, + 0x0d02f406, +/* 0x0a28: ctx_xfer_pre */ + 0xf510f7f0, + 0xf4083621, +/* 0x0a32: ctx_xfer_pre_load */ + 0xf7f01c11, + 0xd721f502, + 0xe921f507, + 0xfe21f507, + 0xf5f4bd07, + 0xf507d721, +/* 0x0a4b: ctx_xfer_exec */ + 0x98087c21, + 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, + 0xf4085e21, +/* 0x0ada: ctx_xfer_post */ + 0xf7f02e02, + 0xd721f502, + 0xf5f4bd07, + 0xf5083621, + 0xf5027f21, + 0xbd07e921, + 0xd721f5f4, + 0x1011f407, + 0xfd400198, + 0x0bf40511, + 0xad21f507, +/* 0x0b05: ctx_xfer_no_post_mmio */ +/* 0x0b05: 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, + 0x00000000, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk208.fuc5 b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk208.fuc5 new file mode 100644 index 000000000..43243a35f --- /dev/null +++ b/kernel/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/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk208.fuc5.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk208.fuc5.h new file mode 100644 index 000000000..0e98fa4a3 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk208.fuc5.h @@ -0,0 +1,916 @@ +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, +}; + +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, + 0x05c94104, + 0xbd0010fe, + 0x07004024, + 0xbd0002f6, + 0x20034204, + 0x01010080, + 0xbd0002f6, + 0x20044204, + 0x01010480, + 0xbd0002f6, + 0x200b4204, + 0x01010880, + 0xbd0002f6, + 0x200c4204, + 0x01011c80, + 0xbd0002f6, + 0x01039204, + 0x03090080, + 0xbd0003f6, + 0x87044204, + 0xf6040040, + 0x04bd0002, + 0x00400402, + 0x0002f603, + 0x31f404bd, + 0x96048e10, + 0x00657e40, + 0xc7feb200, + 0x01b590f1, + 0x1ff4f003, + 0x01020fb5, + 0x041fbb01, + 0x800112b6, + 0xf6010300, + 0x04bd0001, + 0x01040080, + 0xbd0001f6, + 0x01004104, + 0xa87e020f, + 0xb77e0006, + 0x100f0006, + 0x0006f97e, + 0x98000e98, + 0x207e010f, + 0x14950001, + 0xc0008008, + 0x0004f601, + 0x008004bd, + 0x04f601c1, + 0xb704bd00, + 0xbb130030, + 0xf5b6001f, + 0xd3008002, + 0x000ff601, + 0x15b604bd, + 0x0110b608, + 0xb20814b6, + 0x02687e1f, + 0x001fbb00, + 0x84020398, +/* 0x041f: init_gpc */ + 0xb8502000, + 0x0008044e, + 0x8f7e1fb2, + 0x4eb80000, + 0xbd00010c, + 0x008f7ef4, + 0x044eb800, + 0x8f7e0001, + 0x4eb80000, + 0x0f000100, + 0x008f7e02, + 0x004eb800, +/* 0x044e: init_gpc_wait */ + 0x657e0008, + 0xffc80000, + 0xf90bf41f, + 0x08044eb8, + 0x00657e00, + 0x001fbb00, + 0x800040b7, + 0xf40132b6, + 0x000fb41b, + 0x0006f97e, + 0xa87e000f, + 0x00800006, + 0x01f60201, + 0xbd04bd00, + 0x1f19f014, + 0x02300080, + 0xbd0001f6, +/* 0x0491: main */ + 0x0031f404, + 0x0d0028f4, + 0x00377e10, + 0xf401f400, + 0x4001e4b1, + 0x00c71bf5, + 0x99f094bd, + 0x37008004, + 0x0009f602, + 0x008104bd, + 0x11cf02c0, + 0xc1008200, + 0x0022cf02, + 0xf41f13c8, + 0x23c8770b, + 0x550bf41f, + 0x12b220f9, + 0x99f094bd, + 0x37008007, + 0x0009f602, + 0x32f404bd, + 0x0231f401, + 0x00087c7e, + 0x99f094bd, + 0x17008007, + 0x0009f602, + 0x20fc04bd, + 0x99f094bd, + 0x37008006, + 0x0009f602, + 0x31f404bd, + 0x087c7e01, + 0xf094bd00, + 0x00800699, + 0x09f60217, + 0xf404bd00, +/* 0x0522: chsw_prev_no_next */ + 0x20f92f0e, + 0x32f412b2, + 0x0232f401, + 0x00087c7e, + 0x008020fc, + 0x02f602c0, + 0xf404bd00, +/* 0x053e: chsw_no_prev */ + 0x23c8130e, + 0x0d0bf41f, + 0xf40131f4, + 0x7c7e0232, +/* 0x054e: chsw_done */ + 0x01020008, + 0x02c30080, + 0xbd0002f6, + 0xf094bd04, + 0x00800499, + 0x09f60217, + 0xf504bd00, +/* 0x056b: main_not_ctx_switch */ + 0xb0ff2a0e, + 0x1bf401e4, + 0x7ef2b20c, + 0xf400081c, +/* 0x057a: main_not_ctx_chan */ + 0xe4b0400e, + 0x2c1bf402, + 0x99f094bd, + 0x37008007, + 0x0009f602, + 0x32f404bd, + 0x0232f401, + 0x00087c7e, + 0x99f094bd, + 0x17008007, + 0x0009f602, + 0x0ef404bd, +/* 0x05a9: main_not_ctx_save */ + 0x10ef9411, + 0x7e01f5f0, + 0xf50002f8, +/* 0x05b7: main_done */ + 0xbdfede0e, + 0x1f29f024, + 0x02300080, + 0xbd0002f6, + 0xcc0ef504, +/* 0x05c9: ih */ + 0xfe80f9fe, + 0x80f90188, + 0xa0f990f9, + 0xd0f9b0f9, + 0xf0f9e0f9, + 0x004a04bd, + 0x00aacf02, + 0xf404abc4, + 0x100d230b, + 0xcf1a004e, + 0x004f00ee, + 0x00ffcf19, + 0x0000047e, + 0x0400b0b7, + 0x0040010e, + 0x000ef61d, +/* 0x060a: ih_no_fifo */ + 0xabe404bd, + 0x0bf40100, + 0x4e100d0c, + 0x047e4001, +/* 0x061a: ih_no_ctxsw */ + 0xabe40000, + 0x0bf40400, + 0x07088e56, + 0x00657e40, + 0x80ffb200, + 0xf6020400, + 0x04bd000f, + 0x4007048e, + 0x0000657e, + 0x0080ffb2, + 0x0ff60203, + 0xc704bd00, + 0xee9450fe, + 0x07008f02, + 0x00efbb40, + 0x0000657e, + 0x02020080, + 0xbd000ff6, + 0x7e030f04, + 0x4b0002f8, + 0xbfb20100, + 0x4001448e, + 0x00008f7e, +/* 0x0674: ih_no_fwmthd */ + 0xbd05044b, + 0xb4abffb0, + 0x800c0bf4, + 0xf6030700, + 0x04bd000b, +/* 0x0688: ih_no_other */ + 0xf6010040, + 0x04bd000a, + 0xe0fcf0fc, + 0xb0fcd0fc, + 0x90fca0fc, + 0x88fe80fc, + 0xf480fc00, + 0x01f80032, +/* 0x06a8: ctx_4170s */ + 0xb210f5f0, + 0x41708eff, + 0x008f7e40, +/* 0x06b7: ctx_4170w */ + 0x8e00f800, + 0x7e404170, + 0xb2000065, + 0x10f4f0ff, + 0xf8f31bf4, +/* 0x06c9: ctx_redswitch */ + 0x02004e00, + 0xf040e5f0, + 0xe5f020e5, + 0x85008010, + 0x000ef601, + 0x080f04bd, +/* 0x06e0: ctx_redswitch_delay */ + 0xf401f2b6, + 0xe5f1fd1b, + 0xe5f10400, + 0x00800100, + 0x0ef60185, + 0xf804bd00, +/* 0x06f9: ctx_86c */ + 0x23008000, + 0x000ff602, + 0xffb204bd, + 0x408a148e, + 0x00008f7e, + 0x8c8effb2, + 0x8f7e41a8, + 0x00f80000, +/* 0x0718: ctx_mem */ + 0x02840080, + 0xbd000ff6, +/* 0x0721: ctx_mem_wait */ + 0x84008f04, + 0x00ffcf02, + 0xf405fffd, + 0x00f8f61b, +/* 0x0730: ctx_load */ + 0x99f094bd, + 0x37008005, + 0x0009f602, + 0x0c0a04bd, + 0x0000b87e, + 0x0080f4bd, + 0x0ff60289, + 0x8004bd00, + 0xf602c100, + 0x04bd0002, + 0x02830080, + 0xbd0002f6, + 0x7e070f04, + 0x80000718, + 0xf602c000, + 0x04bd0002, + 0xf0000bfe, + 0x24b61f2a, + 0x0220b604, + 0x99f094bd, + 0x37008008, + 0x0009f602, + 0x008004bd, + 0x02f60281, + 0xd204bd00, + 0x80000000, + 0x800225f0, + 0xf6028800, + 0x04bd0002, + 0x00421001, + 0x0223f002, + 0xf80512fa, + 0xf094bd03, + 0x00800899, + 0x09f60217, + 0x9804bd00, + 0x14b68101, + 0x80029818, + 0xfd0825b6, + 0x01b50512, + 0xf094bd16, + 0x00800999, + 0x09f60237, + 0x8004bd00, + 0xf6028100, + 0x04bd0001, + 0x00800102, + 0x02f60288, + 0x4104bd00, + 0x13f00100, + 0x0501fa06, + 0x94bd03f8, + 0x800999f0, + 0xf6021700, + 0x04bd0009, + 0x99f094bd, + 0x17008005, + 0x0009f602, + 0x00f804bd, +/* 0x081c: ctx_chan */ + 0x0007307e, + 0xb87e0c0a, + 0x050f0000, + 0x0007187e, +/* 0x082e: ctx_mmio_exec */ + 0x039800f8, + 0x81008041, + 0x0003f602, + 0x34bd04bd, +/* 0x083c: ctx_mmio_loop */ + 0xf4ff34c4, + 0x00450e1b, + 0x0653f002, + 0xf80535fa, +/* 0x084d: ctx_mmio_pull */ + 0x804e9803, + 0x7e814f98, + 0xb600008f, + 0x12b60830, + 0xdf1bf401, +/* 0x0860: ctx_mmio_done */ + 0x80160398, + 0xf6028100, + 0x04bd0003, + 0x414000b5, + 0x13f00100, + 0x0601fa06, + 0x00f803f8, +/* 0x087c: ctx_xfer */ + 0x0080040e, + 0x0ef60302, +/* 0x0887: ctx_xfer_idle */ + 0x8e04bd00, + 0xcf030000, + 0xe4f100ee, + 0x1bf42000, + 0x0611f4f5, +/* 0x089b: ctx_xfer_pre */ + 0x0f0c02f4, + 0x06f97e10, + 0x1b11f400, +/* 0x08a4: ctx_xfer_pre_load */ + 0xa87e020f, + 0xb77e0006, + 0xc97e0006, + 0xf4bd0006, + 0x0006a87e, + 0x0007307e, +/* 0x08bc: ctx_xfer_exec */ + 0xbd160198, + 0x05008024, + 0x0002f601, + 0x1fb204bd, + 0x41a5008e, + 0x00008f7e, + 0xf001fcf0, + 0x24b6022c, + 0x05f2fd01, + 0x048effb2, + 0x8f7e41a5, + 0x167e0000, + 0x24bd0002, + 0x0247fc80, + 0xbd0002f6, + 0x012cf004, + 0x800320b6, + 0xf6024afc, + 0x04bd0002, + 0xf001acf0, + 0x000b06a5, + 0x98000c98, + 0x000e010d, + 0x00013d7e, + 0xec7e080a, + 0x0a7e0000, + 0x01f40002, + 0x7e0c0a12, + 0x0f0000b8, + 0x07187e05, + 0x2d02f400, +/* 0x0938: ctx_xfer_post */ + 0xa87e020f, + 0xf4bd0006, + 0x0006f97e, + 0x0002277e, + 0x0006b77e, + 0xa87ef4bd, + 0x11f40006, + 0x40019810, + 0xf40511fd, + 0x2e7e070b, +/* 0x0962: ctx_xfer_no_post_mmio */ +/* 0x0962: ctx_xfer_done */ + 0x00f80008, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 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/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgm107.fuc5 b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgm107.fuc5 new file mode 100644 index 000000000..27591b308 --- /dev/null +++ b/kernel/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/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgm107.fuc5.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgm107.fuc5.h new file mode 100644 index 000000000..5f953c5c2 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgm107.fuc5.h @@ -0,0 +1,916 @@ +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, +}; + +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, + 0x05c94104, + 0xbd0010fe, + 0x07004024, + 0xbd0002f6, + 0x20034204, + 0x01010080, + 0xbd0002f6, + 0x20044204, + 0x01010480, + 0xbd0002f6, + 0x200b4204, + 0x01010880, + 0xbd0002f6, + 0x200c4204, + 0x01011c80, + 0xbd0002f6, + 0x01039204, + 0x03090080, + 0xbd0003f6, + 0x87044204, + 0xf6040040, + 0x04bd0002, + 0x00400402, + 0x0002f603, + 0x31f404bd, + 0x96048e10, + 0x00657e40, + 0xc7feb200, + 0x01b590f1, + 0x1ff4f003, + 0x01020fb5, + 0x041fbb01, + 0x800112b6, + 0xf6010300, + 0x04bd0001, + 0x01040080, + 0xbd0001f6, + 0x01004104, + 0xa87e020f, + 0xb77e0006, + 0x100f0006, + 0x0006f97e, + 0x98000e98, + 0x207e010f, + 0x14950001, + 0xc0008008, + 0x0004f601, + 0x008004bd, + 0x04f601c1, + 0xb704bd00, + 0xbb130030, + 0xf5b6001f, + 0xd3008002, + 0x000ff601, + 0x15b604bd, + 0x0110b608, + 0xb20814b6, + 0x02687e1f, + 0x001fbb00, + 0x84020398, +/* 0x041f: init_gpc */ + 0xb8502000, + 0x0008044e, + 0x8f7e1fb2, + 0x4eb80000, + 0xbd00010c, + 0x008f7ef4, + 0x044eb800, + 0x8f7e0001, + 0x4eb80000, + 0x0f000100, + 0x008f7e02, + 0x004eb800, +/* 0x044e: init_gpc_wait */ + 0x657e0008, + 0xffc80000, + 0xf90bf41f, + 0x08044eb8, + 0x00657e00, + 0x001fbb00, + 0x800040b7, + 0xf40132b6, + 0x000fb41b, + 0x0006f97e, + 0xa87e000f, + 0x00800006, + 0x01f60201, + 0xbd04bd00, + 0x1f19f014, + 0x02300080, + 0xbd0001f6, +/* 0x0491: main */ + 0x0031f404, + 0x0d0028f4, + 0x00377e10, + 0xf401f400, + 0x4001e4b1, + 0x00c71bf5, + 0x99f094bd, + 0x37008004, + 0x0009f602, + 0x008104bd, + 0x11cf02c0, + 0xc1008200, + 0x0022cf02, + 0xf41f13c8, + 0x23c8770b, + 0x550bf41f, + 0x12b220f9, + 0x99f094bd, + 0x37008007, + 0x0009f602, + 0x32f404bd, + 0x0231f401, + 0x00087c7e, + 0x99f094bd, + 0x17008007, + 0x0009f602, + 0x20fc04bd, + 0x99f094bd, + 0x37008006, + 0x0009f602, + 0x31f404bd, + 0x087c7e01, + 0xf094bd00, + 0x00800699, + 0x09f60217, + 0xf404bd00, +/* 0x0522: chsw_prev_no_next */ + 0x20f92f0e, + 0x32f412b2, + 0x0232f401, + 0x00087c7e, + 0x008020fc, + 0x02f602c0, + 0xf404bd00, +/* 0x053e: chsw_no_prev */ + 0x23c8130e, + 0x0d0bf41f, + 0xf40131f4, + 0x7c7e0232, +/* 0x054e: chsw_done */ + 0x01020008, + 0x02c30080, + 0xbd0002f6, + 0xf094bd04, + 0x00800499, + 0x09f60217, + 0xf504bd00, +/* 0x056b: main_not_ctx_switch */ + 0xb0ff2a0e, + 0x1bf401e4, + 0x7ef2b20c, + 0xf400081c, +/* 0x057a: main_not_ctx_chan */ + 0xe4b0400e, + 0x2c1bf402, + 0x99f094bd, + 0x37008007, + 0x0009f602, + 0x32f404bd, + 0x0232f401, + 0x00087c7e, + 0x99f094bd, + 0x17008007, + 0x0009f602, + 0x0ef404bd, +/* 0x05a9: main_not_ctx_save */ + 0x10ef9411, + 0x7e01f5f0, + 0xf50002f8, +/* 0x05b7: main_done */ + 0xbdfede0e, + 0x1f29f024, + 0x02300080, + 0xbd0002f6, + 0xcc0ef504, +/* 0x05c9: ih */ + 0xfe80f9fe, + 0x80f90188, + 0xa0f990f9, + 0xd0f9b0f9, + 0xf0f9e0f9, + 0x004a04bd, + 0x00aacf02, + 0xf404abc4, + 0x100d230b, + 0xcf1a004e, + 0x004f00ee, + 0x00ffcf19, + 0x0000047e, + 0x0400b0b7, + 0x0040010e, + 0x000ef61d, +/* 0x060a: ih_no_fifo */ + 0xabe404bd, + 0x0bf40100, + 0x4e100d0c, + 0x047e4001, +/* 0x061a: ih_no_ctxsw */ + 0xabe40000, + 0x0bf40400, + 0x07088e56, + 0x00657e40, + 0x80ffb200, + 0xf6020400, + 0x04bd000f, + 0x4007048e, + 0x0000657e, + 0x0080ffb2, + 0x0ff60203, + 0xc704bd00, + 0xee9450fe, + 0x07008f02, + 0x00efbb40, + 0x0000657e, + 0x02020080, + 0xbd000ff6, + 0x7e030f04, + 0x4b0002f8, + 0xbfb20100, + 0x4001448e, + 0x00008f7e, +/* 0x0674: ih_no_fwmthd */ + 0xbd05044b, + 0xb4abffb0, + 0x800c0bf4, + 0xf6030700, + 0x04bd000b, +/* 0x0688: ih_no_other */ + 0xf6010040, + 0x04bd000a, + 0xe0fcf0fc, + 0xb0fcd0fc, + 0x90fca0fc, + 0x88fe80fc, + 0xf480fc00, + 0x01f80032, +/* 0x06a8: ctx_4170s */ + 0xb210f5f0, + 0x41708eff, + 0x008f7e40, +/* 0x06b7: ctx_4170w */ + 0x8e00f800, + 0x7e404170, + 0xb2000065, + 0x10f4f0ff, + 0xf8f31bf4, +/* 0x06c9: ctx_redswitch */ + 0x02004e00, + 0xf040e5f0, + 0xe5f020e5, + 0x85008010, + 0x000ef601, + 0x080f04bd, +/* 0x06e0: ctx_redswitch_delay */ + 0xf401f2b6, + 0xe5f1fd1b, + 0xe5f10400, + 0x00800100, + 0x0ef60185, + 0xf804bd00, +/* 0x06f9: ctx_86c */ + 0x23008000, + 0x000ff602, + 0xffb204bd, + 0x408a148e, + 0x00008f7e, + 0x8c8effb2, + 0x8f7e41a8, + 0x00f80000, +/* 0x0718: ctx_mem */ + 0x02840080, + 0xbd000ff6, +/* 0x0721: ctx_mem_wait */ + 0x84008f04, + 0x00ffcf02, + 0xf405fffd, + 0x00f8f61b, +/* 0x0730: ctx_load */ + 0x99f094bd, + 0x37008005, + 0x0009f602, + 0x0c0a04bd, + 0x0000b87e, + 0x0080f4bd, + 0x0ff60289, + 0x8004bd00, + 0xf602c100, + 0x04bd0002, + 0x02830080, + 0xbd0002f6, + 0x7e070f04, + 0x80000718, + 0xf602c000, + 0x04bd0002, + 0xf0000bfe, + 0x24b61f2a, + 0x0220b604, + 0x99f094bd, + 0x37008008, + 0x0009f602, + 0x008004bd, + 0x02f60281, + 0xd204bd00, + 0x80000000, + 0x800225f0, + 0xf6028800, + 0x04bd0002, + 0x00421001, + 0x0223f002, + 0xf80512fa, + 0xf094bd03, + 0x00800899, + 0x09f60217, + 0x9804bd00, + 0x14b68101, + 0x80029818, + 0xfd0825b6, + 0x01b50512, + 0xf094bd16, + 0x00800999, + 0x09f60237, + 0x8004bd00, + 0xf6028100, + 0x04bd0001, + 0x00800102, + 0x02f60288, + 0x4104bd00, + 0x13f00100, + 0x0501fa06, + 0x94bd03f8, + 0x800999f0, + 0xf6021700, + 0x04bd0009, + 0x99f094bd, + 0x17008005, + 0x0009f602, + 0x00f804bd, +/* 0x081c: ctx_chan */ + 0x0007307e, + 0xb87e0c0a, + 0x050f0000, + 0x0007187e, +/* 0x082e: ctx_mmio_exec */ + 0x039800f8, + 0x81008041, + 0x0003f602, + 0x34bd04bd, +/* 0x083c: ctx_mmio_loop */ + 0xf4ff34c4, + 0x00450e1b, + 0x0653f002, + 0xf80535fa, +/* 0x084d: ctx_mmio_pull */ + 0x804e9803, + 0x7e814f98, + 0xb600008f, + 0x12b60830, + 0xdf1bf401, +/* 0x0860: ctx_mmio_done */ + 0x80160398, + 0xf6028100, + 0x04bd0003, + 0x414000b5, + 0x13f00100, + 0x0601fa06, + 0x00f803f8, +/* 0x087c: ctx_xfer */ + 0x0080040e, + 0x0ef60302, +/* 0x0887: ctx_xfer_idle */ + 0x8e04bd00, + 0xcf030000, + 0xe4f100ee, + 0x1bf42000, + 0x0611f4f5, +/* 0x089b: ctx_xfer_pre */ + 0x0f0c02f4, + 0x06f97e10, + 0x1b11f400, +/* 0x08a4: ctx_xfer_pre_load */ + 0xa87e020f, + 0xb77e0006, + 0xc97e0006, + 0xf4bd0006, + 0x0006a87e, + 0x0007307e, +/* 0x08bc: ctx_xfer_exec */ + 0xbd160198, + 0x05008024, + 0x0002f601, + 0x1fb204bd, + 0x41a5008e, + 0x00008f7e, + 0xf001fcf0, + 0x24b6022c, + 0x05f2fd01, + 0x048effb2, + 0x8f7e41a5, + 0x167e0000, + 0x24bd0002, + 0x0247fc80, + 0xbd0002f6, + 0x012cf004, + 0x800320b6, + 0xf6024afc, + 0x04bd0002, + 0xf001acf0, + 0x000b06a5, + 0x98000c98, + 0x000e010d, + 0x00013d7e, + 0xec7e080a, + 0x0a7e0000, + 0x01f40002, + 0x7e0c0a12, + 0x0f0000b8, + 0x07187e05, + 0x2d02f400, +/* 0x0938: ctx_xfer_post */ + 0xa87e020f, + 0xf4bd0006, + 0x0006f97e, + 0x0002277e, + 0x0006b77e, + 0xa87ef4bd, + 0x11f40006, + 0x40019810, + 0xf40511fd, + 0x2e7e070b, +/* 0x0962: ctx_xfer_no_post_mmio */ +/* 0x0962: ctx_xfer_done */ + 0x00f80008, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 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/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/macros.fuc b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/macros.fuc new file mode 100644 index 000000000..fa6180664 --- /dev/null +++ b/kernel/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/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/os.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/os.h new file mode 100644 index 000000000..1718ae4e8 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/os.h @@ -0,0 +1,8 @@ +#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/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c new file mode 100644 index 000000000..5606c25e5 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c @@ -0,0 +1,1692 @@ +/* + * 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 + +/******************************************************************************* + * Zero Bandwidth Clear + ******************************************************************************/ + +static void +gf100_gr_zbc_clear_color(struct gf100_gr_priv *priv, int zbc) +{ + if (priv->zbc_color[zbc].format) { + nv_wr32(priv, 0x405804, priv->zbc_color[zbc].ds[0]); + nv_wr32(priv, 0x405808, priv->zbc_color[zbc].ds[1]); + nv_wr32(priv, 0x40580c, priv->zbc_color[zbc].ds[2]); + nv_wr32(priv, 0x405810, priv->zbc_color[zbc].ds[3]); + } + nv_wr32(priv, 0x405814, priv->zbc_color[zbc].format); + nv_wr32(priv, 0x405820, zbc); + nv_wr32(priv, 0x405824, 0x00000004); /* TRIGGER | WRITE | COLOR */ +} + +static int +gf100_gr_zbc_color_get(struct gf100_gr_priv *priv, int format, + const u32 ds[4], const u32 l2[4]) +{ + struct nvkm_ltc *ltc = nvkm_ltc(priv); + int zbc = -ENOSPC, i; + + for (i = ltc->zbc_min; i <= ltc->zbc_max; i++) { + if (priv->zbc_color[i].format) { + if (priv->zbc_color[i].format != format) + continue; + if (memcmp(priv->zbc_color[i].ds, ds, sizeof( + priv->zbc_color[i].ds))) + continue; + if (memcmp(priv->zbc_color[i].l2, l2, sizeof( + priv->zbc_color[i].l2))) { + WARN_ON(1); + return -EINVAL; + } + return i; + } else { + zbc = (zbc < 0) ? i : zbc; + } + } + + if (zbc < 0) + return zbc; + + memcpy(priv->zbc_color[zbc].ds, ds, sizeof(priv->zbc_color[zbc].ds)); + memcpy(priv->zbc_color[zbc].l2, l2, sizeof(priv->zbc_color[zbc].l2)); + priv->zbc_color[zbc].format = format; + ltc->zbc_color_get(ltc, zbc, l2); + gf100_gr_zbc_clear_color(priv, zbc); + return zbc; +} + +static void +gf100_gr_zbc_clear_depth(struct gf100_gr_priv *priv, int zbc) +{ + if (priv->zbc_depth[zbc].format) + nv_wr32(priv, 0x405818, priv->zbc_depth[zbc].ds); + nv_wr32(priv, 0x40581c, priv->zbc_depth[zbc].format); + nv_wr32(priv, 0x405820, zbc); + nv_wr32(priv, 0x405824, 0x00000005); /* TRIGGER | WRITE | DEPTH */ +} + +static int +gf100_gr_zbc_depth_get(struct gf100_gr_priv *priv, int format, + const u32 ds, const u32 l2) +{ + struct nvkm_ltc *ltc = nvkm_ltc(priv); + int zbc = -ENOSPC, i; + + for (i = ltc->zbc_min; i <= ltc->zbc_max; i++) { + if (priv->zbc_depth[i].format) { + if (priv->zbc_depth[i].format != format) + continue; + if (priv->zbc_depth[i].ds != ds) + continue; + if (priv->zbc_depth[i].l2 != l2) { + WARN_ON(1); + return -EINVAL; + } + return i; + } else { + zbc = (zbc < 0) ? i : zbc; + } + } + + if (zbc < 0) + return zbc; + + priv->zbc_depth[zbc].format = format; + priv->zbc_depth[zbc].ds = ds; + priv->zbc_depth[zbc].l2 = l2; + ltc->zbc_depth_get(ltc, zbc, l2); + gf100_gr_zbc_clear_depth(priv, zbc); + return zbc; +} + +/******************************************************************************* + * Graphics object classes + ******************************************************************************/ + +static int +gf100_fermi_mthd_zbc_color(struct nvkm_object *object, void *data, u32 size) +{ + struct gf100_gr_priv *priv = (void *)object->engine; + union { + struct fermi_a_zbc_color_v0 v0; + } *args = data; + int ret; + + if (nvif_unpack(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(priv, 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_priv *priv = (void *)object->engine; + union { + struct fermi_a_zbc_depth_v0 v0; + } *args = data; + int ret; + + if (nvif_unpack(args->v0, 0, 0, false)) { + switch (args->v0.format) { + case FERMI_A_ZBC_DEPTH_V0_FMT_FP32: + ret = gf100_gr_zbc_depth_get(priv, 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) +{ + 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; +} + +struct nvkm_ofuncs +gf100_fermi_ofuncs = { + .ctor = _nvkm_object_ctor, + .dtor = nvkm_object_destroy, + .init = nvkm_object_init, + .fini = nvkm_object_fini, + .mthd = gf100_fermi_mthd, +}; + +static int +gf100_gr_set_shader_exceptions(struct nvkm_object *object, u32 mthd, + void *pdata, u32 size) +{ + struct gf100_gr_priv *priv = (void *)object->engine; + if (size >= sizeof(u32)) { + u32 data = *(u32 *)pdata ? 0xffffffff : 0x00000000; + nv_wr32(priv, 0x419e44, data); + nv_wr32(priv, 0x419e4c, data); + return 0; + } + return -EINVAL; +} + +struct nvkm_omthds +gf100_gr_9097_omthds[] = { + { 0x1528, 0x1528, gf100_gr_set_shader_exceptions }, + {} +}; + +struct nvkm_omthds +gf100_gr_90c0_omthds[] = { + { 0x1528, 0x1528, gf100_gr_set_shader_exceptions }, + {} +}; + +struct nvkm_oclass +gf100_gr_sclass[] = { + { FERMI_TWOD_A, &nvkm_object_ofuncs }, + { FERMI_MEMORY_TO_MEMORY_FORMAT_A, &nvkm_object_ofuncs }, + { FERMI_A, &gf100_fermi_ofuncs, gf100_gr_9097_omthds }, + { FERMI_COMPUTE_A, &nvkm_object_ofuncs, gf100_gr_90c0_omthds }, + {} +}; + +/******************************************************************************* + * PGRAPH context + ******************************************************************************/ + +int +gf100_gr_context_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *args, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_vm *vm = nvkm_client(parent)->vm; + struct gf100_gr_priv *priv = (void *)engine; + struct gf100_gr_data *data = priv->mmio_data; + struct gf100_gr_mmio *mmio = priv->mmio_list; + struct gf100_gr_chan *chan; + int ret, i; + + /* allocate memory for context, and fill with default values */ + ret = nvkm_gr_context_create(parent, engine, oclass, NULL, + priv->size, 0x100, + NVOBJ_FLAG_ZERO_ALLOC, &chan); + *pobject = nv_object(chan); + if (ret) + return ret; + + /* 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_gpuobj_new(nv_object(chan), NULL, 0x1000, 0x100, 0, + &chan->mmio); + if (ret) + return ret; + + ret = nvkm_gpuobj_map_vm(nv_gpuobj(chan->mmio), vm, + NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS, + &chan->mmio_vma); + if (ret) + return ret; + + /* allocate buffers referenced by mmio list */ + for (i = 0; data->size && i < ARRAY_SIZE(priv->mmio_data); i++) { + ret = nvkm_gpuobj_new(nv_object(chan), NULL, data->size, + data->align, 0, &chan->data[i].mem); + if (ret) + return ret; + + ret = nvkm_gpuobj_map_vm(chan->data[i].mem, vm, data->access, + &chan->data[i].vma); + if (ret) + return ret; + + data++; + } + + /* finally, fill in the mmio list and point the context at it */ + for (i = 0; mmio->addr && i < ARRAY_SIZE(priv->mmio_list); i++) { + u32 addr = mmio->addr; + u32 data = mmio->data; + + if (mmio->buffer >= 0) { + u64 info = chan->data[mmio->buffer].vma.offset; + data |= info >> mmio->shift; + } + + nv_wo32(chan->mmio, chan->mmio_nr++ * 4, addr); + nv_wo32(chan->mmio, chan->mmio_nr++ * 4, data); + mmio++; + } + + for (i = 0; i < priv->size; i += 4) + nv_wo32(chan, i, priv->data[i / 4]); + + if (!priv->firmware) { + nv_wo32(chan, 0x00, chan->mmio_nr / 2); + nv_wo32(chan, 0x04, chan->mmio_vma.offset >> 8); + } else { + nv_wo32(chan, 0xf4, 0); + nv_wo32(chan, 0xf8, 0); + nv_wo32(chan, 0x10, chan->mmio_nr / 2); + nv_wo32(chan, 0x14, lower_32_bits(chan->mmio_vma.offset)); + nv_wo32(chan, 0x18, upper_32_bits(chan->mmio_vma.offset)); + nv_wo32(chan, 0x1c, 1); + nv_wo32(chan, 0x20, 0); + nv_wo32(chan, 0x28, 0); + nv_wo32(chan, 0x2c, 0); + } + + return 0; +} + +void +gf100_gr_context_dtor(struct nvkm_object *object) +{ + struct gf100_gr_chan *chan = (void *)object; + int i; + + for (i = 0; i < ARRAY_SIZE(chan->data); i++) { + nvkm_gpuobj_unmap(&chan->data[i].vma); + nvkm_gpuobj_ref(NULL, &chan->data[i].mem); + } + + nvkm_gpuobj_unmap(&chan->mmio_vma); + nvkm_gpuobj_ref(NULL, &chan->mmio); + + nvkm_gr_context_destroy(&chan->base); +} + +/******************************************************************************* + * 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 + ******************************************************************************/ + +void +gf100_gr_zbc_init(struct gf100_gr_priv *priv) +{ + 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 = nvkm_ltc(priv); + int index; + + if (!priv->zbc_color[0].format) { + gf100_gr_zbc_color_get(priv, 1, & zero[0], &zero[4]); + gf100_gr_zbc_color_get(priv, 2, & one[0], &one[4]); + gf100_gr_zbc_color_get(priv, 4, &f32_0[0], &f32_0[4]); + gf100_gr_zbc_color_get(priv, 4, &f32_1[0], &f32_1[4]); + gf100_gr_zbc_depth_get(priv, 1, 0x00000000, 0x00000000); + gf100_gr_zbc_depth_get(priv, 1, 0x3f800000, 0x3f800000); + } + + for (index = ltc->zbc_min; index <= ltc->zbc_max; index++) + gf100_gr_zbc_clear_color(priv, index); + for (index = ltc->zbc_min; index <= ltc->zbc_max; index++) + gf100_gr_zbc_clear_depth(priv, index); +} + +void +gf100_gr_mmio(struct gf100_gr_priv *priv, const struct gf100_gr_pack *p) +{ + 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) { + nv_wr32(priv, addr, init->data); + addr += init->pitch; + } + } +} + +void +gf100_gr_icmd(struct gf100_gr_priv *priv, const struct gf100_gr_pack *p) +{ + const struct gf100_gr_pack *pack; + const struct gf100_gr_init *init; + u32 data = 0; + + nv_wr32(priv, 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) { + nv_wr32(priv, 0x400204, init->data); + data = init->data; + } + + while (addr < next) { + nv_wr32(priv, 0x400200, addr); + nv_wait(priv, 0x400700, 0x00000002, 0x00000000); + addr += init->pitch; + } + } + + nv_wr32(priv, 0x400208, 0x00000000); +} + +void +gf100_gr_mthd(struct gf100_gr_priv *priv, const struct gf100_gr_pack *p) +{ + 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) { + nv_wr32(priv, 0x40448c, init->data); + data = init->data; + } + + while (addr < next) { + nv_wr32(priv, 0x404488, ctrl | (addr << 14)); + addr += init->pitch; + } + } +} + +u64 +gf100_gr_units(struct nvkm_gr *gr) +{ + struct gf100_gr_priv *priv = (void *)gr; + u64 cfg; + + cfg = (u32)priv->gpc_nr; + cfg |= (u32)priv->tpc_total << 8; + cfg |= (u64)priv->rop_nr << 32; + + return cfg; +} + +static const struct nvkm_enum gk104_sked_error[] = { + { 7, "CONSTANT_BUFFER_SIZE" }, + { 9, "LOCAL_MEMORY_SIZE_POS" }, + { 10, "LOCAL_MEMORY_SIZE_NEG" }, + { 11, "WARP_CSTACK_SIZE" }, + { 12, "TOTAL_TEMP_SIZE" }, + { 13, "REGISTER_COUNT" }, + { 18, "TOTAL_THREADS" }, + { 20, "PROGRAM_OFFSET" }, + { 21, "SHARED_MEMORY_SIZE" }, + { 25, "SHARED_CONFIG_TOO_SMALL" }, + { 26, "TOTAL_REGISTER_COUNT" }, + {} +}; + +static const struct nvkm_enum gf100_gpc_rop_error[] = { + { 1, "RT_PITCH_OVERRUN" }, + { 4, "RT_WIDTH_OVERRUN" }, + { 5, "RT_HEIGHT_OVERRUN" }, + { 7, "ZETA_STORAGE_TYPE_MISMATCH" }, + { 8, "RT_STORAGE_TYPE_MISMATCH" }, + { 10, "RT_LINEAR_MISMATCH" }, + {} +}; + +static void +gf100_gr_trap_gpc_rop(struct gf100_gr_priv *priv, int gpc) +{ + u32 trap[4]; + int i; + + trap[0] = nv_rd32(priv, GPC_UNIT(gpc, 0x0420)); + trap[1] = nv_rd32(priv, GPC_UNIT(gpc, 0x0434)); + trap[2] = nv_rd32(priv, GPC_UNIT(gpc, 0x0438)); + trap[3] = nv_rd32(priv, GPC_UNIT(gpc, 0x043c)); + + nv_error(priv, "GPC%d/PROP trap:", gpc); + for (i = 0; i <= 29; ++i) { + if (!(trap[0] & (1 << i))) + continue; + pr_cont(" "); + nvkm_enum_print(gf100_gpc_rop_error, i); + } + pr_cont("\n"); + + nv_error(priv, "x = %u, y = %u, format = %x, storage type = %x\n", + trap[1] & 0xffff, trap[1] >> 16, (trap[2] >> 8) & 0x3f, + trap[3] & 0xff); + nv_wr32(priv, GPC_UNIT(gpc, 0x0420), 0xc0000000); +} + +static const struct nvkm_enum gf100_mp_warp_error[] = { + { 0x00, "NO_ERROR" }, + { 0x01, "STACK_MISMATCH" }, + { 0x05, "MISALIGNED_PC" }, + { 0x08, "MISALIGNED_GPR" }, + { 0x09, "INVALID_OPCODE" }, + { 0x0d, "GPR_OUT_OF_BOUNDS" }, + { 0x0e, "MEM_OUT_OF_BOUNDS" }, + { 0x0f, "UNALIGNED_MEM_ACCESS" }, + { 0x11, "INVALID_PARAM" }, + {} +}; + +static const struct nvkm_bitfield gf100_mp_global_error[] = { + { 0x00000004, "MULTIPLE_WARP_ERRORS" }, + { 0x00000008, "OUT_OF_STACK_SPACE" }, + {} +}; + +static void +gf100_gr_trap_mp(struct gf100_gr_priv *priv, int gpc, int tpc) +{ + u32 werr = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x648)); + u32 gerr = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x650)); + + nv_error(priv, "GPC%i/TPC%i/MP trap:", gpc, tpc); + nvkm_bitfield_print(gf100_mp_global_error, gerr); + if (werr) { + pr_cont(" "); + nvkm_enum_print(gf100_mp_warp_error, werr & 0xffff); + } + pr_cont("\n"); + + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x648), 0x00000000); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x650), gerr); +} + +static void +gf100_gr_trap_tpc(struct gf100_gr_priv *priv, int gpc, int tpc) +{ + u32 stat = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x0508)); + + if (stat & 0x00000001) { + u32 trap = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x0224)); + nv_error(priv, "GPC%d/TPC%d/TEX: 0x%08x\n", gpc, tpc, trap); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x0224), 0xc0000000); + stat &= ~0x00000001; + } + + if (stat & 0x00000002) { + gf100_gr_trap_mp(priv, gpc, tpc); + stat &= ~0x00000002; + } + + if (stat & 0x00000004) { + u32 trap = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x0084)); + nv_error(priv, "GPC%d/TPC%d/POLY: 0x%08x\n", gpc, tpc, trap); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x0084), 0xc0000000); + stat &= ~0x00000004; + } + + if (stat & 0x00000008) { + u32 trap = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x048c)); + nv_error(priv, "GPC%d/TPC%d/L1C: 0x%08x\n", gpc, tpc, trap); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x048c), 0xc0000000); + stat &= ~0x00000008; + } + + if (stat) { + nv_error(priv, "GPC%d/TPC%d/0x%08x: unknown\n", gpc, tpc, stat); + } +} + +static void +gf100_gr_trap_gpc(struct gf100_gr_priv *priv, int gpc) +{ + u32 stat = nv_rd32(priv, GPC_UNIT(gpc, 0x2c90)); + int tpc; + + if (stat & 0x00000001) { + gf100_gr_trap_gpc_rop(priv, gpc); + stat &= ~0x00000001; + } + + if (stat & 0x00000002) { + u32 trap = nv_rd32(priv, GPC_UNIT(gpc, 0x0900)); + nv_error(priv, "GPC%d/ZCULL: 0x%08x\n", gpc, trap); + nv_wr32(priv, GPC_UNIT(gpc, 0x0900), 0xc0000000); + stat &= ~0x00000002; + } + + if (stat & 0x00000004) { + u32 trap = nv_rd32(priv, GPC_UNIT(gpc, 0x1028)); + nv_error(priv, "GPC%d/CCACHE: 0x%08x\n", gpc, trap); + nv_wr32(priv, GPC_UNIT(gpc, 0x1028), 0xc0000000); + stat &= ~0x00000004; + } + + if (stat & 0x00000008) { + u32 trap = nv_rd32(priv, GPC_UNIT(gpc, 0x0824)); + nv_error(priv, "GPC%d/ESETUP: 0x%08x\n", gpc, trap); + nv_wr32(priv, GPC_UNIT(gpc, 0x0824), 0xc0000000); + stat &= ~0x00000009; + } + + for (tpc = 0; tpc < priv->tpc_nr[gpc]; tpc++) { + u32 mask = 0x00010000 << tpc; + if (stat & mask) { + gf100_gr_trap_tpc(priv, gpc, tpc); + nv_wr32(priv, GPC_UNIT(gpc, 0x2c90), mask); + stat &= ~mask; + } + } + + if (stat) { + nv_error(priv, "GPC%d/0x%08x: unknown\n", gpc, stat); + } +} + +static void +gf100_gr_trap_intr(struct gf100_gr_priv *priv) +{ + u32 trap = nv_rd32(priv, 0x400108); + int rop, gpc, i; + + if (trap & 0x00000001) { + u32 stat = nv_rd32(priv, 0x404000); + nv_error(priv, "DISPATCH 0x%08x\n", stat); + nv_wr32(priv, 0x404000, 0xc0000000); + nv_wr32(priv, 0x400108, 0x00000001); + trap &= ~0x00000001; + } + + if (trap & 0x00000002) { + u32 stat = nv_rd32(priv, 0x404600); + nv_error(priv, "M2MF 0x%08x\n", stat); + nv_wr32(priv, 0x404600, 0xc0000000); + nv_wr32(priv, 0x400108, 0x00000002); + trap &= ~0x00000002; + } + + if (trap & 0x00000008) { + u32 stat = nv_rd32(priv, 0x408030); + nv_error(priv, "CCACHE 0x%08x\n", stat); + nv_wr32(priv, 0x408030, 0xc0000000); + nv_wr32(priv, 0x400108, 0x00000008); + trap &= ~0x00000008; + } + + if (trap & 0x00000010) { + u32 stat = nv_rd32(priv, 0x405840); + nv_error(priv, "SHADER 0x%08x\n", stat); + nv_wr32(priv, 0x405840, 0xc0000000); + nv_wr32(priv, 0x400108, 0x00000010); + trap &= ~0x00000010; + } + + if (trap & 0x00000040) { + u32 stat = nv_rd32(priv, 0x40601c); + nv_error(priv, "UNK6 0x%08x\n", stat); + nv_wr32(priv, 0x40601c, 0xc0000000); + nv_wr32(priv, 0x400108, 0x00000040); + trap &= ~0x00000040; + } + + if (trap & 0x00000080) { + u32 stat = nv_rd32(priv, 0x404490); + nv_error(priv, "MACRO 0x%08x\n", stat); + nv_wr32(priv, 0x404490, 0xc0000000); + nv_wr32(priv, 0x400108, 0x00000080); + trap &= ~0x00000080; + } + + if (trap & 0x00000100) { + u32 stat = nv_rd32(priv, 0x407020); + + nv_error(priv, "SKED:"); + for (i = 0; i <= 29; ++i) { + if (!(stat & (1 << i))) + continue; + pr_cont(" "); + nvkm_enum_print(gk104_sked_error, i); + } + pr_cont("\n"); + + if (stat & 0x3fffffff) + nv_wr32(priv, 0x407020, 0x40000000); + nv_wr32(priv, 0x400108, 0x00000100); + trap &= ~0x00000100; + } + + if (trap & 0x01000000) { + u32 stat = nv_rd32(priv, 0x400118); + for (gpc = 0; stat && gpc < priv->gpc_nr; gpc++) { + u32 mask = 0x00000001 << gpc; + if (stat & mask) { + gf100_gr_trap_gpc(priv, gpc); + nv_wr32(priv, 0x400118, mask); + stat &= ~mask; + } + } + nv_wr32(priv, 0x400108, 0x01000000); + trap &= ~0x01000000; + } + + if (trap & 0x02000000) { + for (rop = 0; rop < priv->rop_nr; rop++) { + u32 statz = nv_rd32(priv, ROP_UNIT(rop, 0x070)); + u32 statc = nv_rd32(priv, ROP_UNIT(rop, 0x144)); + nv_error(priv, "ROP%d 0x%08x 0x%08x\n", + rop, statz, statc); + nv_wr32(priv, ROP_UNIT(rop, 0x070), 0xc0000000); + nv_wr32(priv, ROP_UNIT(rop, 0x144), 0xc0000000); + } + nv_wr32(priv, 0x400108, 0x02000000); + trap &= ~0x02000000; + } + + if (trap) { + nv_error(priv, "TRAP UNHANDLED 0x%08x\n", trap); + nv_wr32(priv, 0x400108, trap); + } +} + +static void +gf100_gr_ctxctl_debug_unit(struct gf100_gr_priv *priv, u32 base) +{ + nv_error(priv, "%06x - done 0x%08x\n", base, + nv_rd32(priv, base + 0x400)); + nv_error(priv, "%06x - stat 0x%08x 0x%08x 0x%08x 0x%08x\n", base, + nv_rd32(priv, base + 0x800), nv_rd32(priv, base + 0x804), + nv_rd32(priv, base + 0x808), nv_rd32(priv, base + 0x80c)); + nv_error(priv, "%06x - stat 0x%08x 0x%08x 0x%08x 0x%08x\n", base, + nv_rd32(priv, base + 0x810), nv_rd32(priv, base + 0x814), + nv_rd32(priv, base + 0x818), nv_rd32(priv, base + 0x81c)); +} + +void +gf100_gr_ctxctl_debug(struct gf100_gr_priv *priv) +{ + u32 gpcnr = nv_rd32(priv, 0x409604) & 0xffff; + u32 gpc; + + gf100_gr_ctxctl_debug_unit(priv, 0x409000); + for (gpc = 0; gpc < gpcnr; gpc++) + gf100_gr_ctxctl_debug_unit(priv, 0x502000 + (gpc * 0x8000)); +} + +static void +gf100_gr_ctxctl_isr(struct gf100_gr_priv *priv) +{ + u32 stat = nv_rd32(priv, 0x409c18); + + if (stat & 0x00000001) { + u32 code = nv_rd32(priv, 0x409814); + if (code == E_BAD_FWMTHD) { + u32 class = nv_rd32(priv, 0x409808); + u32 addr = nv_rd32(priv, 0x40980c); + u32 subc = (addr & 0x00070000) >> 16; + u32 mthd = (addr & 0x00003ffc); + u32 data = nv_rd32(priv, 0x409810); + + nv_error(priv, "FECS MTHD subc %d class 0x%04x " + "mthd 0x%04x data 0x%08x\n", + subc, class, mthd, data); + + nv_wr32(priv, 0x409c20, 0x00000001); + stat &= ~0x00000001; + } else { + nv_error(priv, "FECS ucode error %d\n", code); + } + } + + if (stat & 0x00080000) { + nv_error(priv, "FECS watchdog timeout\n"); + gf100_gr_ctxctl_debug(priv); + nv_wr32(priv, 0x409c20, 0x00080000); + stat &= ~0x00080000; + } + + if (stat) { + nv_error(priv, "FECS 0x%08x\n", stat); + gf100_gr_ctxctl_debug(priv); + nv_wr32(priv, 0x409c20, stat); + } +} + +static void +gf100_gr_intr(struct nvkm_subdev *subdev) +{ + struct nvkm_fifo *pfifo = nvkm_fifo(subdev); + struct nvkm_engine *engine = nv_engine(subdev); + struct nvkm_object *engctx; + struct nvkm_handle *handle; + struct gf100_gr_priv *priv = (void *)subdev; + u64 inst = nv_rd32(priv, 0x409b00) & 0x0fffffff; + u32 stat = nv_rd32(priv, 0x400100); + u32 addr = nv_rd32(priv, 0x400704); + u32 mthd = (addr & 0x00003ffc); + u32 subc = (addr & 0x00070000) >> 16; + u32 data = nv_rd32(priv, 0x400708); + u32 code = nv_rd32(priv, 0x400110); + u32 class; + int chid; + + if (nv_device(priv)->card_type < NV_E0 || subc < 4) + class = nv_rd32(priv, 0x404200 + (subc * 4)); + else + class = 0x0000; + + engctx = nvkm_engctx_get(engine, inst); + chid = pfifo->chid(pfifo, engctx); + + if (stat & 0x00000001) { + /* + * notifier interrupt, only needed for cyclestats + * can be safely ignored + */ + nv_wr32(priv, 0x400100, 0x00000001); + stat &= ~0x00000001; + } + + if (stat & 0x00000010) { + handle = nvkm_handle_get_class(engctx, class); + if (!handle || nv_call(handle->object, mthd, data)) { + nv_error(priv, + "ILLEGAL_MTHD ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n", + chid, inst << 12, nvkm_client_name(engctx), + subc, class, mthd, data); + } + nvkm_handle_put(handle); + nv_wr32(priv, 0x400100, 0x00000010); + stat &= ~0x00000010; + } + + if (stat & 0x00000020) { + nv_error(priv, + "ILLEGAL_CLASS ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n", + chid, inst << 12, nvkm_client_name(engctx), subc, + class, mthd, data); + nv_wr32(priv, 0x400100, 0x00000020); + stat &= ~0x00000020; + } + + if (stat & 0x00100000) { + nv_error(priv, "DATA_ERROR ["); + nvkm_enum_print(nv50_data_error_names, code); + pr_cont("] ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n", + chid, inst << 12, nvkm_client_name(engctx), subc, + class, mthd, data); + nv_wr32(priv, 0x400100, 0x00100000); + stat &= ~0x00100000; + } + + if (stat & 0x00200000) { + nv_error(priv, "TRAP ch %d [0x%010llx %s]\n", chid, inst << 12, + nvkm_client_name(engctx)); + gf100_gr_trap_intr(priv); + nv_wr32(priv, 0x400100, 0x00200000); + stat &= ~0x00200000; + } + + if (stat & 0x00080000) { + gf100_gr_ctxctl_isr(priv); + nv_wr32(priv, 0x400100, 0x00080000); + stat &= ~0x00080000; + } + + if (stat) { + nv_error(priv, "unknown stat 0x%08x\n", stat); + nv_wr32(priv, 0x400100, stat); + } + + nv_wr32(priv, 0x400500, 0x00010001); + nvkm_engctx_put(engctx); +} + +void +gf100_gr_init_fw(struct gf100_gr_priv *priv, u32 fuc_base, + struct gf100_gr_fuc *code, struct gf100_gr_fuc *data) +{ + int i; + + nv_wr32(priv, fuc_base + 0x01c0, 0x01000000); + for (i = 0; i < data->size / 4; i++) + nv_wr32(priv, fuc_base + 0x01c4, data->data[i]); + + nv_wr32(priv, fuc_base + 0x0180, 0x01000000); + for (i = 0; i < code->size / 4; i++) { + if ((i & 0x3f) == 0) + nv_wr32(priv, fuc_base + 0x0188, i >> 6); + nv_wr32(priv, fuc_base + 0x0184, code->data[i]); + } + + /* code must be padded to 0x40 words */ + for (; i & 0x3f; i++) + nv_wr32(priv, fuc_base + 0x0184, 0); +} + +static void +gf100_gr_init_csdata(struct gf100_gr_priv *priv, + const struct gf100_gr_pack *pack, + u32 falcon, u32 starstar, u32 base) +{ + const struct gf100_gr_pack *iter; + const struct gf100_gr_init *init; + u32 addr = ~0, prev = ~0, xfer = 0; + u32 star, temp; + + nv_wr32(priv, falcon + 0x01c0, 0x02000000 + starstar); + star = nv_rd32(priv, falcon + 0x01c4); + temp = nv_rd32(priv, falcon + 0x01c4); + if (temp > star) + star = temp; + nv_wr32(priv, 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); + nv_wr32(priv, falcon + 0x01c4, data); + star += 4; + } + addr = head; + xfer = 0; + } + prev = head; + xfer = xfer + 1; + head = head + init->pitch; + } + } + + nv_wr32(priv, falcon + 0x01c4, (--xfer << 26) | addr); + nv_wr32(priv, falcon + 0x01c0, 0x01000004 + starstar); + nv_wr32(priv, falcon + 0x01c4, star + 4); +} + +int +gf100_gr_init_ctxctl(struct gf100_gr_priv *priv) +{ + struct gf100_gr_oclass *oclass = (void *)nv_object(priv)->oclass; + struct gf100_grctx_oclass *cclass = (void *)nv_engine(priv)->cclass; + int i; + + if (priv->firmware) { + /* load fuc microcode */ + nvkm_mc(priv)->unk260(nvkm_mc(priv), 0); + gf100_gr_init_fw(priv, 0x409000, &priv->fuc409c, + &priv->fuc409d); + gf100_gr_init_fw(priv, 0x41a000, &priv->fuc41ac, + &priv->fuc41ad); + nvkm_mc(priv)->unk260(nvkm_mc(priv), 1); + + /* start both of them running */ + nv_wr32(priv, 0x409840, 0xffffffff); + nv_wr32(priv, 0x41a10c, 0x00000000); + nv_wr32(priv, 0x40910c, 0x00000000); + nv_wr32(priv, 0x41a100, 0x00000002); + nv_wr32(priv, 0x409100, 0x00000002); + if (!nv_wait(priv, 0x409800, 0x00000001, 0x00000001)) + nv_warn(priv, "0x409800 wait failed\n"); + + nv_wr32(priv, 0x409840, 0xffffffff); + nv_wr32(priv, 0x409500, 0x7fffffff); + nv_wr32(priv, 0x409504, 0x00000021); + + nv_wr32(priv, 0x409840, 0xffffffff); + nv_wr32(priv, 0x409500, 0x00000000); + nv_wr32(priv, 0x409504, 0x00000010); + if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) { + nv_error(priv, "fuc09 req 0x10 timeout\n"); + return -EBUSY; + } + priv->size = nv_rd32(priv, 0x409800); + + nv_wr32(priv, 0x409840, 0xffffffff); + nv_wr32(priv, 0x409500, 0x00000000); + nv_wr32(priv, 0x409504, 0x00000016); + if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) { + nv_error(priv, "fuc09 req 0x16 timeout\n"); + return -EBUSY; + } + + nv_wr32(priv, 0x409840, 0xffffffff); + nv_wr32(priv, 0x409500, 0x00000000); + nv_wr32(priv, 0x409504, 0x00000025); + if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) { + nv_error(priv, "fuc09 req 0x25 timeout\n"); + return -EBUSY; + } + + if (nv_device(priv)->chipset >= 0xe0) { + nv_wr32(priv, 0x409800, 0x00000000); + nv_wr32(priv, 0x409500, 0x00000001); + nv_wr32(priv, 0x409504, 0x00000030); + if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) { + nv_error(priv, "fuc09 req 0x30 timeout\n"); + return -EBUSY; + } + + nv_wr32(priv, 0x409810, 0xb00095c8); + nv_wr32(priv, 0x409800, 0x00000000); + nv_wr32(priv, 0x409500, 0x00000001); + nv_wr32(priv, 0x409504, 0x00000031); + if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) { + nv_error(priv, "fuc09 req 0x31 timeout\n"); + return -EBUSY; + } + + nv_wr32(priv, 0x409810, 0x00080420); + nv_wr32(priv, 0x409800, 0x00000000); + nv_wr32(priv, 0x409500, 0x00000001); + nv_wr32(priv, 0x409504, 0x00000032); + if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) { + nv_error(priv, "fuc09 req 0x32 timeout\n"); + return -EBUSY; + } + + nv_wr32(priv, 0x409614, 0x00000070); + nv_wr32(priv, 0x409614, 0x00000770); + nv_wr32(priv, 0x40802c, 0x00000001); + } + + if (priv->data == NULL) { + int ret = gf100_grctx_generate(priv); + if (ret) { + nv_error(priv, "failed to construct context\n"); + return ret; + } + } + + return 0; + } else + if (!oclass->fecs.ucode) { + return -ENOSYS; + } + + /* load HUB microcode */ + nvkm_mc(priv)->unk260(nvkm_mc(priv), 0); + nv_wr32(priv, 0x4091c0, 0x01000000); + for (i = 0; i < oclass->fecs.ucode->data.size / 4; i++) + nv_wr32(priv, 0x4091c4, oclass->fecs.ucode->data.data[i]); + + nv_wr32(priv, 0x409180, 0x01000000); + for (i = 0; i < oclass->fecs.ucode->code.size / 4; i++) { + if ((i & 0x3f) == 0) + nv_wr32(priv, 0x409188, i >> 6); + nv_wr32(priv, 0x409184, oclass->fecs.ucode->code.data[i]); + } + + /* load GPC microcode */ + nv_wr32(priv, 0x41a1c0, 0x01000000); + for (i = 0; i < oclass->gpccs.ucode->data.size / 4; i++) + nv_wr32(priv, 0x41a1c4, oclass->gpccs.ucode->data.data[i]); + + nv_wr32(priv, 0x41a180, 0x01000000); + for (i = 0; i < oclass->gpccs.ucode->code.size / 4; i++) { + if ((i & 0x3f) == 0) + nv_wr32(priv, 0x41a188, i >> 6); + nv_wr32(priv, 0x41a184, oclass->gpccs.ucode->code.data[i]); + } + nvkm_mc(priv)->unk260(nvkm_mc(priv), 1); + + /* load register lists */ + gf100_gr_init_csdata(priv, cclass->hub, 0x409000, 0x000, 0x000000); + gf100_gr_init_csdata(priv, cclass->gpc, 0x41a000, 0x000, 0x418000); + gf100_gr_init_csdata(priv, cclass->tpc, 0x41a000, 0x004, 0x419800); + gf100_gr_init_csdata(priv, cclass->ppc, 0x41a000, 0x008, 0x41be00); + + /* start HUB ucode running, it'll init the GPCs */ + nv_wr32(priv, 0x40910c, 0x00000000); + nv_wr32(priv, 0x409100, 0x00000002); + if (!nv_wait(priv, 0x409800, 0x80000000, 0x80000000)) { + nv_error(priv, "HUB_INIT timed out\n"); + gf100_gr_ctxctl_debug(priv); + return -EBUSY; + } + + priv->size = nv_rd32(priv, 0x409804); + if (priv->data == NULL) { + int ret = gf100_grctx_generate(priv); + if (ret) { + nv_error(priv, "failed to construct context\n"); + return ret; + } + } + + return 0; +} + +int +gf100_gr_init(struct nvkm_object *object) +{ + struct gf100_gr_oclass *oclass = (void *)object->oclass; + struct gf100_gr_priv *priv = (void *)object; + const u32 magicgpc918 = DIV_ROUND_UP(0x00800000, priv->tpc_total); + u32 data[TPC_MAX / 8] = {}; + u8 tpcnr[GPC_MAX]; + int gpc, tpc, rop; + int ret, i; + + ret = nvkm_gr_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, GPC_BCAST(0x0880), 0x00000000); + nv_wr32(priv, GPC_BCAST(0x08a4), 0x00000000); + nv_wr32(priv, GPC_BCAST(0x0888), 0x00000000); + nv_wr32(priv, GPC_BCAST(0x088c), 0x00000000); + nv_wr32(priv, GPC_BCAST(0x0890), 0x00000000); + nv_wr32(priv, GPC_BCAST(0x0894), 0x00000000); + nv_wr32(priv, GPC_BCAST(0x08b4), priv->unk4188b4->addr >> 8); + nv_wr32(priv, GPC_BCAST(0x08b8), priv->unk4188b8->addr >> 8); + + gf100_gr_mmio(priv, oclass->mmio); + + memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr)); + for (i = 0, gpc = -1; i < priv->tpc_total; i++) { + do { + gpc = (gpc + 1) % priv->gpc_nr; + } while (!tpcnr[gpc]); + tpc = priv->tpc_nr[gpc] - tpcnr[gpc]--; + + data[i / 8] |= tpc << ((i % 8) * 4); + } + + nv_wr32(priv, GPC_BCAST(0x0980), data[0]); + nv_wr32(priv, GPC_BCAST(0x0984), data[1]); + nv_wr32(priv, GPC_BCAST(0x0988), data[2]); + nv_wr32(priv, GPC_BCAST(0x098c), data[3]); + + for (gpc = 0; gpc < priv->gpc_nr; gpc++) { + nv_wr32(priv, GPC_UNIT(gpc, 0x0914), + priv->magic_not_rop_nr << 8 | priv->tpc_nr[gpc]); + nv_wr32(priv, GPC_UNIT(gpc, 0x0910), 0x00040000 | + priv->tpc_total); + nv_wr32(priv, GPC_UNIT(gpc, 0x0918), magicgpc918); + } + + if (nv_device(priv)->chipset != 0xd7) + nv_wr32(priv, GPC_BCAST(0x1bd4), magicgpc918); + else + nv_wr32(priv, GPC_BCAST(0x3fd4), magicgpc918); + + nv_wr32(priv, GPC_BCAST(0x08ac), nv_rd32(priv, 0x100800)); + + nv_wr32(priv, 0x400500, 0x00010001); + + nv_wr32(priv, 0x400100, 0xffffffff); + nv_wr32(priv, 0x40013c, 0xffffffff); + + nv_wr32(priv, 0x409c24, 0x000f0000); + nv_wr32(priv, 0x404000, 0xc0000000); + nv_wr32(priv, 0x404600, 0xc0000000); + nv_wr32(priv, 0x408030, 0xc0000000); + nv_wr32(priv, 0x40601c, 0xc0000000); + nv_wr32(priv, 0x404490, 0xc0000000); + nv_wr32(priv, 0x406018, 0xc0000000); + nv_wr32(priv, 0x405840, 0xc0000000); + nv_wr32(priv, 0x405844, 0x00ffffff); + nv_mask(priv, 0x419cc0, 0x00000008, 0x00000008); + nv_mask(priv, 0x419eb4, 0x00001000, 0x00001000); + + for (gpc = 0; gpc < priv->gpc_nr; gpc++) { + nv_wr32(priv, GPC_UNIT(gpc, 0x0420), 0xc0000000); + nv_wr32(priv, GPC_UNIT(gpc, 0x0900), 0xc0000000); + nv_wr32(priv, GPC_UNIT(gpc, 0x1028), 0xc0000000); + nv_wr32(priv, GPC_UNIT(gpc, 0x0824), 0xc0000000); + for (tpc = 0; tpc < priv->tpc_nr[gpc]; tpc++) { + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x508), 0xffffffff); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x50c), 0xffffffff); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x224), 0xc0000000); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x48c), 0xc0000000); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x084), 0xc0000000); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x644), 0x001ffffe); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x64c), 0x0000000f); + } + nv_wr32(priv, GPC_UNIT(gpc, 0x2c90), 0xffffffff); + nv_wr32(priv, GPC_UNIT(gpc, 0x2c94), 0xffffffff); + } + + for (rop = 0; rop < priv->rop_nr; rop++) { + nv_wr32(priv, ROP_UNIT(rop, 0x144), 0xc0000000); + nv_wr32(priv, ROP_UNIT(rop, 0x070), 0xc0000000); + nv_wr32(priv, ROP_UNIT(rop, 0x204), 0xffffffff); + nv_wr32(priv, ROP_UNIT(rop, 0x208), 0xffffffff); + } + + nv_wr32(priv, 0x400108, 0xffffffff); + nv_wr32(priv, 0x400138, 0xffffffff); + nv_wr32(priv, 0x400118, 0xffffffff); + nv_wr32(priv, 0x400130, 0xffffffff); + nv_wr32(priv, 0x40011c, 0xffffffff); + nv_wr32(priv, 0x400134, 0xffffffff); + + nv_wr32(priv, 0x400054, 0x34ce3464); + + gf100_gr_zbc_init(priv); + + return gf100_gr_init_ctxctl(priv); +} + +static void +gf100_gr_dtor_fw(struct gf100_gr_fuc *fuc) +{ + kfree(fuc->data); + fuc->data = NULL; +} + +int +gf100_gr_ctor_fw(struct gf100_gr_priv *priv, const char *fwname, + struct gf100_gr_fuc *fuc) +{ + struct nvkm_device *device = nv_device(priv); + const struct firmware *fw; + char f[32]; + int ret; + + snprintf(f, sizeof(f), "nouveau/nv%02x_%s", device->chipset, fwname); + ret = request_firmware(&fw, f, nv_device_base(device)); + if (ret) { + snprintf(f, sizeof(f), "nouveau/%s", fwname); + ret = request_firmware(&fw, f, nv_device_base(device)); + if (ret) { + nv_error(priv, "failed to load %s\n", fwname); + return ret; + } + } + + fuc->size = fw->size; + fuc->data = kmemdup(fw->data, fuc->size, GFP_KERNEL); + release_firmware(fw); + return (fuc->data != NULL) ? 0 : -ENOMEM; +} + +void +gf100_gr_dtor(struct nvkm_object *object) +{ + struct gf100_gr_priv *priv = (void *)object; + + kfree(priv->data); + + gf100_gr_dtor_fw(&priv->fuc409c); + gf100_gr_dtor_fw(&priv->fuc409d); + gf100_gr_dtor_fw(&priv->fuc41ac); + gf100_gr_dtor_fw(&priv->fuc41ad); + + nvkm_gpuobj_ref(NULL, &priv->unk4188b8); + nvkm_gpuobj_ref(NULL, &priv->unk4188b4); + + nvkm_gr_destroy(&priv->base); +} + +int +gf100_gr_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *bclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gf100_gr_oclass *oclass = (void *)bclass; + struct nvkm_device *device = nv_device(parent); + struct gf100_gr_priv *priv; + bool use_ext_fw, enable; + int ret, i, j; + + use_ext_fw = nvkm_boolopt(device->cfgopt, "NvGrUseFW", + oclass->fecs.ucode == NULL); + enable = use_ext_fw || oclass->fecs.ucode != NULL; + + ret = nvkm_gr_create(parent, engine, bclass, enable, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x08001000; + nv_subdev(priv)->intr = gf100_gr_intr; + + priv->base.units = gf100_gr_units; + + if (use_ext_fw) { + nv_info(priv, "using external firmware\n"); + if (gf100_gr_ctor_fw(priv, "fuc409c", &priv->fuc409c) || + gf100_gr_ctor_fw(priv, "fuc409d", &priv->fuc409d) || + gf100_gr_ctor_fw(priv, "fuc41ac", &priv->fuc41ac) || + gf100_gr_ctor_fw(priv, "fuc41ad", &priv->fuc41ad)) + return -ENODEV; + priv->firmware = true; + } + + ret = nvkm_gpuobj_new(nv_object(priv), NULL, 0x1000, 256, 0, + &priv->unk4188b4); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(nv_object(priv), NULL, 0x1000, 256, 0, + &priv->unk4188b8); + if (ret) + return ret; + + for (i = 0; i < 0x1000; i += 4) { + nv_wo32(priv->unk4188b4, i, 0x00000010); + nv_wo32(priv->unk4188b8, i, 0x00000010); + } + + priv->rop_nr = (nv_rd32(priv, 0x409604) & 0x001f0000) >> 16; + priv->gpc_nr = nv_rd32(priv, 0x409604) & 0x0000001f; + for (i = 0; i < priv->gpc_nr; i++) { + priv->tpc_nr[i] = nv_rd32(priv, GPC_UNIT(i, 0x2608)); + priv->tpc_total += priv->tpc_nr[i]; + priv->ppc_nr[i] = oclass->ppc_nr; + for (j = 0; j < priv->ppc_nr[i]; j++) { + u8 mask = nv_rd32(priv, GPC_UNIT(i, 0x0c30 + (j * 4))); + priv->ppc_tpc_nr[i][j] = hweight8(mask); + } + } + + /*XXX: these need figuring out... though it might not even matter */ + switch (nv_device(priv)->chipset) { + case 0xc0: + if (priv->tpc_total == 11) { /* 465, 3/4/4/0, 4 */ + priv->magic_not_rop_nr = 0x07; + } else + if (priv->tpc_total == 14) { /* 470, 3/3/4/4, 5 */ + priv->magic_not_rop_nr = 0x05; + } else + if (priv->tpc_total == 15) { /* 480, 3/4/4/4, 6 */ + priv->magic_not_rop_nr = 0x06; + } + break; + case 0xc3: /* 450, 4/0/0/0, 2 */ + priv->magic_not_rop_nr = 0x03; + break; + case 0xc4: /* 460, 3/4/0/0, 4 */ + priv->magic_not_rop_nr = 0x01; + break; + case 0xc1: /* 2/0/0/0, 1 */ + priv->magic_not_rop_nr = 0x01; + break; + case 0xc8: /* 4/4/3/4, 5 */ + priv->magic_not_rop_nr = 0x06; + break; + case 0xce: /* 4/4/0/0, 4 */ + priv->magic_not_rop_nr = 0x03; + break; + case 0xcf: /* 4/0/0/0, 3 */ + priv->magic_not_rop_nr = 0x03; + break; + case 0xd7: + case 0xd9: /* 1/0/0/0, 1 */ + priv->magic_not_rop_nr = 0x01; + break; + } + + nv_engine(priv)->cclass = *oclass->cclass; + nv_engine(priv)->sclass = oclass->sclass; + return 0; +} + +#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), +}; + +struct nvkm_oclass * +gf100_gr_oclass = &(struct gf100_gr_oclass) { + .base.handle = NV_ENGINE(GR, 0xc0), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_gr_ctor, + .dtor = gf100_gr_dtor, + .init = gf100_gr_init, + .fini = _nvkm_gr_fini, + }, + .cclass = &gf100_grctx_oclass, + .sclass = gf100_gr_sclass, + .mmio = gf100_gr_pack_mmio, + .fecs.ucode = &gf100_gr_fecs_ucode, + .gpccs.ucode = &gf100_gr_gpccs_ucode, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.h new file mode 100644 index 000000000..8af1a89ed --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.h @@ -0,0 +1,266 @@ +/* + * 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 __NVC0_GR_H__ +#define __NVC0_GR_H__ +#include + +#include + +#define GPC_MAX 32 +#define TPC_MAX (GPC_MAX * 8) + +#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; + u32 access; +}; + +struct gf100_gr_mmio { + u32 addr; + u32 data; + u32 shift; + int buffer; +}; + +struct gf100_gr_fuc { + u32 *data; + u32 size; +}; + +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_priv { + struct nvkm_gr base; + + struct gf100_gr_fuc fuc409c; + struct gf100_gr_fuc fuc409d; + struct gf100_gr_fuc fuc41ac; + struct gf100_gr_fuc fuc41ad; + bool firmware; + + struct gf100_gr_zbc_color zbc_color[NVKM_LTC_MAX_ZBC_CNT]; + struct gf100_gr_zbc_depth zbc_depth[NVKM_LTC_MAX_ZBC_CNT]; + + u8 rop_nr; + u8 gpc_nr; + u8 tpc_nr[GPC_MAX]; + u8 tpc_total; + u8 ppc_nr[GPC_MAX]; + u8 ppc_tpc_nr[GPC_MAX][4]; + + struct nvkm_gpuobj *unk4188b4; + struct nvkm_gpuobj *unk4188b8; + + struct gf100_gr_data mmio_data[4]; + struct gf100_gr_mmio mmio_list[4096/8]; + u32 size; + u32 *data; + + u8 magic_not_rop_nr; +}; + +struct gf100_gr_chan { + struct nvkm_gr_chan base; + + struct nvkm_gpuobj *mmio; + struct nvkm_vma mmio_vma; + int mmio_nr; + struct { + struct nvkm_gpuobj *mem; + struct nvkm_vma vma; + } data[4]; +}; + +int gf100_gr_context_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); +void gf100_gr_context_dtor(struct nvkm_object *); + +void gf100_gr_ctxctl_debug(struct gf100_gr_priv *); + +u64 gf100_gr_units(struct nvkm_gr *); +int gf100_gr_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *data, u32 size, + struct nvkm_object **); +void gf100_gr_dtor(struct nvkm_object *); +int gf100_gr_init(struct nvkm_object *); +void gf100_gr_zbc_init(struct gf100_gr_priv *); + +int gk104_gr_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *data, u32 size, + struct nvkm_object **); +int gk104_gr_init(struct nvkm_object *); + +int gm204_gr_init(struct nvkm_object *); + +extern struct nvkm_ofuncs gf100_fermi_ofuncs; + +extern struct nvkm_oclass gf100_gr_sclass[]; +extern struct nvkm_omthds gf100_gr_9097_omthds[]; +extern struct nvkm_omthds gf100_gr_90c0_omthds[]; +extern struct nvkm_oclass gf110_gr_sclass[]; +extern struct nvkm_oclass gk110_gr_sclass[]; +extern struct nvkm_oclass gm204_gr_sclass[]; + +struct gf100_gr_init { + u32 addr; + u8 count; + u8 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 gf100_gr_fuc code; + struct gf100_gr_fuc 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; + +struct gf100_gr_oclass { + struct nvkm_oclass base; + struct nvkm_oclass **cclass; + struct nvkm_oclass *sclass; + const struct gf100_gr_pack *mmio; + struct { + struct gf100_gr_ucode *ucode; + } fecs; + struct { + struct gf100_gr_ucode *ucode; + } gpccs; + int ppc_nr; +}; + +void gf100_gr_mmio(struct gf100_gr_priv *, const struct gf100_gr_pack *); +void gf100_gr_icmd(struct gf100_gr_priv *, const struct gf100_gr_pack *); +void gf100_gr_mthd(struct gf100_gr_priv *, const struct gf100_gr_pack *); +int gf100_gr_init_ctxctl(struct gf100_gr_priv *); + +/* 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[]; + +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_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_priv *); + +extern const struct gf100_gr_pack gm204_gr_pack_mmio[]; +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf104.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf104.c new file mode 100644 index 000000000..20d3b85db --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf104.c @@ -0,0 +1,127 @@ +/* + * 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" + +/******************************************************************************* + * 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 + ******************************************************************************/ + +struct nvkm_oclass * +gf104_gr_oclass = &(struct gf100_gr_oclass) { + .base.handle = NV_ENGINE(GR, 0xc3), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_gr_ctor, + .dtor = gf100_gr_dtor, + .init = gf100_gr_init, + .fini = _nvkm_gr_fini, + }, + .cclass = &gf104_grctx_oclass, + .sclass = gf100_gr_sclass, + .mmio = gf104_gr_pack_mmio, + .fecs.ucode = &gf100_gr_fecs_ucode, + .gpccs.ucode = &gf100_gr_gpccs_ucode, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf108.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf108.c new file mode 100644 index 000000000..8df73421c --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf108.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 + */ +#include "gf100.h" +#include "ctxgf100.h" + +#include + +/******************************************************************************* + * Graphics object classes + ******************************************************************************/ + +static struct nvkm_oclass +gf108_gr_sclass[] = { + { FERMI_TWOD_A, &nvkm_object_ofuncs }, + { FERMI_MEMORY_TO_MEMORY_FORMAT_A, &nvkm_object_ofuncs }, + { FERMI_A, &gf100_fermi_ofuncs, gf100_gr_9097_omthds }, + { FERMI_B, &gf100_fermi_ofuncs, gf100_gr_9097_omthds }, + { FERMI_COMPUTE_A, &nvkm_object_ofuncs, gf100_gr_90c0_omthds }, + {} +}; + +/******************************************************************************* + * 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 + ******************************************************************************/ + +struct nvkm_oclass * +gf108_gr_oclass = &(struct gf100_gr_oclass) { + .base.handle = NV_ENGINE(GR, 0xc1), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_gr_ctor, + .dtor = gf100_gr_dtor, + .init = gf100_gr_init, + .fini = _nvkm_gr_fini, + }, + .cclass = &gf108_grctx_oclass, + .sclass = gf108_gr_sclass, + .mmio = gf108_gr_pack_mmio, + .fecs.ucode = &gf100_gr_fecs_ucode, + .gpccs.ucode = &gf100_gr_gpccs_ucode, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf110.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf110.c new file mode 100644 index 000000000..ef76e2dd1 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf110.c @@ -0,0 +1,116 @@ +/* + * 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 + +/******************************************************************************* + * Graphics object classes + ******************************************************************************/ + +struct nvkm_oclass +gf110_gr_sclass[] = { + { FERMI_TWOD_A, &nvkm_object_ofuncs }, + { FERMI_MEMORY_TO_MEMORY_FORMAT_A, &nvkm_object_ofuncs }, + { FERMI_A, &gf100_fermi_ofuncs, gf100_gr_9097_omthds }, + { FERMI_B, &gf100_fermi_ofuncs, gf100_gr_9097_omthds }, + { FERMI_C, &gf100_fermi_ofuncs, gf100_gr_9097_omthds }, + { FERMI_COMPUTE_A, &nvkm_object_ofuncs, gf100_gr_90c0_omthds }, + {} +}; + +/******************************************************************************* + * 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 + ******************************************************************************/ + +struct nvkm_oclass * +gf110_gr_oclass = &(struct gf100_gr_oclass) { + .base.handle = NV_ENGINE(GR, 0xc8), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_gr_ctor, + .dtor = gf100_gr_dtor, + .init = gf100_gr_init, + .fini = _nvkm_gr_fini, + }, + .cclass = &gf110_grctx_oclass, + .sclass = gf110_gr_sclass, + .mmio = gf110_gr_pack_mmio, + .fecs.ucode = &gf100_gr_fecs_ucode, + .gpccs.ucode = &gf100_gr_gpccs_ucode, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf117.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf117.c new file mode 100644 index 000000000..871ac5f80 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf117.c @@ -0,0 +1,136 @@ +/* + * 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" + +/******************************************************************************* + * 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" + +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" + +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), +}; + +struct nvkm_oclass * +gf117_gr_oclass = &(struct gf100_gr_oclass) { + .base.handle = NV_ENGINE(GR, 0xd7), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_gr_ctor, + .dtor = gf100_gr_dtor, + .init = gf100_gr_init, + .fini = _nvkm_gr_fini, + }, + .cclass = &gf117_grctx_oclass, + .sclass = gf110_gr_sclass, + .mmio = gf117_gr_pack_mmio, + .fecs.ucode = &gf117_gr_fecs_ucode, + .gpccs.ucode = &gf117_gr_gpccs_ucode, + .ppc_nr = 1, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf119.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf119.c new file mode 100644 index 000000000..e6dd651e2 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf119.c @@ -0,0 +1,190 @@ +/* + * 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" + +/******************************************************************************* + * 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 + ******************************************************************************/ + +struct nvkm_oclass * +gf119_gr_oclass = &(struct gf100_gr_oclass) { + .base.handle = NV_ENGINE(GR, 0xd9), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_gr_ctor, + .dtor = gf100_gr_dtor, + .init = gf100_gr_init, + .fini = _nvkm_gr_fini, + }, + .cclass = &gf119_grctx_oclass, + .sclass = gf110_gr_sclass, + .mmio = gf119_gr_pack_mmio, + .fecs.ucode = &gf100_gr_fecs_ucode, + .gpccs.ucode = &gf100_gr_gpccs_ucode, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk104.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk104.c new file mode 100644 index 000000000..46f7844ec --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk104.c @@ -0,0 +1,359 @@ +/* + * 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 + +/******************************************************************************* + * Graphics object classes + ******************************************************************************/ + +static struct nvkm_oclass +gk104_gr_sclass[] = { + { FERMI_TWOD_A, &nvkm_object_ofuncs }, + { KEPLER_INLINE_TO_MEMORY_A, &nvkm_object_ofuncs }, + { KEPLER_A, &gf100_fermi_ofuncs, gf100_gr_9097_omthds }, + { KEPLER_COMPUTE_A, &nvkm_object_ofuncs, gf100_gr_90c0_omthds }, + {} +}; + +/******************************************************************************* + * 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_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_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 }, + {} +}; + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +int +gk104_gr_init(struct nvkm_object *object) +{ + struct gf100_gr_oclass *oclass = (void *)object->oclass; + struct gf100_gr_priv *priv = (void *)object; + struct nvkm_pmu *pmu = nvkm_pmu(priv); + const u32 magicgpc918 = DIV_ROUND_UP(0x00800000, priv->tpc_total); + u32 data[TPC_MAX / 8] = {}; + u8 tpcnr[GPC_MAX]; + int gpc, tpc, rop; + int ret, i; + + if (pmu) + pmu->pgob(pmu, false); + + ret = nvkm_gr_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, GPC_BCAST(0x0880), 0x00000000); + nv_wr32(priv, GPC_BCAST(0x08a4), 0x00000000); + nv_wr32(priv, GPC_BCAST(0x0888), 0x00000000); + nv_wr32(priv, GPC_BCAST(0x088c), 0x00000000); + nv_wr32(priv, GPC_BCAST(0x0890), 0x00000000); + nv_wr32(priv, GPC_BCAST(0x0894), 0x00000000); + nv_wr32(priv, GPC_BCAST(0x08b4), priv->unk4188b4->addr >> 8); + nv_wr32(priv, GPC_BCAST(0x08b8), priv->unk4188b8->addr >> 8); + + gf100_gr_mmio(priv, oclass->mmio); + + nv_wr32(priv, GPC_UNIT(0, 0x3018), 0x00000001); + + memset(data, 0x00, sizeof(data)); + memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr)); + for (i = 0, gpc = -1; i < priv->tpc_total; i++) { + do { + gpc = (gpc + 1) % priv->gpc_nr; + } while (!tpcnr[gpc]); + tpc = priv->tpc_nr[gpc] - tpcnr[gpc]--; + + data[i / 8] |= tpc << ((i % 8) * 4); + } + + nv_wr32(priv, GPC_BCAST(0x0980), data[0]); + nv_wr32(priv, GPC_BCAST(0x0984), data[1]); + nv_wr32(priv, GPC_BCAST(0x0988), data[2]); + nv_wr32(priv, GPC_BCAST(0x098c), data[3]); + + for (gpc = 0; gpc < priv->gpc_nr; gpc++) { + nv_wr32(priv, GPC_UNIT(gpc, 0x0914), + priv->magic_not_rop_nr << 8 | priv->tpc_nr[gpc]); + nv_wr32(priv, GPC_UNIT(gpc, 0x0910), 0x00040000 | + priv->tpc_total); + nv_wr32(priv, GPC_UNIT(gpc, 0x0918), magicgpc918); + } + + nv_wr32(priv, GPC_BCAST(0x3fd4), magicgpc918); + nv_wr32(priv, GPC_BCAST(0x08ac), nv_rd32(priv, 0x100800)); + + nv_wr32(priv, 0x400500, 0x00010001); + + nv_wr32(priv, 0x400100, 0xffffffff); + nv_wr32(priv, 0x40013c, 0xffffffff); + + nv_wr32(priv, 0x409ffc, 0x00000000); + nv_wr32(priv, 0x409c14, 0x00003e3e); + nv_wr32(priv, 0x409c24, 0x000f0001); + nv_wr32(priv, 0x404000, 0xc0000000); + nv_wr32(priv, 0x404600, 0xc0000000); + nv_wr32(priv, 0x408030, 0xc0000000); + nv_wr32(priv, 0x404490, 0xc0000000); + nv_wr32(priv, 0x406018, 0xc0000000); + nv_wr32(priv, 0x407020, 0x40000000); + nv_wr32(priv, 0x405840, 0xc0000000); + nv_wr32(priv, 0x405844, 0x00ffffff); + nv_mask(priv, 0x419cc0, 0x00000008, 0x00000008); + nv_mask(priv, 0x419eb4, 0x00001000, 0x00001000); + + for (gpc = 0; gpc < priv->gpc_nr; gpc++) { + nv_wr32(priv, GPC_UNIT(gpc, 0x3038), 0xc0000000); + nv_wr32(priv, GPC_UNIT(gpc, 0x0420), 0xc0000000); + nv_wr32(priv, GPC_UNIT(gpc, 0x0900), 0xc0000000); + nv_wr32(priv, GPC_UNIT(gpc, 0x1028), 0xc0000000); + nv_wr32(priv, GPC_UNIT(gpc, 0x0824), 0xc0000000); + for (tpc = 0; tpc < priv->tpc_nr[gpc]; tpc++) { + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x508), 0xffffffff); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x50c), 0xffffffff); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x224), 0xc0000000); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x48c), 0xc0000000); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x084), 0xc0000000); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x644), 0x001ffffe); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x64c), 0x0000000f); + } + nv_wr32(priv, GPC_UNIT(gpc, 0x2c90), 0xffffffff); + nv_wr32(priv, GPC_UNIT(gpc, 0x2c94), 0xffffffff); + } + + for (rop = 0; rop < priv->rop_nr; rop++) { + nv_wr32(priv, ROP_UNIT(rop, 0x144), 0xc0000000); + nv_wr32(priv, ROP_UNIT(rop, 0x070), 0xc0000000); + nv_wr32(priv, ROP_UNIT(rop, 0x204), 0xffffffff); + nv_wr32(priv, ROP_UNIT(rop, 0x208), 0xffffffff); + } + + nv_wr32(priv, 0x400108, 0xffffffff); + nv_wr32(priv, 0x400138, 0xffffffff); + nv_wr32(priv, 0x400118, 0xffffffff); + nv_wr32(priv, 0x400130, 0xffffffff); + nv_wr32(priv, 0x40011c, 0xffffffff); + nv_wr32(priv, 0x400134, 0xffffffff); + + nv_wr32(priv, 0x400054, 0x34ce3464); + + gf100_gr_zbc_init(priv); + + return gf100_gr_init_ctxctl(priv); +} + +int +gk104_gr_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_pmu *pmu = nvkm_pmu(parent); + if (pmu) + pmu->pgob(pmu, false); + return gf100_gr_ctor(parent, engine, oclass, data, size, pobject); +} + +#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), +}; + +struct nvkm_oclass * +gk104_gr_oclass = &(struct gf100_gr_oclass) { + .base.handle = NV_ENGINE(GR, 0xe4), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gk104_gr_ctor, + .dtor = gf100_gr_dtor, + .init = gk104_gr_init, + .fini = _nvkm_gr_fini, + }, + .cclass = &gk104_grctx_oclass, + .sclass = gk104_gr_sclass, + .mmio = gk104_gr_pack_mmio, + .fecs.ucode = &gk104_gr_fecs_ucode, + .gpccs.ucode = &gk104_gr_gpccs_ucode, + .ppc_nr = 1, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk110.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk110.c new file mode 100644 index 000000000..f4cd8e554 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk110.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 "gf100.h" +#include "ctxgf100.h" + +#include + +#include + +/******************************************************************************* + * Graphics object classes + ******************************************************************************/ + +struct nvkm_oclass +gk110_gr_sclass[] = { + { FERMI_TWOD_A, &nvkm_object_ofuncs }, + { KEPLER_INLINE_TO_MEMORY_B, &nvkm_object_ofuncs }, + { KEPLER_B, &gf100_fermi_ofuncs, gf100_gr_9097_omthds }, + { KEPLER_COMPUTE_B, &nvkm_object_ofuncs, gf100_gr_90c0_omthds }, + {} +}; + +/******************************************************************************* + * 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_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 }, + {} +}; + +/******************************************************************************* + * 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), +}; + +struct nvkm_oclass * +gk110_gr_oclass = &(struct gf100_gr_oclass) { + .base.handle = NV_ENGINE(GR, 0xf0), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gk104_gr_ctor, + .dtor = gf100_gr_dtor, + .init = gk104_gr_init, + .fini = _nvkm_gr_fini, + }, + .cclass = &gk110_grctx_oclass, + .sclass = gk110_gr_sclass, + .mmio = gk110_gr_pack_mmio, + .fecs.ucode = &gk110_gr_fecs_ucode, + .gpccs.ucode = &gk110_gr_gpccs_ucode, + .ppc_nr = 2, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk110b.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk110b.c new file mode 100644 index 000000000..9ff9eab0c --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk110b.c @@ -0,0 +1,116 @@ +/* + * 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" + +/******************************************************************************* + * 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_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 + ******************************************************************************/ + +struct nvkm_oclass * +gk110b_gr_oclass = &(struct gf100_gr_oclass) { + .base.handle = NV_ENGINE(GR, 0xf1), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gk104_gr_ctor, + .dtor = gf100_gr_dtor, + .init = gk104_gr_init, + .fini = _nvkm_gr_fini, + }, + .cclass = &gk110b_grctx_oclass, + .sclass = gk110_gr_sclass, + .mmio = gk110b_gr_pack_mmio, + .fecs.ucode = &gk110_gr_fecs_ucode, + .gpccs.ucode = &gk110_gr_gpccs_ucode, + .ppc_nr = 2, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk208.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk208.c new file mode 100644 index 000000000..85f44a3d5 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk208.c @@ -0,0 +1,190 @@ +/* + * 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 + +/******************************************************************************* + * Graphics object classes + ******************************************************************************/ + +static struct nvkm_oclass +gk208_gr_sclass[] = { + { FERMI_TWOD_A, &nvkm_object_ofuncs }, + { KEPLER_INLINE_TO_MEMORY_B, &nvkm_object_ofuncs }, + { KEPLER_B, &gf100_fermi_ofuncs }, + { KEPLER_COMPUTE_B, &nvkm_object_ofuncs }, + {} +}; + +/******************************************************************************* + * 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_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), +}; + +struct nvkm_oclass * +gk208_gr_oclass = &(struct gf100_gr_oclass) { + .base.handle = NV_ENGINE(GR, 0x08), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gk104_gr_ctor, + .dtor = gf100_gr_dtor, + .init = gk104_gr_init, + .fini = _nvkm_gr_fini, + }, + .cclass = &gk208_grctx_oclass, + .sclass = gk208_gr_sclass, + .mmio = gk208_gr_pack_mmio, + .fecs.ucode = &gk208_gr_fecs_ucode, + .gpccs.ucode = &gk208_gr_gpccs_ucode, + .ppc_nr = 1, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk20a.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk20a.c new file mode 100644 index 000000000..40ff5eb91 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk20a.c @@ -0,0 +1,49 @@ +/* + * 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" +#include "ctxgf100.h" + +#include + +static struct nvkm_oclass +gk20a_gr_sclass[] = { + { FERMI_TWOD_A, &nvkm_object_ofuncs }, + { KEPLER_INLINE_TO_MEMORY_A, &nvkm_object_ofuncs }, + { KEPLER_C, &gf100_fermi_ofuncs, gf100_gr_9097_omthds }, + { KEPLER_COMPUTE_A, &nvkm_object_ofuncs, gf100_gr_90c0_omthds }, + {} +}; + +struct nvkm_oclass * +gk20a_gr_oclass = &(struct gf100_gr_oclass) { + .base.handle = NV_ENGINE(GR, 0xea), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_gr_ctor, + .dtor = gf100_gr_dtor, + .init = gk104_gr_init, + .fini = _nvkm_gr_fini, + }, + .cclass = &gk20a_grctx_oclass, + .sclass = gk20a_gr_sclass, + .mmio = gk104_gr_pack_mmio, + .ppc_nr = 1, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gm107.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gm107.c new file mode 100644 index 000000000..a5ebd459b --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gm107.c @@ -0,0 +1,470 @@ +/* + * 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 + +/******************************************************************************* + * Graphics object classes + ******************************************************************************/ + +static struct nvkm_oclass +gm107_gr_sclass[] = { + { FERMI_TWOD_A, &nvkm_object_ofuncs }, + { KEPLER_INLINE_TO_MEMORY_B, &nvkm_object_ofuncs }, + { MAXWELL_A, &gf100_fermi_ofuncs, gf100_gr_9097_omthds }, + { MAXWELL_COMPUTE_A, &nvkm_object_ofuncs, gf100_gr_90c0_omthds }, + {} +}; + +/******************************************************************************* + * PGRAPH register lists + ******************************************************************************/ + +static const struct gf100_gr_init +gm107_gr_init_main_0[] = { + { 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 }, + { 0x40880c, 1, 0x04, 0x00000000 }, + { 0x408850, 1, 0x04, 0x00000004 }, + { 0x408878, 1, 0x04, 0x00c81603 }, + { 0x40887c, 1, 0x04, 0x80543432 }, + { 0x408880, 1, 0x04, 0x0010581e }, + { 0x408884, 1, 0x04, 0x00001205 }, + { 0x408974, 1, 0x04, 0x000000ff }, + { 0x408910, 9, 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 }, + { 0x408984, 1, 0x04, 0x00000000 }, + { 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 }, + { 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_bios(struct gf100_gr_priv *priv) +{ + static const struct { + u32 ctrl; + u32 data; + } regs[] = { + { 0x419ed8, 0x419ee0 }, + { 0x419ad0, 0x419ad4 }, + { 0x419ae0, 0x419ae4 }, + { 0x419af0, 0x419af4 }, + { 0x419af8, 0x419afc }, + }; + struct nvkm_bios *bios = nvkm_bios(priv); + 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)) { + nv_wr32(priv, regs[E].ctrl, infoE.data); + while (nvbios_P0260Xp(bios, ++X, &ver, &hdr, &infoX)) + nv_wr32(priv, regs[E].data, infoX.data); + } + } +} + +int +gm107_gr_init(struct nvkm_object *object) +{ + struct gf100_gr_oclass *oclass = (void *)object->oclass; + struct gf100_gr_priv *priv = (void *)object; + const u32 magicgpc918 = DIV_ROUND_UP(0x00800000, priv->tpc_total); + u32 data[TPC_MAX / 8] = {}; + u8 tpcnr[GPC_MAX]; + int gpc, tpc, ppc, rop; + int ret, i; + + ret = nvkm_gr_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, GPC_BCAST(0x0880), 0x00000000); + nv_wr32(priv, GPC_BCAST(0x0890), 0x00000000); + nv_wr32(priv, GPC_BCAST(0x0894), 0x00000000); + nv_wr32(priv, GPC_BCAST(0x08b4), priv->unk4188b4->addr >> 8); + nv_wr32(priv, GPC_BCAST(0x08b8), priv->unk4188b8->addr >> 8); + + gf100_gr_mmio(priv, oclass->mmio); + + gm107_gr_init_bios(priv); + + nv_wr32(priv, GPC_UNIT(0, 0x3018), 0x00000001); + + memset(data, 0x00, sizeof(data)); + memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr)); + for (i = 0, gpc = -1; i < priv->tpc_total; i++) { + do { + gpc = (gpc + 1) % priv->gpc_nr; + } while (!tpcnr[gpc]); + tpc = priv->tpc_nr[gpc] - tpcnr[gpc]--; + + data[i / 8] |= tpc << ((i % 8) * 4); + } + + nv_wr32(priv, GPC_BCAST(0x0980), data[0]); + nv_wr32(priv, GPC_BCAST(0x0984), data[1]); + nv_wr32(priv, GPC_BCAST(0x0988), data[2]); + nv_wr32(priv, GPC_BCAST(0x098c), data[3]); + + for (gpc = 0; gpc < priv->gpc_nr; gpc++) { + nv_wr32(priv, GPC_UNIT(gpc, 0x0914), + priv->magic_not_rop_nr << 8 | priv->tpc_nr[gpc]); + nv_wr32(priv, GPC_UNIT(gpc, 0x0910), 0x00040000 | + priv->tpc_total); + nv_wr32(priv, GPC_UNIT(gpc, 0x0918), magicgpc918); + } + + nv_wr32(priv, GPC_BCAST(0x3fd4), magicgpc918); + nv_wr32(priv, GPC_BCAST(0x08ac), nv_rd32(priv, 0x100800)); + + nv_wr32(priv, 0x400500, 0x00010001); + + nv_wr32(priv, 0x400100, 0xffffffff); + nv_wr32(priv, 0x40013c, 0xffffffff); + nv_wr32(priv, 0x400124, 0x00000002); + nv_wr32(priv, 0x409c24, 0x000e0000); + + nv_wr32(priv, 0x404000, 0xc0000000); + nv_wr32(priv, 0x404600, 0xc0000000); + nv_wr32(priv, 0x408030, 0xc0000000); + nv_wr32(priv, 0x404490, 0xc0000000); + nv_wr32(priv, 0x406018, 0xc0000000); + nv_wr32(priv, 0x407020, 0x40000000); + nv_wr32(priv, 0x405840, 0xc0000000); + nv_wr32(priv, 0x405844, 0x00ffffff); + nv_mask(priv, 0x419cc0, 0x00000008, 0x00000008); + + for (gpc = 0; gpc < priv->gpc_nr; gpc++) { + for (ppc = 0; ppc < 2 /* priv->ppc_nr[gpc] */; ppc++) + nv_wr32(priv, PPC_UNIT(gpc, ppc, 0x038), 0xc0000000); + nv_wr32(priv, GPC_UNIT(gpc, 0x0420), 0xc0000000); + nv_wr32(priv, GPC_UNIT(gpc, 0x0900), 0xc0000000); + nv_wr32(priv, GPC_UNIT(gpc, 0x1028), 0xc0000000); + nv_wr32(priv, GPC_UNIT(gpc, 0x0824), 0xc0000000); + for (tpc = 0; tpc < priv->tpc_nr[gpc]; tpc++) { + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x508), 0xffffffff); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x50c), 0xffffffff); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x224), 0xc0000000); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x48c), 0xc0000000); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x084), 0xc0000000); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x430), 0xc0000000); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x644), 0x00dffffe); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x64c), 0x00000005); + } + nv_wr32(priv, GPC_UNIT(gpc, 0x2c90), 0xffffffff); + nv_wr32(priv, GPC_UNIT(gpc, 0x2c94), 0xffffffff); + } + + for (rop = 0; rop < priv->rop_nr; rop++) { + nv_wr32(priv, ROP_UNIT(rop, 0x144), 0x40000000); + nv_wr32(priv, ROP_UNIT(rop, 0x070), 0x40000000); + nv_wr32(priv, ROP_UNIT(rop, 0x204), 0xffffffff); + nv_wr32(priv, ROP_UNIT(rop, 0x208), 0xffffffff); + } + + nv_wr32(priv, 0x400108, 0xffffffff); + nv_wr32(priv, 0x400138, 0xffffffff); + nv_wr32(priv, 0x400118, 0xffffffff); + nv_wr32(priv, 0x400130, 0xffffffff); + nv_wr32(priv, 0x40011c, 0xffffffff); + nv_wr32(priv, 0x400134, 0xffffffff); + + nv_wr32(priv, 0x400054, 0x2c350f63); + + gf100_gr_zbc_init(priv); + + return gf100_gr_init_ctxctl(priv); +} + +#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), +}; + +struct nvkm_oclass * +gm107_gr_oclass = &(struct gf100_gr_oclass) { + .base.handle = NV_ENGINE(GR, 0x07), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_gr_ctor, + .dtor = gf100_gr_dtor, + .init = gm107_gr_init, + .fini = _nvkm_gr_fini, + }, + .cclass = &gm107_grctx_oclass, + .sclass = gm107_gr_sclass, + .mmio = gm107_gr_pack_mmio, + .fecs.ucode = &gm107_gr_fecs_ucode, + .gpccs.ucode = &gm107_gr_gpccs_ucode, + .ppc_nr = 2, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gm204.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gm204.c new file mode 100644 index 000000000..fdb1dcf16 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gm204.c @@ -0,0 +1,386 @@ +/* + * 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 + +/******************************************************************************* + * Graphics object classes + ******************************************************************************/ + +struct nvkm_oclass +gm204_gr_sclass[] = { + { FERMI_TWOD_A, &nvkm_object_ofuncs }, + { KEPLER_INLINE_TO_MEMORY_B, &nvkm_object_ofuncs }, + { MAXWELL_B, &gf100_fermi_ofuncs, gf100_gr_9097_omthds }, + { MAXWELL_COMPUTE_B, &nvkm_object_ofuncs, gf100_gr_90c0_omthds }, + {} +}; + +/******************************************************************************* + * PGRAPH register lists + ******************************************************************************/ + +static const struct gf100_gr_init +gm204_gr_init_main_0[] = { + { 0x400080, 1, 0x04, 0x003003e2 }, + { 0x400088, 1, 0x04, 0xe007bfe7 }, + { 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 +gm204_gr_init_fe_0[] = { + { 0x40415c, 1, 0x04, 0x00000000 }, + { 0x404170, 1, 0x04, 0x00000000 }, + { 0x4041b4, 1, 0x04, 0x00000000 }, + { 0x4041b8, 1, 0x04, 0x00000010 }, + {} +}; + +static const struct gf100_gr_init +gm204_gr_init_ds_0[] = { + { 0x40583c, 1, 0x04, 0x00000000 }, + { 0x405844, 1, 0x04, 0x00ffffff }, + { 0x40584c, 1, 0x04, 0x00000001 }, + { 0x405850, 1, 0x04, 0x00000000 }, + { 0x405900, 1, 0x04, 0x00000000 }, + { 0x405908, 1, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gm204_gr_init_sked_0[] = { + { 0x407010, 1, 0x04, 0x00000000 }, + { 0x407040, 1, 0x04, 0x80440434 }, + { 0x407048, 1, 0x04, 0x00000008 }, + {} +}; + +static const struct gf100_gr_init +gm204_gr_init_tpccs_0[] = { + { 0x419d60, 1, 0x04, 0x0000003f }, + { 0x419d88, 3, 0x04, 0x00000000 }, + { 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 }, + { 0x419de8, 1, 0x04, 0x000000cc }, + { 0x419dec, 1, 0x04, 0x00000000 }, + { 0x419df0, 1, 0x04, 0x000000cc }, + { 0x419df4, 1, 0x04, 0x00000000 }, + { 0x419d0c, 1, 0x04, 0x00000000 }, + { 0x419d10, 1, 0x04, 0x00000014 }, + {} +}; + +static const struct gf100_gr_init +gm204_gr_init_pe_0[] = { + { 0x419900, 1, 0x04, 0x000000ff }, + { 0x419810, 1, 0x04, 0x00000000 }, + { 0x41980c, 1, 0x04, 0x00000010 }, + { 0x419844, 1, 0x04, 0x00000000 }, + { 0x419838, 1, 0x04, 0x000000ff }, + { 0x419850, 1, 0x04, 0x00000004 }, + { 0x419854, 2, 0x04, 0x00000000 }, + { 0x419894, 3, 0x04, 0x00100401 }, + {} +}; + +static const struct gf100_gr_init +gm204_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, 0x00000000 }, + { 0x419ee8, 1, 0x04, 0x00000091 }, + { 0x419eb4, 1, 0x04, 0x00000000 }, + { 0x419ebc, 2, 0x04, 0x00000000 }, + { 0x419edc, 1, 0x04, 0x000c1810 }, + { 0x419ed8, 1, 0x04, 0x00000000 }, + { 0x419ee0, 1, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gm204_gr_init_l1c_1[] = { + { 0x419cf8, 2, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gm204_gr_init_sm_1[] = { + { 0x419f74, 1, 0x04, 0x00055155 }, + { 0x419f80, 4, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gm204_gr_init_l1c_2[] = { + { 0x419ccc, 2, 0x04, 0x00000000 }, + { 0x419c80, 1, 0x04, 0x3f006022 }, + { 0x419c88, 1, 0x04, 0x00210000 }, + {} +}; + +static const struct gf100_gr_init +gm204_gr_init_pes_0[] = { + { 0x41be50, 1, 0x04, 0x000000ff }, + { 0x41be04, 1, 0x04, 0x00000000 }, + { 0x41be08, 1, 0x04, 0x00000004 }, + { 0x41be0c, 1, 0x04, 0x00000008 }, + { 0x41be10, 1, 0x04, 0x2e3b8bc7 }, + { 0x41be14, 2, 0x04, 0x00000000 }, + { 0x41be3c, 5, 0x04, 0x00100401 }, + {} +}; + +static const struct gf100_gr_init +gm204_gr_init_be_0[] = { + { 0x408890, 1, 0x04, 0x000000ff }, + { 0x40880c, 1, 0x04, 0x00000000 }, + { 0x408850, 1, 0x04, 0x00000004 }, + { 0x408878, 1, 0x04, 0x01b4201c }, + { 0x40887c, 1, 0x04, 0x80004c55 }, + { 0x408880, 1, 0x04, 0x0018c258 }, + { 0x408884, 1, 0x04, 0x0000160f }, + { 0x408974, 1, 0x04, 0x000000ff }, + { 0x408910, 9, 0x04, 0x00000000 }, + { 0x408950, 1, 0x04, 0x00000000 }, + { 0x408954, 1, 0x04, 0x0000ffff }, + { 0x408958, 1, 0x04, 0x00000034 }, + { 0x40895c, 1, 0x04, 0x84b17403 }, + { 0x408960, 1, 0x04, 0x04c1884f }, + { 0x408964, 1, 0x04, 0x04714445 }, + { 0x408968, 1, 0x04, 0x0280802f }, + { 0x40896c, 1, 0x04, 0x04304856 }, + { 0x408970, 1, 0x04, 0x00012800 }, + { 0x408984, 1, 0x04, 0x00000000 }, + { 0x408988, 1, 0x04, 0x08040201 }, + { 0x40898c, 1, 0x04, 0x80402010 }, + {} +}; + +const struct gf100_gr_pack +gm204_gr_pack_mmio[] = { + { gm204_gr_init_main_0 }, + { gm204_gr_init_fe_0 }, + { gf100_gr_init_pri_0 }, + { gf100_gr_init_rstr2d_0 }, + { gf100_gr_init_pd_0 }, + { gm204_gr_init_ds_0 }, + { gm107_gr_init_scc_0 }, + { gm204_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 }, + { gm204_gr_init_tpccs_0 }, + { gm107_gr_init_tex_0 }, + { gm204_gr_init_pe_0 }, + { gm107_gr_init_l1c_0 }, + { gf100_gr_init_mpc_0 }, + { gm204_gr_init_sm_0 }, + { gm204_gr_init_l1c_1 }, + { gm204_gr_init_sm_1 }, + { gm204_gr_init_l1c_2 }, + { gm204_gr_init_pes_0 }, + { gm107_gr_init_wwdx_0 }, + { gm107_gr_init_cbm_0 }, + { gm204_gr_init_be_0 }, + {} +}; + +const struct gf100_gr_pack * +gm204_gr_data[] = { + gm204_gr_pack_mmio, + NULL +}; + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +static int +gm204_gr_init_ctxctl(struct gf100_gr_priv *priv) +{ + return 0; +} + +int +gm204_gr_init(struct nvkm_object *object) +{ + struct gf100_gr_oclass *oclass = (void *)object->oclass; + struct gf100_gr_priv *priv = (void *)object; + const u32 magicgpc918 = DIV_ROUND_UP(0x00800000, priv->tpc_total); + u32 data[TPC_MAX / 8] = {}; + u8 tpcnr[GPC_MAX]; + int gpc, tpc, ppc, rop; + int ret, i; + u32 tmp; + + ret = nvkm_gr_init(&priv->base); + if (ret) + return ret; + + tmp = nv_rd32(priv, 0x100c80); /*XXX: mask? */ + nv_wr32(priv, 0x418880, 0x00001000 | (tmp & 0x00000fff)); + nv_wr32(priv, 0x418890, 0x00000000); + nv_wr32(priv, 0x418894, 0x00000000); + nv_wr32(priv, 0x4188b4, priv->unk4188b4->addr >> 8); + nv_wr32(priv, 0x4188b8, priv->unk4188b8->addr >> 8); + nv_mask(priv, 0x4188b0, 0x00040000, 0x00040000); + + /*XXX: belongs in fb */ + nv_wr32(priv, 0x100cc8, priv->unk4188b4->addr >> 8); + nv_wr32(priv, 0x100ccc, priv->unk4188b8->addr >> 8); + nv_mask(priv, 0x100cc4, 0x00040000, 0x00040000); + + gf100_gr_mmio(priv, oclass->mmio); + + gm107_gr_init_bios(priv); + + nv_wr32(priv, GPC_UNIT(0, 0x3018), 0x00000001); + + memset(data, 0x00, sizeof(data)); + memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr)); + for (i = 0, gpc = -1; i < priv->tpc_total; i++) { + do { + gpc = (gpc + 1) % priv->gpc_nr; + } while (!tpcnr[gpc]); + tpc = priv->tpc_nr[gpc] - tpcnr[gpc]--; + + data[i / 8] |= tpc << ((i % 8) * 4); + } + + nv_wr32(priv, GPC_BCAST(0x0980), data[0]); + nv_wr32(priv, GPC_BCAST(0x0984), data[1]); + nv_wr32(priv, GPC_BCAST(0x0988), data[2]); + nv_wr32(priv, GPC_BCAST(0x098c), data[3]); + + for (gpc = 0; gpc < priv->gpc_nr; gpc++) { + nv_wr32(priv, GPC_UNIT(gpc, 0x0914), + priv->magic_not_rop_nr << 8 | priv->tpc_nr[gpc]); + nv_wr32(priv, GPC_UNIT(gpc, 0x0910), 0x00040000 | + priv->tpc_total); + nv_wr32(priv, GPC_UNIT(gpc, 0x0918), magicgpc918); + } + + nv_wr32(priv, GPC_BCAST(0x3fd4), magicgpc918); + nv_wr32(priv, GPC_BCAST(0x08ac), nv_rd32(priv, 0x100800)); + nv_wr32(priv, GPC_BCAST(0x033c), nv_rd32(priv, 0x100804)); + + nv_wr32(priv, 0x400500, 0x00010001); + nv_wr32(priv, 0x400100, 0xffffffff); + nv_wr32(priv, 0x40013c, 0xffffffff); + nv_wr32(priv, 0x400124, 0x00000002); + nv_wr32(priv, 0x409c24, 0x000e0000); + nv_wr32(priv, 0x405848, 0xc0000000); + nv_wr32(priv, 0x40584c, 0x00000001); + nv_wr32(priv, 0x404000, 0xc0000000); + nv_wr32(priv, 0x404600, 0xc0000000); + nv_wr32(priv, 0x408030, 0xc0000000); + nv_wr32(priv, 0x404490, 0xc0000000); + nv_wr32(priv, 0x406018, 0xc0000000); + nv_wr32(priv, 0x407020, 0x40000000); + nv_wr32(priv, 0x405840, 0xc0000000); + nv_wr32(priv, 0x405844, 0x00ffffff); + nv_mask(priv, 0x419cc0, 0x00000008, 0x00000008); + + for (gpc = 0; gpc < priv->gpc_nr; gpc++) { + for (ppc = 0; ppc < priv->ppc_nr[gpc]; ppc++) + nv_wr32(priv, PPC_UNIT(gpc, ppc, 0x038), 0xc0000000); + nv_wr32(priv, GPC_UNIT(gpc, 0x0420), 0xc0000000); + nv_wr32(priv, GPC_UNIT(gpc, 0x0900), 0xc0000000); + nv_wr32(priv, GPC_UNIT(gpc, 0x1028), 0xc0000000); + nv_wr32(priv, GPC_UNIT(gpc, 0x0824), 0xc0000000); + for (tpc = 0; tpc < priv->tpc_nr[gpc]; tpc++) { + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x508), 0xffffffff); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x50c), 0xffffffff); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x224), 0xc0000000); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x48c), 0xc0000000); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x084), 0xc0000000); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x430), 0xc0000000); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x644), 0x00dffffe); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x64c), 0x00000005); + } + nv_wr32(priv, GPC_UNIT(gpc, 0x2c90), 0xffffffff); + nv_wr32(priv, GPC_UNIT(gpc, 0x2c94), 0xffffffff); + } + + for (rop = 0; rop < priv->rop_nr; rop++) { + nv_wr32(priv, ROP_UNIT(rop, 0x144), 0x40000000); + nv_wr32(priv, ROP_UNIT(rop, 0x070), 0x40000000); + nv_wr32(priv, ROP_UNIT(rop, 0x204), 0xffffffff); + nv_wr32(priv, ROP_UNIT(rop, 0x208), 0xffffffff); + } + + nv_wr32(priv, 0x400108, 0xffffffff); + nv_wr32(priv, 0x400138, 0xffffffff); + nv_wr32(priv, 0x400118, 0xffffffff); + nv_wr32(priv, 0x400130, 0xffffffff); + nv_wr32(priv, 0x40011c, 0xffffffff); + nv_wr32(priv, 0x400134, 0xffffffff); + + nv_wr32(priv, 0x400054, 0x2c350f63); + + gf100_gr_zbc_init(priv); + + return gm204_gr_init_ctxctl(priv); +} + +struct nvkm_oclass * +gm204_gr_oclass = &(struct gf100_gr_oclass) { + .base.handle = NV_ENGINE(GR, 0x24), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_gr_ctor, + .dtor = gf100_gr_dtor, + .init = gm204_gr_init, + .fini = _nvkm_gr_fini, + }, + .cclass = &gm204_grctx_oclass, + .sclass = gm204_gr_sclass, + .mmio = gm204_gr_pack_mmio, + .ppc_nr = 2, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gm206.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gm206.c new file mode 100644 index 000000000..04b9733d1 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/gm206.c @@ -0,0 +1,40 @@ +/* + * 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" + +struct nvkm_oclass * +gm206_gr_oclass = &(struct gf100_gr_oclass) { + .base.handle = NV_ENGINE(GR, 0x26), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_gr_ctor, + .dtor = gf100_gr_dtor, + .init = gm204_gr_init, + .fini = _nvkm_gr_fini, + }, + .cclass = &gm206_grctx_oclass, + .sclass = gm204_gr_sclass, + .mmio = gm204_gr_pack_mmio, + .ppc_nr = 2, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv04.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv04.c new file mode 100644 index 000000000..2614510c2 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv04.c @@ -0,0 +1,1382 @@ +/* + * 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 +#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 +}; + +struct nv04_gr_priv { + struct nvkm_gr base; + struct nv04_gr_chan *chan[16]; + spinlock_t lock; +}; + +struct nv04_gr_chan { + struct nvkm_object base; + int chid; + u32 nv04[ARRAY_SIZE(nv04_gr_ctx_regs)]; +}; + + +static inline struct nv04_gr_priv * +nv04_gr_priv(struct nv04_gr_chan *chan) +{ + return (void *)nv_object(chan)->engine; +} + +/******************************************************************************* + * 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_object *object, u32 mask, u32 value) +{ + struct nv04_gr_priv *priv = (void *)object->engine; + int subc = (nv_rd32(priv, NV04_PGRAPH_TRAPPED_ADDR) >> 13) & 0x7; + u32 tmp; + + tmp = nv_ro32(object, 0x00); + tmp &= ~mask; + tmp |= value; + nv_wo32(object, 0x00, tmp); + + nv_wr32(priv, NV04_PGRAPH_CTX_SWITCH1, tmp); + nv_wr32(priv, NV04_PGRAPH_CTX_CACHE1 + (subc<<2), tmp); +} + +static void +nv04_gr_set_ctx_val(struct nvkm_object *object, u32 mask, u32 value) +{ + int class, op, valid = 1; + u32 tmp, ctx1; + + ctx1 = nv_ro32(object, 0x00); + class = ctx1 & 0xff; + op = (ctx1 >> 15) & 7; + + tmp = nv_ro32(object, 0x0c); + tmp &= ~mask; + tmp |= value; + nv_wo32(object, 0x0c, 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(object, 0x01000000, valid << 24); +} + +static int +nv04_gr_mthd_set_operation(struct nvkm_object *object, u32 mthd, + void *args, u32 size) +{ + u32 class = nv_ro32(object, 0) & 0xff; + u32 data = *(u32 *)args; + if (data > 5) + return 1; + /* Old versions of the objects only accept first three operations. */ + if (data > 2 && class < 0x40) + return 1; + nv04_gr_set_ctx1(object, 0x00038000, data << 15); + /* changing operation changes set of objects needed for validation */ + nv04_gr_set_ctx_val(object, 0, 0); + return 0; +} + +static int +nv04_gr_mthd_surf3d_clip_h(struct nvkm_object *object, u32 mthd, + void *args, u32 size) +{ + struct nv04_gr_priv *priv = (void *)object->engine; + u32 data = *(u32 *)args; + u32 min = data & 0xffff, max; + u32 w = data >> 16; + if (min & 0x8000) + /* too large */ + return 1; + if (w & 0x8000) + /* yes, it accepts negative for some reason. */ + w |= 0xffff0000; + max = min + w; + max &= 0x3ffff; + nv_wr32(priv, 0x40053c, min); + nv_wr32(priv, 0x400544, max); + return 0; +} + +static int +nv04_gr_mthd_surf3d_clip_v(struct nvkm_object *object, u32 mthd, + void *args, u32 size) +{ + struct nv04_gr_priv *priv = (void *)object->engine; + u32 data = *(u32 *)args; + u32 min = data & 0xffff, max; + u32 w = data >> 16; + if (min & 0x8000) + /* too large */ + return 1; + if (w & 0x8000) + /* yes, it accepts negative for some reason. */ + w |= 0xffff0000; + max = min + w; + max &= 0x3ffff; + nv_wr32(priv, 0x400540, min); + nv_wr32(priv, 0x400548, max); + return 0; +} + +static u16 +nv04_gr_mthd_bind_class(struct nvkm_object *object, u32 *args, u32 size) +{ + struct nvkm_instmem *imem = nvkm_instmem(object); + u32 inst = *(u32 *)args << 4; + return nv_ro32(imem, inst); +} + +static int +nv04_gr_mthd_bind_surf2d(struct nvkm_object *object, u32 mthd, + void *args, u32 size) +{ + switch (nv04_gr_mthd_bind_class(object, args, size)) { + case 0x30: + nv04_gr_set_ctx1(object, 0x00004000, 0); + nv04_gr_set_ctx_val(object, 0x02000000, 0); + return 0; + case 0x42: + nv04_gr_set_ctx1(object, 0x00004000, 0); + nv04_gr_set_ctx_val(object, 0x02000000, 0x02000000); + return 0; + } + return 1; +} + +static int +nv04_gr_mthd_bind_surf2d_swzsurf(struct nvkm_object *object, u32 mthd, + void *args, u32 size) +{ + switch (nv04_gr_mthd_bind_class(object, args, size)) { + case 0x30: + nv04_gr_set_ctx1(object, 0x00004000, 0); + nv04_gr_set_ctx_val(object, 0x02000000, 0); + return 0; + case 0x42: + nv04_gr_set_ctx1(object, 0x00004000, 0); + nv04_gr_set_ctx_val(object, 0x02000000, 0x02000000); + return 0; + case 0x52: + nv04_gr_set_ctx1(object, 0x00004000, 0x00004000); + nv04_gr_set_ctx_val(object, 0x02000000, 0x02000000); + return 0; + } + return 1; +} + +static int +nv01_gr_mthd_bind_patt(struct nvkm_object *object, u32 mthd, + void *args, u32 size) +{ + switch (nv04_gr_mthd_bind_class(object, args, size)) { + case 0x30: + nv04_gr_set_ctx_val(object, 0x08000000, 0); + return 0; + case 0x18: + nv04_gr_set_ctx_val(object, 0x08000000, 0x08000000); + return 0; + } + return 1; +} + +static int +nv04_gr_mthd_bind_patt(struct nvkm_object *object, u32 mthd, + void *args, u32 size) +{ + switch (nv04_gr_mthd_bind_class(object, args, size)) { + case 0x30: + nv04_gr_set_ctx_val(object, 0x08000000, 0); + return 0; + case 0x44: + nv04_gr_set_ctx_val(object, 0x08000000, 0x08000000); + return 0; + } + return 1; +} + +static int +nv04_gr_mthd_bind_rop(struct nvkm_object *object, u32 mthd, + void *args, u32 size) +{ + switch (nv04_gr_mthd_bind_class(object, args, size)) { + case 0x30: + nv04_gr_set_ctx_val(object, 0x10000000, 0); + return 0; + case 0x43: + nv04_gr_set_ctx_val(object, 0x10000000, 0x10000000); + return 0; + } + return 1; +} + +static int +nv04_gr_mthd_bind_beta1(struct nvkm_object *object, u32 mthd, + void *args, u32 size) +{ + switch (nv04_gr_mthd_bind_class(object, args, size)) { + case 0x30: + nv04_gr_set_ctx_val(object, 0x20000000, 0); + return 0; + case 0x12: + nv04_gr_set_ctx_val(object, 0x20000000, 0x20000000); + return 0; + } + return 1; +} + +static int +nv04_gr_mthd_bind_beta4(struct nvkm_object *object, u32 mthd, + void *args, u32 size) +{ + switch (nv04_gr_mthd_bind_class(object, args, size)) { + case 0x30: + nv04_gr_set_ctx_val(object, 0x40000000, 0); + return 0; + case 0x72: + nv04_gr_set_ctx_val(object, 0x40000000, 0x40000000); + return 0; + } + return 1; +} + +static int +nv04_gr_mthd_bind_surf_dst(struct nvkm_object *object, u32 mthd, + void *args, u32 size) +{ + switch (nv04_gr_mthd_bind_class(object, args, size)) { + case 0x30: + nv04_gr_set_ctx_val(object, 0x02000000, 0); + return 0; + case 0x58: + nv04_gr_set_ctx_val(object, 0x02000000, 0x02000000); + return 0; + } + return 1; +} + +static int +nv04_gr_mthd_bind_surf_src(struct nvkm_object *object, u32 mthd, + void *args, u32 size) +{ + switch (nv04_gr_mthd_bind_class(object, args, size)) { + case 0x30: + nv04_gr_set_ctx_val(object, 0x04000000, 0); + return 0; + case 0x59: + nv04_gr_set_ctx_val(object, 0x04000000, 0x04000000); + return 0; + } + return 1; +} + +static int +nv04_gr_mthd_bind_surf_color(struct nvkm_object *object, u32 mthd, + void *args, u32 size) +{ + switch (nv04_gr_mthd_bind_class(object, args, size)) { + case 0x30: + nv04_gr_set_ctx_val(object, 0x02000000, 0); + return 0; + case 0x5a: + nv04_gr_set_ctx_val(object, 0x02000000, 0x02000000); + return 0; + } + return 1; +} + +static int +nv04_gr_mthd_bind_surf_zeta(struct nvkm_object *object, u32 mthd, + void *args, u32 size) +{ + switch (nv04_gr_mthd_bind_class(object, args, size)) { + case 0x30: + nv04_gr_set_ctx_val(object, 0x04000000, 0); + return 0; + case 0x5b: + nv04_gr_set_ctx_val(object, 0x04000000, 0x04000000); + return 0; + } + return 1; +} + +static int +nv01_gr_mthd_bind_clip(struct nvkm_object *object, u32 mthd, + void *args, u32 size) +{ + switch (nv04_gr_mthd_bind_class(object, args, size)) { + case 0x30: + nv04_gr_set_ctx1(object, 0x2000, 0); + return 0; + case 0x19: + nv04_gr_set_ctx1(object, 0x2000, 0x2000); + return 0; + } + return 1; +} + +static int +nv01_gr_mthd_bind_chroma(struct nvkm_object *object, u32 mthd, + void *args, u32 size) +{ + switch (nv04_gr_mthd_bind_class(object, args, size)) { + case 0x30: + nv04_gr_set_ctx1(object, 0x1000, 0); + return 0; + /* Yes, for some reason even the old versions of objects + * accept 0x57 and not 0x17. Consistency be damned. + */ + case 0x57: + nv04_gr_set_ctx1(object, 0x1000, 0x1000); + return 0; + } + return 1; +} + +static struct nvkm_omthds +nv03_gr_gdi_omthds[] = { + { 0x0184, 0x0184, nv01_gr_mthd_bind_patt }, + { 0x0188, 0x0188, nv04_gr_mthd_bind_rop }, + { 0x018c, 0x018c, nv04_gr_mthd_bind_beta1 }, + { 0x0190, 0x0190, nv04_gr_mthd_bind_surf_dst }, + { 0x02fc, 0x02fc, nv04_gr_mthd_set_operation }, + {} +}; + +static struct nvkm_omthds +nv04_gr_gdi_omthds[] = { + { 0x0188, 0x0188, nv04_gr_mthd_bind_patt }, + { 0x018c, 0x018c, nv04_gr_mthd_bind_rop }, + { 0x0190, 0x0190, nv04_gr_mthd_bind_beta1 }, + { 0x0194, 0x0194, nv04_gr_mthd_bind_beta4 }, + { 0x0198, 0x0198, nv04_gr_mthd_bind_surf2d }, + { 0x02fc, 0x02fc, nv04_gr_mthd_set_operation }, + {} +}; + +static struct nvkm_omthds +nv01_gr_blit_omthds[] = { + { 0x0184, 0x0184, nv01_gr_mthd_bind_chroma }, + { 0x0188, 0x0188, nv01_gr_mthd_bind_clip }, + { 0x018c, 0x018c, nv01_gr_mthd_bind_patt }, + { 0x0190, 0x0190, nv04_gr_mthd_bind_rop }, + { 0x0194, 0x0194, nv04_gr_mthd_bind_beta1 }, + { 0x0198, 0x0198, nv04_gr_mthd_bind_surf_dst }, + { 0x019c, 0x019c, nv04_gr_mthd_bind_surf_src }, + { 0x02fc, 0x02fc, nv04_gr_mthd_set_operation }, + {} +}; + +static struct nvkm_omthds +nv04_gr_blit_omthds[] = { + { 0x0184, 0x0184, nv01_gr_mthd_bind_chroma }, + { 0x0188, 0x0188, nv01_gr_mthd_bind_clip }, + { 0x018c, 0x018c, nv04_gr_mthd_bind_patt }, + { 0x0190, 0x0190, nv04_gr_mthd_bind_rop }, + { 0x0194, 0x0194, nv04_gr_mthd_bind_beta1 }, + { 0x0198, 0x0198, nv04_gr_mthd_bind_beta4 }, + { 0x019c, 0x019c, nv04_gr_mthd_bind_surf2d }, + { 0x02fc, 0x02fc, nv04_gr_mthd_set_operation }, + {} +}; + +static struct nvkm_omthds +nv04_gr_iifc_omthds[] = { + { 0x0188, 0x0188, nv01_gr_mthd_bind_chroma }, + { 0x018c, 0x018c, nv01_gr_mthd_bind_clip }, + { 0x0190, 0x0190, nv04_gr_mthd_bind_patt }, + { 0x0194, 0x0194, nv04_gr_mthd_bind_rop }, + { 0x0198, 0x0198, nv04_gr_mthd_bind_beta1 }, + { 0x019c, 0x019c, nv04_gr_mthd_bind_beta4 }, + { 0x01a0, 0x01a0, nv04_gr_mthd_bind_surf2d_swzsurf }, + { 0x03e4, 0x03e4, nv04_gr_mthd_set_operation }, + {} +}; + +static struct nvkm_omthds +nv01_gr_ifc_omthds[] = { + { 0x0184, 0x0184, nv01_gr_mthd_bind_chroma }, + { 0x0188, 0x0188, nv01_gr_mthd_bind_clip }, + { 0x018c, 0x018c, nv01_gr_mthd_bind_patt }, + { 0x0190, 0x0190, nv04_gr_mthd_bind_rop }, + { 0x0194, 0x0194, nv04_gr_mthd_bind_beta1 }, + { 0x0198, 0x0198, nv04_gr_mthd_bind_surf_dst }, + { 0x02fc, 0x02fc, nv04_gr_mthd_set_operation }, + {} +}; + +static struct nvkm_omthds +nv04_gr_ifc_omthds[] = { + { 0x0184, 0x0184, nv01_gr_mthd_bind_chroma }, + { 0x0188, 0x0188, nv01_gr_mthd_bind_clip }, + { 0x018c, 0x018c, nv04_gr_mthd_bind_patt }, + { 0x0190, 0x0190, nv04_gr_mthd_bind_rop }, + { 0x0194, 0x0194, nv04_gr_mthd_bind_beta1 }, + { 0x0198, 0x0198, nv04_gr_mthd_bind_beta4 }, + { 0x019c, 0x019c, nv04_gr_mthd_bind_surf2d }, + { 0x02fc, 0x02fc, nv04_gr_mthd_set_operation }, + {} +}; + +static struct nvkm_omthds +nv03_gr_sifc_omthds[] = { + { 0x0184, 0x0184, nv01_gr_mthd_bind_chroma }, + { 0x0188, 0x0188, nv01_gr_mthd_bind_patt }, + { 0x018c, 0x018c, nv04_gr_mthd_bind_rop }, + { 0x0190, 0x0190, nv04_gr_mthd_bind_beta1 }, + { 0x0194, 0x0194, nv04_gr_mthd_bind_surf_dst }, + { 0x02fc, 0x02fc, nv04_gr_mthd_set_operation }, + {} +}; + +static struct nvkm_omthds +nv04_gr_sifc_omthds[] = { + { 0x0184, 0x0184, nv01_gr_mthd_bind_chroma }, + { 0x0188, 0x0188, nv04_gr_mthd_bind_patt }, + { 0x018c, 0x018c, nv04_gr_mthd_bind_rop }, + { 0x0190, 0x0190, nv04_gr_mthd_bind_beta1 }, + { 0x0194, 0x0194, nv04_gr_mthd_bind_beta4 }, + { 0x0198, 0x0198, nv04_gr_mthd_bind_surf2d }, + { 0x02fc, 0x02fc, nv04_gr_mthd_set_operation }, + {} +}; + +static struct nvkm_omthds +nv03_gr_sifm_omthds[] = { + { 0x0188, 0x0188, nv01_gr_mthd_bind_patt }, + { 0x018c, 0x018c, nv04_gr_mthd_bind_rop }, + { 0x0190, 0x0190, nv04_gr_mthd_bind_beta1 }, + { 0x0194, 0x0194, nv04_gr_mthd_bind_surf_dst }, + { 0x0304, 0x0304, nv04_gr_mthd_set_operation }, + {} +}; + +static struct nvkm_omthds +nv04_gr_sifm_omthds[] = { + { 0x0188, 0x0188, nv04_gr_mthd_bind_patt }, + { 0x018c, 0x018c, nv04_gr_mthd_bind_rop }, + { 0x0190, 0x0190, nv04_gr_mthd_bind_beta1 }, + { 0x0194, 0x0194, nv04_gr_mthd_bind_beta4 }, + { 0x0198, 0x0198, nv04_gr_mthd_bind_surf2d }, + { 0x0304, 0x0304, nv04_gr_mthd_set_operation }, + {} +}; + +static struct nvkm_omthds +nv04_gr_surf3d_omthds[] = { + { 0x02f8, 0x02f8, nv04_gr_mthd_surf3d_clip_h }, + { 0x02fc, 0x02fc, nv04_gr_mthd_surf3d_clip_v }, + {} +}; + +static struct nvkm_omthds +nv03_gr_ttri_omthds[] = { + { 0x0188, 0x0188, nv01_gr_mthd_bind_clip }, + { 0x018c, 0x018c, nv04_gr_mthd_bind_surf_color }, + { 0x0190, 0x0190, nv04_gr_mthd_bind_surf_zeta }, + {} +}; + +static struct nvkm_omthds +nv01_gr_prim_omthds[] = { + { 0x0184, 0x0184, nv01_gr_mthd_bind_clip }, + { 0x0188, 0x0188, nv01_gr_mthd_bind_patt }, + { 0x018c, 0x018c, nv04_gr_mthd_bind_rop }, + { 0x0190, 0x0190, nv04_gr_mthd_bind_beta1 }, + { 0x0194, 0x0194, nv04_gr_mthd_bind_surf_dst }, + { 0x02fc, 0x02fc, nv04_gr_mthd_set_operation }, + {} +}; + +static struct nvkm_omthds +nv04_gr_prim_omthds[] = { + { 0x0184, 0x0184, nv01_gr_mthd_bind_clip }, + { 0x0188, 0x0188, nv04_gr_mthd_bind_patt }, + { 0x018c, 0x018c, nv04_gr_mthd_bind_rop }, + { 0x0190, 0x0190, nv04_gr_mthd_bind_beta1 }, + { 0x0194, 0x0194, nv04_gr_mthd_bind_beta4 }, + { 0x0198, 0x0198, nv04_gr_mthd_bind_surf2d }, + { 0x02fc, 0x02fc, nv04_gr_mthd_set_operation }, + {} +}; + +static int +nv04_gr_object_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_gpuobj *obj; + int ret; + + ret = nvkm_gpuobj_create(parent, engine, oclass, 0, parent, + 16, 16, 0, &obj); + *pobject = nv_object(obj); + if (ret) + return ret; + + nv_wo32(obj, 0x00, nv_mclass(obj)); +#ifdef __BIG_ENDIAN + nv_mo32(obj, 0x00, 0x00080000, 0x00080000); +#endif + nv_wo32(obj, 0x04, 0x00000000); + nv_wo32(obj, 0x08, 0x00000000); + nv_wo32(obj, 0x0c, 0x00000000); + return 0; +} + +struct nvkm_ofuncs +nv04_gr_ofuncs = { + .ctor = nv04_gr_object_ctor, + .dtor = _nvkm_gpuobj_dtor, + .init = _nvkm_gpuobj_init, + .fini = _nvkm_gpuobj_fini, + .rd32 = _nvkm_gpuobj_rd32, + .wr32 = _nvkm_gpuobj_wr32, +}; + +static struct nvkm_oclass +nv04_gr_sclass[] = { + { 0x0012, &nv04_gr_ofuncs }, /* beta1 */ + { 0x0017, &nv04_gr_ofuncs }, /* chroma */ + { 0x0018, &nv04_gr_ofuncs }, /* pattern (nv01) */ + { 0x0019, &nv04_gr_ofuncs }, /* clip */ + { 0x001c, &nv04_gr_ofuncs, nv01_gr_prim_omthds }, /* line */ + { 0x001d, &nv04_gr_ofuncs, nv01_gr_prim_omthds }, /* tri */ + { 0x001e, &nv04_gr_ofuncs, nv01_gr_prim_omthds }, /* rect */ + { 0x001f, &nv04_gr_ofuncs, nv01_gr_blit_omthds }, + { 0x0021, &nv04_gr_ofuncs, nv01_gr_ifc_omthds }, + { 0x0030, &nv04_gr_ofuncs }, /* null */ + { 0x0036, &nv04_gr_ofuncs, nv03_gr_sifc_omthds }, + { 0x0037, &nv04_gr_ofuncs, nv03_gr_sifm_omthds }, + { 0x0038, &nv04_gr_ofuncs }, /* dvd subpicture */ + { 0x0039, &nv04_gr_ofuncs }, /* m2mf */ + { 0x0042, &nv04_gr_ofuncs }, /* surf2d */ + { 0x0043, &nv04_gr_ofuncs }, /* rop */ + { 0x0044, &nv04_gr_ofuncs }, /* pattern */ + { 0x0048, &nv04_gr_ofuncs, nv03_gr_ttri_omthds }, + { 0x004a, &nv04_gr_ofuncs, nv04_gr_gdi_omthds }, + { 0x004b, &nv04_gr_ofuncs, nv03_gr_gdi_omthds }, + { 0x0052, &nv04_gr_ofuncs }, /* swzsurf */ + { 0x0053, &nv04_gr_ofuncs, nv04_gr_surf3d_omthds }, + { 0x0054, &nv04_gr_ofuncs }, /* ttri */ + { 0x0055, &nv04_gr_ofuncs }, /* mtri */ + { 0x0057, &nv04_gr_ofuncs }, /* chroma */ + { 0x0058, &nv04_gr_ofuncs }, /* surf_dst */ + { 0x0059, &nv04_gr_ofuncs }, /* surf_src */ + { 0x005a, &nv04_gr_ofuncs }, /* surf_color */ + { 0x005b, &nv04_gr_ofuncs }, /* surf_zeta */ + { 0x005c, &nv04_gr_ofuncs, nv04_gr_prim_omthds }, /* line */ + { 0x005d, &nv04_gr_ofuncs, nv04_gr_prim_omthds }, /* tri */ + { 0x005e, &nv04_gr_ofuncs, nv04_gr_prim_omthds }, /* rect */ + { 0x005f, &nv04_gr_ofuncs, nv04_gr_blit_omthds }, + { 0x0060, &nv04_gr_ofuncs, nv04_gr_iifc_omthds }, + { 0x0061, &nv04_gr_ofuncs, nv04_gr_ifc_omthds }, + { 0x0064, &nv04_gr_ofuncs }, /* iifc (nv05) */ + { 0x0065, &nv04_gr_ofuncs }, /* ifc (nv05) */ + { 0x0066, &nv04_gr_ofuncs }, /* sifc (nv05) */ + { 0x0072, &nv04_gr_ofuncs }, /* beta4 */ + { 0x0076, &nv04_gr_ofuncs, nv04_gr_sifc_omthds }, + { 0x0077, &nv04_gr_ofuncs, nv04_gr_sifm_omthds }, + {}, +}; + +/******************************************************************************* + * PGRAPH context + ******************************************************************************/ + +static struct nv04_gr_chan * +nv04_gr_channel(struct nv04_gr_priv *priv) +{ + struct nv04_gr_chan *chan = NULL; + if (nv_rd32(priv, NV04_PGRAPH_CTX_CONTROL) & 0x00010000) { + int chid = nv_rd32(priv, NV04_PGRAPH_CTX_USER) >> 24; + if (chid < ARRAY_SIZE(priv->chan)) + chan = priv->chan[chid]; + } + return chan; +} + +static int +nv04_gr_load_context(struct nv04_gr_chan *chan, int chid) +{ + struct nv04_gr_priv *priv = nv04_gr_priv(chan); + int i; + + for (i = 0; i < ARRAY_SIZE(nv04_gr_ctx_regs); i++) + nv_wr32(priv, nv04_gr_ctx_regs[i], chan->nv04[i]); + + nv_wr32(priv, NV04_PGRAPH_CTX_CONTROL, 0x10010100); + nv_mask(priv, NV04_PGRAPH_CTX_USER, 0xff000000, chid << 24); + nv_mask(priv, NV04_PGRAPH_FFINTFC_ST2, 0xfff00000, 0x00000000); + return 0; +} + +static int +nv04_gr_unload_context(struct nv04_gr_chan *chan) +{ + struct nv04_gr_priv *priv = nv04_gr_priv(chan); + int i; + + for (i = 0; i < ARRAY_SIZE(nv04_gr_ctx_regs); i++) + chan->nv04[i] = nv_rd32(priv, nv04_gr_ctx_regs[i]); + + nv_wr32(priv, NV04_PGRAPH_CTX_CONTROL, 0x10000000); + nv_mask(priv, NV04_PGRAPH_CTX_USER, 0xff000000, 0x0f000000); + return 0; +} + +static void +nv04_gr_context_switch(struct nv04_gr_priv *priv) +{ + struct nv04_gr_chan *prev = NULL; + struct nv04_gr_chan *next = NULL; + unsigned long flags; + int chid; + + spin_lock_irqsave(&priv->lock, flags); + nv04_gr_idle(priv); + + /* If previous context is valid, we need to save it */ + prev = nv04_gr_channel(priv); + if (prev) + nv04_gr_unload_context(prev); + + /* load context for next channel */ + chid = (nv_rd32(priv, NV04_PGRAPH_TRAPPED_ADDR) >> 24) & 0x0f; + next = priv->chan[chid]; + if (next) + nv04_gr_load_context(next, chid); + + spin_unlock_irqrestore(&priv->lock, flags); +} + +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 int +nv04_gr_context_ctor(struct nvkm_object *parent, + struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_fifo_chan *fifo = (void *)parent; + struct nv04_gr_priv *priv = (void *)engine; + struct nv04_gr_chan *chan; + unsigned long flags; + int ret; + + ret = nvkm_object_create(parent, engine, oclass, 0, &chan); + *pobject = nv_object(chan); + if (ret) + return ret; + + spin_lock_irqsave(&priv->lock, flags); + if (priv->chan[fifo->chid]) { + *pobject = nv_object(priv->chan[fifo->chid]); + atomic_inc(&(*pobject)->refcount); + spin_unlock_irqrestore(&priv->lock, flags); + nvkm_object_destroy(&chan->base); + return 1; + } + + *ctx_reg(chan, NV04_PGRAPH_DEBUG_3) = 0xfad4ff31; + + priv->chan[fifo->chid] = chan; + chan->chid = fifo->chid; + spin_unlock_irqrestore(&priv->lock, flags); + return 0; +} + +static void +nv04_gr_context_dtor(struct nvkm_object *object) +{ + struct nv04_gr_priv *priv = (void *)object->engine; + struct nv04_gr_chan *chan = (void *)object; + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + priv->chan[chan->chid] = NULL; + spin_unlock_irqrestore(&priv->lock, flags); + + nvkm_object_destroy(&chan->base); +} + +static int +nv04_gr_context_fini(struct nvkm_object *object, bool suspend) +{ + struct nv04_gr_priv *priv = (void *)object->engine; + struct nv04_gr_chan *chan = (void *)object; + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + nv_mask(priv, NV04_PGRAPH_FIFO, 0x00000001, 0x00000000); + if (nv04_gr_channel(priv) == chan) + nv04_gr_unload_context(chan); + nv_mask(priv, NV04_PGRAPH_FIFO, 0x00000001, 0x00000001); + spin_unlock_irqrestore(&priv->lock, flags); + + return nvkm_object_fini(&chan->base, suspend); +} + +static struct nvkm_oclass +nv04_gr_cclass = { + .handle = NV_ENGCTX(GR, 0x04), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_gr_context_ctor, + .dtor = nv04_gr_context_dtor, + .init = nvkm_object_init, + .fini = nv04_gr_context_fini, + }, +}; + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +bool +nv04_gr_idle(void *obj) +{ + struct nvkm_gr *gr = nvkm_gr(obj); + u32 mask = 0xffffffff; + + if (nv_device(obj)->card_type == NV_40) + mask &= ~NV40_PGRAPH_STATUS_SYNC_STALL; + + if (!nv_wait(gr, NV04_PGRAPH_STATUS, mask, 0)) { + nv_error(gr, "idle timed out with status 0x%08x\n", + nv_rd32(gr, 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_subdev *subdev) +{ + struct nv04_gr_priv *priv = (void *)subdev; + struct nv04_gr_chan *chan = NULL; + struct nvkm_namedb *namedb = NULL; + struct nvkm_handle *handle = NULL; + u32 stat = nv_rd32(priv, NV03_PGRAPH_INTR); + u32 nsource = nv_rd32(priv, NV03_PGRAPH_NSOURCE); + u32 nstatus = nv_rd32(priv, NV03_PGRAPH_NSTATUS); + u32 addr = nv_rd32(priv, NV04_PGRAPH_TRAPPED_ADDR); + u32 chid = (addr & 0x0f000000) >> 24; + u32 subc = (addr & 0x0000e000) >> 13; + u32 mthd = (addr & 0x00001ffc); + u32 data = nv_rd32(priv, NV04_PGRAPH_TRAPPED_DATA); + u32 class = nv_rd32(priv, 0x400180 + subc * 4) & 0xff; + u32 inst = (nv_rd32(priv, 0x40016c) & 0xffff) << 4; + u32 show = stat; + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + chan = priv->chan[chid]; + if (chan) + namedb = (void *)nv_pclass(nv_object(chan), NV_NAMEDB_CLASS); + spin_unlock_irqrestore(&priv->lock, flags); + + if (stat & NV_PGRAPH_INTR_NOTIFY) { + if (chan && (nsource & NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD)) { + handle = nvkm_namedb_get_vinst(namedb, inst); + if (handle && !nv_call(handle->object, mthd, data)) + show &= ~NV_PGRAPH_INTR_NOTIFY; + } + } + + if (stat & NV_PGRAPH_INTR_CONTEXT_SWITCH) { + nv_wr32(priv, NV03_PGRAPH_INTR, NV_PGRAPH_INTR_CONTEXT_SWITCH); + stat &= ~NV_PGRAPH_INTR_CONTEXT_SWITCH; + show &= ~NV_PGRAPH_INTR_CONTEXT_SWITCH; + nv04_gr_context_switch(priv); + } + + nv_wr32(priv, NV03_PGRAPH_INTR, stat); + nv_wr32(priv, NV04_PGRAPH_FIFO, 0x00000001); + + if (show) { + nv_error(priv, "%s", ""); + nvkm_bitfield_print(nv04_gr_intr_name, show); + pr_cont(" nsource:"); + nvkm_bitfield_print(nv04_gr_nsource, nsource); + pr_cont(" nstatus:"); + nvkm_bitfield_print(nv04_gr_nstatus, nstatus); + pr_cont("\n"); + nv_error(priv, + "ch %d [%s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n", + chid, nvkm_client_name(chan), subc, class, mthd, + data); + } + + nvkm_namedb_put(handle); +} + +static int +nv04_gr_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv04_gr_priv *priv; + int ret; + + ret = nvkm_gr_create(parent, engine, oclass, true, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00001000; + nv_subdev(priv)->intr = nv04_gr_intr; + nv_engine(priv)->cclass = &nv04_gr_cclass; + nv_engine(priv)->sclass = nv04_gr_sclass; + spin_lock_init(&priv->lock); + return 0; +} + +static int +nv04_gr_init(struct nvkm_object *object) +{ + struct nvkm_engine *engine = nv_engine(object); + struct nv04_gr_priv *priv = (void *)engine; + int ret; + + ret = nvkm_gr_init(&priv->base); + if (ret) + return ret; + + /* Enable PGRAPH interrupts */ + nv_wr32(priv, NV03_PGRAPH_INTR, 0xFFFFFFFF); + nv_wr32(priv, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF); + + nv_wr32(priv, NV04_PGRAPH_VALID1, 0); + nv_wr32(priv, NV04_PGRAPH_VALID2, 0); + /*nv_wr32(priv, NV04_PGRAPH_DEBUG_0, 0x000001FF); + nv_wr32(priv, NV04_PGRAPH_DEBUG_0, 0x001FFFFF);*/ + nv_wr32(priv, NV04_PGRAPH_DEBUG_0, 0x1231c000); + /*1231C000 blob, 001 haiku*/ + /*V_WRITE(NV04_PGRAPH_DEBUG_1, 0xf2d91100);*/ + nv_wr32(priv, NV04_PGRAPH_DEBUG_1, 0x72111100); + /*0x72111100 blob , 01 haiku*/ + /*nv_wr32(priv, NV04_PGRAPH_DEBUG_2, 0x11d5f870);*/ + nv_wr32(priv, NV04_PGRAPH_DEBUG_2, 0x11d5f071); + /*haiku same*/ + + /*nv_wr32(priv, NV04_PGRAPH_DEBUG_3, 0xfad4ff31);*/ + nv_wr32(priv, NV04_PGRAPH_DEBUG_3, 0xf0d4ff31); + /*haiku and blob 10d4*/ + + nv_wr32(priv, NV04_PGRAPH_STATE , 0xFFFFFFFF); + nv_wr32(priv, NV04_PGRAPH_CTX_CONTROL , 0x10000100); + nv_mask(priv, NV04_PGRAPH_CTX_USER, 0xff000000, 0x0f000000); + + /* These don't belong here, they're part of a per-channel context */ + nv_wr32(priv, NV04_PGRAPH_PATTERN_SHAPE, 0x00000000); + nv_wr32(priv, NV04_PGRAPH_BETA_AND , 0xFFFFFFFF); + return 0; +} + +struct nvkm_oclass +nv04_gr_oclass = { + .handle = NV_ENGINE(GR, 0x04), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_gr_ctor, + .dtor = _nvkm_gr_dtor, + .init = nv04_gr_init, + .fini = _nvkm_gr_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv10.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv10.c new file mode 100644 index 000000000..389904eb6 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv10.c @@ -0,0 +1,1315 @@ +/* + * 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 +#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, +}; + +struct nv10_gr_priv { + struct nvkm_gr base; + struct nv10_gr_chan *chan[32]; + spinlock_t lock; +}; + +struct nv10_gr_chan { + struct nvkm_object base; + 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]; +}; + + +static inline struct nv10_gr_priv * +nv10_gr_priv(struct nv10_gr_chan *chan) +{ + return (void *)nv_object(chan)->engine; +} + +/******************************************************************************* + * Graphics object classes + ******************************************************************************/ + +#define PIPE_SAVE(priv, state, addr) \ + do { \ + int __i; \ + nv_wr32(priv, NV10_PGRAPH_PIPE_ADDRESS, addr); \ + for (__i = 0; __i < ARRAY_SIZE(state); __i++) \ + state[__i] = nv_rd32(priv, NV10_PGRAPH_PIPE_DATA); \ + } while (0) + +#define PIPE_RESTORE(priv, state, addr) \ + do { \ + int __i; \ + nv_wr32(priv, NV10_PGRAPH_PIPE_ADDRESS, addr); \ + for (__i = 0; __i < ARRAY_SIZE(state); __i++) \ + nv_wr32(priv, NV10_PGRAPH_PIPE_DATA, state[__i]); \ + } while (0) + +static struct nvkm_oclass +nv10_gr_sclass[] = { + { 0x0012, &nv04_gr_ofuncs }, /* beta1 */ + { 0x0019, &nv04_gr_ofuncs }, /* clip */ + { 0x0030, &nv04_gr_ofuncs }, /* null */ + { 0x0039, &nv04_gr_ofuncs }, /* m2mf */ + { 0x0043, &nv04_gr_ofuncs }, /* rop */ + { 0x0044, &nv04_gr_ofuncs }, /* pattern */ + { 0x004a, &nv04_gr_ofuncs }, /* gdi */ + { 0x0052, &nv04_gr_ofuncs }, /* swzsurf */ + { 0x005f, &nv04_gr_ofuncs }, /* blit */ + { 0x0062, &nv04_gr_ofuncs }, /* surf2d */ + { 0x0072, &nv04_gr_ofuncs }, /* beta4 */ + { 0x0089, &nv04_gr_ofuncs }, /* sifm */ + { 0x008a, &nv04_gr_ofuncs }, /* ifc */ + { 0x009f, &nv04_gr_ofuncs }, /* blit */ + { 0x0093, &nv04_gr_ofuncs }, /* surf3d */ + { 0x0094, &nv04_gr_ofuncs }, /* ttri */ + { 0x0095, &nv04_gr_ofuncs }, /* mtri */ + { 0x0056, &nv04_gr_ofuncs }, /* celcius */ + {}, +}; + +static struct nvkm_oclass +nv15_gr_sclass[] = { + { 0x0012, &nv04_gr_ofuncs }, /* beta1 */ + { 0x0019, &nv04_gr_ofuncs }, /* clip */ + { 0x0030, &nv04_gr_ofuncs }, /* null */ + { 0x0039, &nv04_gr_ofuncs }, /* m2mf */ + { 0x0043, &nv04_gr_ofuncs }, /* rop */ + { 0x0044, &nv04_gr_ofuncs }, /* pattern */ + { 0x004a, &nv04_gr_ofuncs }, /* gdi */ + { 0x0052, &nv04_gr_ofuncs }, /* swzsurf */ + { 0x005f, &nv04_gr_ofuncs }, /* blit */ + { 0x0062, &nv04_gr_ofuncs }, /* surf2d */ + { 0x0072, &nv04_gr_ofuncs }, /* beta4 */ + { 0x0089, &nv04_gr_ofuncs }, /* sifm */ + { 0x008a, &nv04_gr_ofuncs }, /* ifc */ + { 0x009f, &nv04_gr_ofuncs }, /* blit */ + { 0x0093, &nv04_gr_ofuncs }, /* surf3d */ + { 0x0094, &nv04_gr_ofuncs }, /* ttri */ + { 0x0095, &nv04_gr_ofuncs }, /* mtri */ + { 0x0096, &nv04_gr_ofuncs }, /* celcius */ + {}, +}; + +static int +nv17_gr_mthd_lma_window(struct nvkm_object *object, u32 mthd, + void *args, u32 size) +{ + struct nv10_gr_chan *chan = (void *)object->parent; + struct nv10_gr_priv *priv = nv10_gr_priv(chan); + struct pipe_state *pipe = &chan->pipe_state; + u32 pipe_0x0040[1], pipe_0x64c0[8], pipe_0x6a80[3], pipe_0x6ab0[3]; + u32 xfmode0, xfmode1; + u32 data = *(u32 *)args; + int i; + + chan->lma_window[(mthd - 0x1638) / 4] = data; + + if (mthd != 0x1644) + return 0; + + nv04_gr_idle(priv); + + PIPE_SAVE(priv, pipe_0x0040, 0x0040); + PIPE_SAVE(priv, pipe->pipe_0x0200, 0x0200); + + PIPE_RESTORE(priv, chan->lma_window, 0x6790); + + nv04_gr_idle(priv); + + xfmode0 = nv_rd32(priv, NV10_PGRAPH_XFMODE0); + xfmode1 = nv_rd32(priv, NV10_PGRAPH_XFMODE1); + + PIPE_SAVE(priv, pipe->pipe_0x4400, 0x4400); + PIPE_SAVE(priv, pipe_0x64c0, 0x64c0); + PIPE_SAVE(priv, pipe_0x6ab0, 0x6ab0); + PIPE_SAVE(priv, pipe_0x6a80, 0x6a80); + + nv04_gr_idle(priv); + + nv_wr32(priv, NV10_PGRAPH_XFMODE0, 0x10000000); + nv_wr32(priv, NV10_PGRAPH_XFMODE1, 0x00000000); + nv_wr32(priv, NV10_PGRAPH_PIPE_ADDRESS, 0x000064c0); + for (i = 0; i < 4; i++) + nv_wr32(priv, NV10_PGRAPH_PIPE_DATA, 0x3f800000); + for (i = 0; i < 4; i++) + nv_wr32(priv, NV10_PGRAPH_PIPE_DATA, 0x00000000); + + nv_wr32(priv, NV10_PGRAPH_PIPE_ADDRESS, 0x00006ab0); + for (i = 0; i < 3; i++) + nv_wr32(priv, NV10_PGRAPH_PIPE_DATA, 0x3f800000); + + nv_wr32(priv, NV10_PGRAPH_PIPE_ADDRESS, 0x00006a80); + for (i = 0; i < 3; i++) + nv_wr32(priv, NV10_PGRAPH_PIPE_DATA, 0x00000000); + + nv_wr32(priv, NV10_PGRAPH_PIPE_ADDRESS, 0x00000040); + nv_wr32(priv, NV10_PGRAPH_PIPE_DATA, 0x00000008); + + PIPE_RESTORE(priv, pipe->pipe_0x0200, 0x0200); + + nv04_gr_idle(priv); + + PIPE_RESTORE(priv, pipe_0x0040, 0x0040); + + nv_wr32(priv, NV10_PGRAPH_XFMODE0, xfmode0); + nv_wr32(priv, NV10_PGRAPH_XFMODE1, xfmode1); + + PIPE_RESTORE(priv, pipe_0x64c0, 0x64c0); + PIPE_RESTORE(priv, pipe_0x6ab0, 0x6ab0); + PIPE_RESTORE(priv, pipe_0x6a80, 0x6a80); + PIPE_RESTORE(priv, pipe->pipe_0x4400, 0x4400); + + nv_wr32(priv, NV10_PGRAPH_PIPE_ADDRESS, 0x000000c0); + nv_wr32(priv, NV10_PGRAPH_PIPE_DATA, 0x00000000); + + nv04_gr_idle(priv); + + return 0; +} + +static int +nv17_gr_mthd_lma_enable(struct nvkm_object *object, u32 mthd, + void *args, u32 size) +{ + struct nv10_gr_chan *chan = (void *)object->parent; + struct nv10_gr_priv *priv = nv10_gr_priv(chan); + + nv04_gr_idle(priv); + + nv_mask(priv, NV10_PGRAPH_DEBUG_4, 0x00000100, 0x00000100); + nv_mask(priv, 0x4006b0, 0x08000000, 0x08000000); + return 0; +} + +static struct nvkm_omthds +nv17_celcius_omthds[] = { + { 0x1638, 0x1638, nv17_gr_mthd_lma_window }, + { 0x163c, 0x163c, nv17_gr_mthd_lma_window }, + { 0x1640, 0x1640, nv17_gr_mthd_lma_window }, + { 0x1644, 0x1644, nv17_gr_mthd_lma_window }, + { 0x1658, 0x1658, nv17_gr_mthd_lma_enable }, + {} +}; + +static struct nvkm_oclass +nv17_gr_sclass[] = { + { 0x0012, &nv04_gr_ofuncs }, /* beta1 */ + { 0x0019, &nv04_gr_ofuncs }, /* clip */ + { 0x0030, &nv04_gr_ofuncs }, /* null */ + { 0x0039, &nv04_gr_ofuncs }, /* m2mf */ + { 0x0043, &nv04_gr_ofuncs }, /* rop */ + { 0x0044, &nv04_gr_ofuncs }, /* pattern */ + { 0x004a, &nv04_gr_ofuncs }, /* gdi */ + { 0x0052, &nv04_gr_ofuncs }, /* swzsurf */ + { 0x005f, &nv04_gr_ofuncs }, /* blit */ + { 0x0062, &nv04_gr_ofuncs }, /* surf2d */ + { 0x0072, &nv04_gr_ofuncs }, /* beta4 */ + { 0x0089, &nv04_gr_ofuncs }, /* sifm */ + { 0x008a, &nv04_gr_ofuncs }, /* ifc */ + { 0x009f, &nv04_gr_ofuncs }, /* blit */ + { 0x0093, &nv04_gr_ofuncs }, /* surf3d */ + { 0x0094, &nv04_gr_ofuncs }, /* ttri */ + { 0x0095, &nv04_gr_ofuncs }, /* mtri */ + { 0x0099, &nv04_gr_ofuncs, nv17_celcius_omthds }, + {}, +}; + +/******************************************************************************* + * PGRAPH context + ******************************************************************************/ + +static struct nv10_gr_chan * +nv10_gr_channel(struct nv10_gr_priv *priv) +{ + struct nv10_gr_chan *chan = NULL; + if (nv_rd32(priv, 0x400144) & 0x00010000) { + int chid = nv_rd32(priv, 0x400148) >> 24; + if (chid < ARRAY_SIZE(priv->chan)) + chan = priv->chan[chid]; + } + return chan; +} + +static void +nv10_gr_save_pipe(struct nv10_gr_chan *chan) +{ + struct nv10_gr_priv *priv = nv10_gr_priv(chan); + struct pipe_state *pipe = &chan->pipe_state; + + PIPE_SAVE(priv, pipe->pipe_0x4400, 0x4400); + PIPE_SAVE(priv, pipe->pipe_0x0200, 0x0200); + PIPE_SAVE(priv, pipe->pipe_0x6400, 0x6400); + PIPE_SAVE(priv, pipe->pipe_0x6800, 0x6800); + PIPE_SAVE(priv, pipe->pipe_0x6c00, 0x6c00); + PIPE_SAVE(priv, pipe->pipe_0x7000, 0x7000); + PIPE_SAVE(priv, pipe->pipe_0x7400, 0x7400); + PIPE_SAVE(priv, pipe->pipe_0x7800, 0x7800); + PIPE_SAVE(priv, pipe->pipe_0x0040, 0x0040); + PIPE_SAVE(priv, pipe->pipe_0x0000, 0x0000); +} + +static void +nv10_gr_load_pipe(struct nv10_gr_chan *chan) +{ + struct nv10_gr_priv *priv = nv10_gr_priv(chan); + struct pipe_state *pipe = &chan->pipe_state; + u32 xfmode0, xfmode1; + int i; + + nv04_gr_idle(priv); + /* XXX check haiku comments */ + xfmode0 = nv_rd32(priv, NV10_PGRAPH_XFMODE0); + xfmode1 = nv_rd32(priv, NV10_PGRAPH_XFMODE1); + nv_wr32(priv, NV10_PGRAPH_XFMODE0, 0x10000000); + nv_wr32(priv, NV10_PGRAPH_XFMODE1, 0x00000000); + nv_wr32(priv, NV10_PGRAPH_PIPE_ADDRESS, 0x000064c0); + for (i = 0; i < 4; i++) + nv_wr32(priv, NV10_PGRAPH_PIPE_DATA, 0x3f800000); + for (i = 0; i < 4; i++) + nv_wr32(priv, NV10_PGRAPH_PIPE_DATA, 0x00000000); + + nv_wr32(priv, NV10_PGRAPH_PIPE_ADDRESS, 0x00006ab0); + for (i = 0; i < 3; i++) + nv_wr32(priv, NV10_PGRAPH_PIPE_DATA, 0x3f800000); + + nv_wr32(priv, NV10_PGRAPH_PIPE_ADDRESS, 0x00006a80); + for (i = 0; i < 3; i++) + nv_wr32(priv, NV10_PGRAPH_PIPE_DATA, 0x00000000); + + nv_wr32(priv, NV10_PGRAPH_PIPE_ADDRESS, 0x00000040); + nv_wr32(priv, NV10_PGRAPH_PIPE_DATA, 0x00000008); + + + PIPE_RESTORE(priv, pipe->pipe_0x0200, 0x0200); + nv04_gr_idle(priv); + + /* restore XFMODE */ + nv_wr32(priv, NV10_PGRAPH_XFMODE0, xfmode0); + nv_wr32(priv, NV10_PGRAPH_XFMODE1, xfmode1); + PIPE_RESTORE(priv, pipe->pipe_0x6400, 0x6400); + PIPE_RESTORE(priv, pipe->pipe_0x6800, 0x6800); + PIPE_RESTORE(priv, pipe->pipe_0x6c00, 0x6c00); + PIPE_RESTORE(priv, pipe->pipe_0x7000, 0x7000); + PIPE_RESTORE(priv, pipe->pipe_0x7400, 0x7400); + PIPE_RESTORE(priv, pipe->pipe_0x7800, 0x7800); + PIPE_RESTORE(priv, pipe->pipe_0x4400, 0x4400); + PIPE_RESTORE(priv, pipe->pipe_0x0000, 0x0000); + PIPE_RESTORE(priv, pipe->pipe_0x0040, 0x0040); + nv04_gr_idle(priv); +} + +static void +nv10_gr_create_pipe(struct nv10_gr_chan *chan) +{ + struct nv10_gr_priv *priv = nv10_gr_priv(chan); + 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) \ + nv_error(priv, "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_priv *priv, int reg) +{ + int i; + for (i = 0; i < ARRAY_SIZE(nv10_gr_ctx_regs); i++) { + if (nv10_gr_ctx_regs[i] == reg) + return i; + } + nv_error(priv, "unknow offset nv10_ctx_regs %d\n", reg); + return -1; +} + +static int +nv17_gr_ctx_regs_find_offset(struct nv10_gr_priv *priv, int reg) +{ + int i; + for (i = 0; i < ARRAY_SIZE(nv17_gr_ctx_regs); i++) { + if (nv17_gr_ctx_regs[i] == reg) + return i; + } + nv_error(priv, "unknow 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_priv *priv = nv10_gr_priv(chan); + 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 = nv_rd32(priv, 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 = nv_rd32(priv, NV10_PGRAPH_CTX_USER); + for (i = 0; i < 5; i++) + ctx_switch[i] = nv_rd32(priv, NV10_PGRAPH_CTX_SWITCH(i)); + + /* Save the FIFO state */ + st2 = nv_rd32(priv, NV10_PGRAPH_FFINTFC_ST2); + st2_dl = nv_rd32(priv, NV10_PGRAPH_FFINTFC_ST2_DL); + st2_dh = nv_rd32(priv, NV10_PGRAPH_FFINTFC_ST2_DH); + fifo_ptr = nv_rd32(priv, NV10_PGRAPH_FFINTFC_FIFO_PTR); + + for (i = 0; i < ARRAY_SIZE(fifo); i++) + fifo[i] = nv_rd32(priv, 0x4007a0 + 4 * i); + + /* Switch to the celsius subchannel */ + for (i = 0; i < 5; i++) + nv_wr32(priv, NV10_PGRAPH_CTX_SWITCH(i), + nv_rd32(priv, NV10_PGRAPH_CTX_CACHE(subchan, i))); + nv_mask(priv, NV10_PGRAPH_CTX_USER, 0xe000, subchan << 13); + + /* Inject NV10TCL_DMA_VTXBUF */ + nv_wr32(priv, NV10_PGRAPH_FFINTFC_FIFO_PTR, 0); + nv_wr32(priv, NV10_PGRAPH_FFINTFC_ST2, + 0x2c000000 | chid << 20 | subchan << 16 | 0x18c); + nv_wr32(priv, NV10_PGRAPH_FFINTFC_ST2_DL, inst); + nv_mask(priv, NV10_PGRAPH_CTX_CONTROL, 0, 0x10000); + nv_mask(priv, NV04_PGRAPH_FIFO, 0x00000001, 0x00000001); + nv_mask(priv, NV04_PGRAPH_FIFO, 0x00000001, 0x00000000); + + /* Restore the FIFO state */ + for (i = 0; i < ARRAY_SIZE(fifo); i++) + nv_wr32(priv, 0x4007a0 + 4 * i, fifo[i]); + + nv_wr32(priv, NV10_PGRAPH_FFINTFC_FIFO_PTR, fifo_ptr); + nv_wr32(priv, NV10_PGRAPH_FFINTFC_ST2, st2); + nv_wr32(priv, NV10_PGRAPH_FFINTFC_ST2_DL, st2_dl); + nv_wr32(priv, NV10_PGRAPH_FFINTFC_ST2_DH, st2_dh); + + /* Restore the current ctx object */ + for (i = 0; i < 5; i++) + nv_wr32(priv, NV10_PGRAPH_CTX_SWITCH(i), ctx_switch[i]); + nv_wr32(priv, NV10_PGRAPH_CTX_USER, ctx_user); +} + +static int +nv10_gr_load_context(struct nv10_gr_chan *chan, int chid) +{ + struct nv10_gr_priv *priv = nv10_gr_priv(chan); + u32 inst; + int i; + + for (i = 0; i < ARRAY_SIZE(nv10_gr_ctx_regs); i++) + nv_wr32(priv, nv10_gr_ctx_regs[i], chan->nv10[i]); + + if (nv_device(priv)->card_type >= NV_11 && + nv_device(priv)->chipset >= 0x17) { + for (i = 0; i < ARRAY_SIZE(nv17_gr_ctx_regs); i++) + nv_wr32(priv, nv17_gr_ctx_regs[i], chan->nv17[i]); + } + + nv10_gr_load_pipe(chan); + + inst = nv_rd32(priv, NV10_PGRAPH_GLOBALSTATE1) & 0xffff; + nv10_gr_load_dma_vtxbuf(chan, chid, inst); + + nv_wr32(priv, NV10_PGRAPH_CTX_CONTROL, 0x10010100); + nv_mask(priv, NV10_PGRAPH_CTX_USER, 0xff000000, chid << 24); + nv_mask(priv, NV10_PGRAPH_FFINTFC_ST2, 0x30000000, 0x00000000); + return 0; +} + +static int +nv10_gr_unload_context(struct nv10_gr_chan *chan) +{ + struct nv10_gr_priv *priv = nv10_gr_priv(chan); + int i; + + for (i = 0; i < ARRAY_SIZE(nv10_gr_ctx_regs); i++) + chan->nv10[i] = nv_rd32(priv, nv10_gr_ctx_regs[i]); + + if (nv_device(priv)->card_type >= NV_11 && + nv_device(priv)->chipset >= 0x17) { + for (i = 0; i < ARRAY_SIZE(nv17_gr_ctx_regs); i++) + chan->nv17[i] = nv_rd32(priv, nv17_gr_ctx_regs[i]); + } + + nv10_gr_save_pipe(chan); + + nv_wr32(priv, NV10_PGRAPH_CTX_CONTROL, 0x10000000); + nv_mask(priv, NV10_PGRAPH_CTX_USER, 0xff000000, 0x1f000000); + return 0; +} + +static void +nv10_gr_context_switch(struct nv10_gr_priv *priv) +{ + struct nv10_gr_chan *prev = NULL; + struct nv10_gr_chan *next = NULL; + unsigned long flags; + int chid; + + spin_lock_irqsave(&priv->lock, flags); + nv04_gr_idle(priv); + + /* If previous context is valid, we need to save it */ + prev = nv10_gr_channel(priv); + if (prev) + nv10_gr_unload_context(prev); + + /* load context for next channel */ + chid = (nv_rd32(priv, NV04_PGRAPH_TRAPPED_ADDR) >> 20) & 0x1f; + next = priv->chan[chid]; + if (next) + nv10_gr_load_context(next, chid); + + spin_unlock_irqrestore(&priv->lock, flags); +} + +#define NV_WRITE_CTX(reg, val) do { \ + int offset = nv10_gr_ctx_regs_find_offset(priv, 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(priv, reg); \ + if (offset > 0) \ + chan->nv17[offset] = val; \ + } while (0) + +static int +nv10_gr_context_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_fifo_chan *fifo = (void *)parent; + struct nv10_gr_priv *priv = (void *)engine; + struct nv10_gr_chan *chan; + unsigned long flags; + int ret; + + ret = nvkm_object_create(parent, engine, oclass, 0, &chan); + *pobject = nv_object(chan); + if (ret) + return ret; + + spin_lock_irqsave(&priv->lock, flags); + if (priv->chan[fifo->chid]) { + *pobject = nv_object(priv->chan[fifo->chid]); + atomic_inc(&(*pobject)->refcount); + spin_unlock_irqrestore(&priv->lock, flags); + nvkm_object_destroy(&chan->base); + return 1; + } + + 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 (nv_device(priv)->card_type >= NV_11 && + nv_device(priv)->chipset >= 0x17) { + /* is it really needed ??? */ + NV17_WRITE_CTX(NV10_PGRAPH_DEBUG_4, + nv_rd32(priv, NV10_PGRAPH_DEBUG_4)); + NV17_WRITE_CTX(0x004006b0, nv_rd32(priv, 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); + + priv->chan[fifo->chid] = chan; + chan->chid = fifo->chid; + spin_unlock_irqrestore(&priv->lock, flags); + return 0; +} + +static void +nv10_gr_context_dtor(struct nvkm_object *object) +{ + struct nv10_gr_priv *priv = (void *)object->engine; + struct nv10_gr_chan *chan = (void *)object; + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + priv->chan[chan->chid] = NULL; + spin_unlock_irqrestore(&priv->lock, flags); + + nvkm_object_destroy(&chan->base); +} + +static int +nv10_gr_context_fini(struct nvkm_object *object, bool suspend) +{ + struct nv10_gr_priv *priv = (void *)object->engine; + struct nv10_gr_chan *chan = (void *)object; + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + nv_mask(priv, NV04_PGRAPH_FIFO, 0x00000001, 0x00000000); + if (nv10_gr_channel(priv) == chan) + nv10_gr_unload_context(chan); + nv_mask(priv, NV04_PGRAPH_FIFO, 0x00000001, 0x00000001); + spin_unlock_irqrestore(&priv->lock, flags); + + return nvkm_object_fini(&chan->base, suspend); +} + +static struct nvkm_oclass +nv10_gr_cclass = { + .handle = NV_ENGCTX(GR, 0x10), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv10_gr_context_ctor, + .dtor = nv10_gr_context_dtor, + .init = nvkm_object_init, + .fini = nv10_gr_context_fini, + }, +}; + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +static void +nv10_gr_tile_prog(struct nvkm_engine *engine, int i) +{ + struct nvkm_fb_tile *tile = &nvkm_fb(engine)->tile.region[i]; + struct nvkm_fifo *pfifo = nvkm_fifo(engine); + struct nv10_gr_priv *priv = (void *)engine; + unsigned long flags; + + pfifo->pause(pfifo, &flags); + nv04_gr_idle(priv); + + nv_wr32(priv, NV10_PGRAPH_TLIMIT(i), tile->limit); + nv_wr32(priv, NV10_PGRAPH_TSIZE(i), tile->pitch); + nv_wr32(priv, NV10_PGRAPH_TILE(i), tile->addr); + + pfifo->start(pfifo, &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" }, + {} +}; + +static void +nv10_gr_intr(struct nvkm_subdev *subdev) +{ + struct nv10_gr_priv *priv = (void *)subdev; + struct nv10_gr_chan *chan = NULL; + struct nvkm_namedb *namedb = NULL; + struct nvkm_handle *handle = NULL; + u32 stat = nv_rd32(priv, NV03_PGRAPH_INTR); + u32 nsource = nv_rd32(priv, NV03_PGRAPH_NSOURCE); + u32 nstatus = nv_rd32(priv, NV03_PGRAPH_NSTATUS); + u32 addr = nv_rd32(priv, NV04_PGRAPH_TRAPPED_ADDR); + u32 chid = (addr & 0x01f00000) >> 20; + u32 subc = (addr & 0x00070000) >> 16; + u32 mthd = (addr & 0x00001ffc); + u32 data = nv_rd32(priv, NV04_PGRAPH_TRAPPED_DATA); + u32 class = nv_rd32(priv, 0x400160 + subc * 4) & 0xfff; + u32 show = stat; + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + chan = priv->chan[chid]; + if (chan) + namedb = (void *)nv_pclass(nv_object(chan), NV_NAMEDB_CLASS); + spin_unlock_irqrestore(&priv->lock, flags); + + if (stat & NV_PGRAPH_INTR_ERROR) { + if (chan && (nsource & NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD)) { + handle = nvkm_namedb_get_class(namedb, class); + if (handle && !nv_call(handle->object, mthd, data)) + show &= ~NV_PGRAPH_INTR_ERROR; + } + } + + if (stat & NV_PGRAPH_INTR_CONTEXT_SWITCH) { + nv_wr32(priv, NV03_PGRAPH_INTR, NV_PGRAPH_INTR_CONTEXT_SWITCH); + stat &= ~NV_PGRAPH_INTR_CONTEXT_SWITCH; + show &= ~NV_PGRAPH_INTR_CONTEXT_SWITCH; + nv10_gr_context_switch(priv); + } + + nv_wr32(priv, NV03_PGRAPH_INTR, stat); + nv_wr32(priv, NV04_PGRAPH_FIFO, 0x00000001); + + if (show) { + nv_error(priv, "%s", ""); + nvkm_bitfield_print(nv10_gr_intr_name, show); + pr_cont(" nsource:"); + nvkm_bitfield_print(nv04_gr_nsource, nsource); + pr_cont(" nstatus:"); + nvkm_bitfield_print(nv10_gr_nstatus, nstatus); + pr_cont("\n"); + nv_error(priv, + "ch %d [%s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n", + chid, nvkm_client_name(chan), subc, class, mthd, + data); + } + + nvkm_namedb_put(handle); +} + +static int +nv10_gr_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv10_gr_priv *priv; + int ret; + + ret = nvkm_gr_create(parent, engine, oclass, true, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00001000; + nv_subdev(priv)->intr = nv10_gr_intr; + nv_engine(priv)->cclass = &nv10_gr_cclass; + + if (nv_device(priv)->chipset <= 0x10) + nv_engine(priv)->sclass = nv10_gr_sclass; + else + if (nv_device(priv)->chipset < 0x17 || + nv_device(priv)->card_type < NV_11) + nv_engine(priv)->sclass = nv15_gr_sclass; + else + nv_engine(priv)->sclass = nv17_gr_sclass; + + nv_engine(priv)->tile_prog = nv10_gr_tile_prog; + spin_lock_init(&priv->lock); + return 0; +} + +static void +nv10_gr_dtor(struct nvkm_object *object) +{ + struct nv10_gr_priv *priv = (void *)object; + nvkm_gr_destroy(&priv->base); +} + +static int +nv10_gr_init(struct nvkm_object *object) +{ + struct nvkm_engine *engine = nv_engine(object); + struct nvkm_fb *pfb = nvkm_fb(object); + struct nv10_gr_priv *priv = (void *)engine; + int ret, i; + + ret = nvkm_gr_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, NV03_PGRAPH_INTR , 0xFFFFFFFF); + nv_wr32(priv, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF); + + nv_wr32(priv, NV04_PGRAPH_DEBUG_0, 0xFFFFFFFF); + nv_wr32(priv, NV04_PGRAPH_DEBUG_0, 0x00000000); + nv_wr32(priv, NV04_PGRAPH_DEBUG_1, 0x00118700); + /* nv_wr32(priv, NV04_PGRAPH_DEBUG_2, 0x24E00810); */ /* 0x25f92ad9 */ + nv_wr32(priv, NV04_PGRAPH_DEBUG_2, 0x25f92ad9); + nv_wr32(priv, NV04_PGRAPH_DEBUG_3, 0x55DE0830 | (1 << 29) | (1 << 31)); + + if (nv_device(priv)->card_type >= NV_11 && + nv_device(priv)->chipset >= 0x17) { + nv_wr32(priv, NV10_PGRAPH_DEBUG_4, 0x1f000000); + nv_wr32(priv, 0x400a10, 0x03ff3fb6); + nv_wr32(priv, 0x400838, 0x002f8684); + nv_wr32(priv, 0x40083c, 0x00115f3f); + nv_wr32(priv, 0x4006b0, 0x40000020); + } else { + nv_wr32(priv, NV10_PGRAPH_DEBUG_4, 0x00000000); + } + + /* Turn all the tiling regions off. */ + for (i = 0; i < pfb->tile.regions; i++) + engine->tile_prog(engine, i); + + nv_wr32(priv, NV10_PGRAPH_CTX_SWITCH(0), 0x00000000); + nv_wr32(priv, NV10_PGRAPH_CTX_SWITCH(1), 0x00000000); + nv_wr32(priv, NV10_PGRAPH_CTX_SWITCH(2), 0x00000000); + nv_wr32(priv, NV10_PGRAPH_CTX_SWITCH(3), 0x00000000); + nv_wr32(priv, NV10_PGRAPH_CTX_SWITCH(4), 0x00000000); + nv_wr32(priv, NV10_PGRAPH_STATE, 0xFFFFFFFF); + + nv_mask(priv, NV10_PGRAPH_CTX_USER, 0xff000000, 0x1f000000); + nv_wr32(priv, NV10_PGRAPH_CTX_CONTROL, 0x10000100); + nv_wr32(priv, NV10_PGRAPH_FFINTFC_ST2, 0x08000000); + return 0; +} + +static int +nv10_gr_fini(struct nvkm_object *object, bool suspend) +{ + struct nv10_gr_priv *priv = (void *)object; + return nvkm_gr_fini(&priv->base, suspend); +} + +struct nvkm_oclass +nv10_gr_oclass = { + .handle = NV_ENGINE(GR, 0x10), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv10_gr_ctor, + .dtor = nv10_gr_dtor, + .init = nv10_gr_init, + .fini = nv10_gr_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv20.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv20.c new file mode 100644 index 000000000..1713ffb66 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv20.c @@ -0,0 +1,376 @@ +#include "nv20.h" +#include "regs.h" + +#include +#include +#include +#include +#include +#include + +/******************************************************************************* + * Graphics object classes + ******************************************************************************/ + +static struct nvkm_oclass +nv20_gr_sclass[] = { + { 0x0012, &nv04_gr_ofuncs, NULL }, /* beta1 */ + { 0x0019, &nv04_gr_ofuncs, NULL }, /* clip */ + { 0x0030, &nv04_gr_ofuncs, NULL }, /* null */ + { 0x0039, &nv04_gr_ofuncs, NULL }, /* m2mf */ + { 0x0043, &nv04_gr_ofuncs, NULL }, /* rop */ + { 0x0044, &nv04_gr_ofuncs, NULL }, /* patt */ + { 0x004a, &nv04_gr_ofuncs, NULL }, /* gdi */ + { 0x0062, &nv04_gr_ofuncs, NULL }, /* surf2d */ + { 0x0072, &nv04_gr_ofuncs, NULL }, /* beta4 */ + { 0x0089, &nv04_gr_ofuncs, NULL }, /* sifm */ + { 0x008a, &nv04_gr_ofuncs, NULL }, /* ifc */ + { 0x0096, &nv04_gr_ofuncs, NULL }, /* celcius */ + { 0x0097, &nv04_gr_ofuncs, NULL }, /* kelvin */ + { 0x009e, &nv04_gr_ofuncs, NULL }, /* swzsurf */ + { 0x009f, &nv04_gr_ofuncs, NULL }, /* imageblit */ + {}, +}; + +/******************************************************************************* + * PGRAPH context + ******************************************************************************/ + +static int +nv20_gr_context_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv20_gr_chan *chan; + int ret, i; + + ret = nvkm_gr_context_create(parent, engine, oclass, NULL, 0x37f0, + 16, NVOBJ_FLAG_ZERO_ALLOC, &chan); + *pobject = nv_object(chan); + if (ret) + return ret; + + chan->chid = nvkm_fifo_chan(parent)->chid; + + nv_wo32(chan, 0x0000, 0x00000001 | (chan->chid << 24)); + nv_wo32(chan, 0x033c, 0xffff0000); + nv_wo32(chan, 0x03a0, 0x0fff0000); + nv_wo32(chan, 0x03a4, 0x0fff0000); + nv_wo32(chan, 0x047c, 0x00000101); + nv_wo32(chan, 0x0490, 0x00000111); + nv_wo32(chan, 0x04a8, 0x44400000); + for (i = 0x04d4; i <= 0x04e0; i += 4) + nv_wo32(chan, i, 0x00030303); + for (i = 0x04f4; i <= 0x0500; i += 4) + nv_wo32(chan, i, 0x00080000); + for (i = 0x050c; i <= 0x0518; i += 4) + nv_wo32(chan, i, 0x01012000); + for (i = 0x051c; i <= 0x0528; i += 4) + nv_wo32(chan, i, 0x000105b8); + for (i = 0x052c; i <= 0x0538; i += 4) + nv_wo32(chan, i, 0x00080008); + for (i = 0x055c; i <= 0x0598; i += 4) + nv_wo32(chan, i, 0x07ff0000); + nv_wo32(chan, 0x05a4, 0x4b7fffff); + nv_wo32(chan, 0x05fc, 0x00000001); + nv_wo32(chan, 0x0604, 0x00004000); + nv_wo32(chan, 0x0610, 0x00000001); + nv_wo32(chan, 0x0618, 0x00040000); + nv_wo32(chan, 0x061c, 0x00010000); + for (i = 0x1c1c; i <= 0x248c; i += 16) { + nv_wo32(chan, (i + 0), 0x10700ff9); + nv_wo32(chan, (i + 4), 0x0436086c); + nv_wo32(chan, (i + 8), 0x000c001b); + } + nv_wo32(chan, 0x281c, 0x3f800000); + nv_wo32(chan, 0x2830, 0x3f800000); + nv_wo32(chan, 0x285c, 0x40000000); + nv_wo32(chan, 0x2860, 0x3f800000); + nv_wo32(chan, 0x2864, 0x3f000000); + nv_wo32(chan, 0x286c, 0x40000000); + nv_wo32(chan, 0x2870, 0x3f800000); + nv_wo32(chan, 0x2878, 0xbf800000); + nv_wo32(chan, 0x2880, 0xbf800000); + nv_wo32(chan, 0x34a4, 0x000fe000); + nv_wo32(chan, 0x3530, 0x000003f8); + nv_wo32(chan, 0x3540, 0x002fe000); + for (i = 0x355c; i <= 0x3578; i += 4) + nv_wo32(chan, i, 0x001c527c); + return 0; +} + +int +nv20_gr_context_init(struct nvkm_object *object) +{ + struct nv20_gr_priv *priv = (void *)object->engine; + struct nv20_gr_chan *chan = (void *)object; + int ret; + + ret = nvkm_gr_context_init(&chan->base); + if (ret) + return ret; + + nv_wo32(priv->ctxtab, chan->chid * 4, nv_gpuobj(chan)->addr >> 4); + return 0; +} + +int +nv20_gr_context_fini(struct nvkm_object *object, bool suspend) +{ + struct nv20_gr_priv *priv = (void *)object->engine; + struct nv20_gr_chan *chan = (void *)object; + int chid = -1; + + nv_mask(priv, 0x400720, 0x00000001, 0x00000000); + if (nv_rd32(priv, 0x400144) & 0x00010000) + chid = (nv_rd32(priv, 0x400148) & 0x1f000000) >> 24; + if (chan->chid == chid) { + nv_wr32(priv, 0x400784, nv_gpuobj(chan)->addr >> 4); + nv_wr32(priv, 0x400788, 0x00000002); + nv_wait(priv, 0x400700, 0xffffffff, 0x00000000); + nv_wr32(priv, 0x400144, 0x10000000); + nv_mask(priv, 0x400148, 0xff000000, 0x1f000000); + } + nv_mask(priv, 0x400720, 0x00000001, 0x00000001); + + nv_wo32(priv->ctxtab, chan->chid * 4, 0x00000000); + return nvkm_gr_context_fini(&chan->base, suspend); +} + +static struct nvkm_oclass +nv20_gr_cclass = { + .handle = NV_ENGCTX(GR, 0x20), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv20_gr_context_ctor, + .dtor = _nvkm_gr_context_dtor, + .init = nv20_gr_context_init, + .fini = nv20_gr_context_fini, + .rd32 = _nvkm_gr_context_rd32, + .wr32 = _nvkm_gr_context_wr32, + }, +}; + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +void +nv20_gr_tile_prog(struct nvkm_engine *engine, int i) +{ + struct nvkm_fb_tile *tile = &nvkm_fb(engine)->tile.region[i]; + struct nvkm_fifo *pfifo = nvkm_fifo(engine); + struct nv20_gr_priv *priv = (void *)engine; + unsigned long flags; + + pfifo->pause(pfifo, &flags); + nv04_gr_idle(priv); + + nv_wr32(priv, NV20_PGRAPH_TLIMIT(i), tile->limit); + nv_wr32(priv, NV20_PGRAPH_TSIZE(i), tile->pitch); + nv_wr32(priv, NV20_PGRAPH_TILE(i), tile->addr); + + nv_wr32(priv, NV10_PGRAPH_RDI_INDEX, 0x00EA0030 + 4 * i); + nv_wr32(priv, NV10_PGRAPH_RDI_DATA, tile->limit); + nv_wr32(priv, NV10_PGRAPH_RDI_INDEX, 0x00EA0050 + 4 * i); + nv_wr32(priv, NV10_PGRAPH_RDI_DATA, tile->pitch); + nv_wr32(priv, NV10_PGRAPH_RDI_INDEX, 0x00EA0010 + 4 * i); + nv_wr32(priv, NV10_PGRAPH_RDI_DATA, tile->addr); + + if (nv_device(engine)->chipset != 0x34) { + nv_wr32(priv, NV20_PGRAPH_ZCOMP(i), tile->zcomp); + nv_wr32(priv, NV10_PGRAPH_RDI_INDEX, 0x00ea0090 + 4 * i); + nv_wr32(priv, NV10_PGRAPH_RDI_DATA, tile->zcomp); + } + + pfifo->start(pfifo, &flags); +} + +void +nv20_gr_intr(struct nvkm_subdev *subdev) +{ + struct nvkm_engine *engine = nv_engine(subdev); + struct nvkm_object *engctx; + struct nvkm_handle *handle; + struct nv20_gr_priv *priv = (void *)subdev; + u32 stat = nv_rd32(priv, NV03_PGRAPH_INTR); + u32 nsource = nv_rd32(priv, NV03_PGRAPH_NSOURCE); + u32 nstatus = nv_rd32(priv, NV03_PGRAPH_NSTATUS); + u32 addr = nv_rd32(priv, NV04_PGRAPH_TRAPPED_ADDR); + u32 chid = (addr & 0x01f00000) >> 20; + u32 subc = (addr & 0x00070000) >> 16; + u32 mthd = (addr & 0x00001ffc); + u32 data = nv_rd32(priv, NV04_PGRAPH_TRAPPED_DATA); + u32 class = nv_rd32(priv, 0x400160 + subc * 4) & 0xfff; + u32 show = stat; + + engctx = nvkm_engctx_get(engine, chid); + if (stat & NV_PGRAPH_INTR_ERROR) { + if (nsource & NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD) { + handle = nvkm_handle_get_class(engctx, class); + if (handle && !nv_call(handle->object, mthd, data)) + show &= ~NV_PGRAPH_INTR_ERROR; + nvkm_handle_put(handle); + } + } + + nv_wr32(priv, NV03_PGRAPH_INTR, stat); + nv_wr32(priv, NV04_PGRAPH_FIFO, 0x00000001); + + if (show) { + nv_error(priv, "%s", ""); + nvkm_bitfield_print(nv10_gr_intr_name, show); + pr_cont(" nsource:"); + nvkm_bitfield_print(nv04_gr_nsource, nsource); + pr_cont(" nstatus:"); + nvkm_bitfield_print(nv10_gr_nstatus, nstatus); + pr_cont("\n"); + nv_error(priv, + "ch %d [%s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n", + chid, nvkm_client_name(engctx), subc, class, mthd, + data); + } + + nvkm_engctx_put(engctx); +} + +static int +nv20_gr_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv20_gr_priv *priv; + int ret; + + ret = nvkm_gr_create(parent, engine, oclass, true, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(nv_object(priv), NULL, 32 * 4, 16, + NVOBJ_FLAG_ZERO_ALLOC, &priv->ctxtab); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00001000; + nv_subdev(priv)->intr = nv20_gr_intr; + nv_engine(priv)->cclass = &nv20_gr_cclass; + nv_engine(priv)->sclass = nv20_gr_sclass; + nv_engine(priv)->tile_prog = nv20_gr_tile_prog; + return 0; +} + +void +nv20_gr_dtor(struct nvkm_object *object) +{ + struct nv20_gr_priv *priv = (void *)object; + nvkm_gpuobj_ref(NULL, &priv->ctxtab); + nvkm_gr_destroy(&priv->base); +} + +int +nv20_gr_init(struct nvkm_object *object) +{ + struct nvkm_engine *engine = nv_engine(object); + struct nv20_gr_priv *priv = (void *)engine; + struct nvkm_fb *pfb = nvkm_fb(object); + u32 tmp, vramsz; + int ret, i; + + ret = nvkm_gr_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, NV20_PGRAPH_CHANNEL_CTX_TABLE, priv->ctxtab->addr >> 4); + + if (nv_device(priv)->chipset == 0x20) { + nv_wr32(priv, NV10_PGRAPH_RDI_INDEX, 0x003d0000); + for (i = 0; i < 15; i++) + nv_wr32(priv, NV10_PGRAPH_RDI_DATA, 0x00000000); + nv_wait(priv, 0x400700, 0xffffffff, 0x00000000); + } else { + nv_wr32(priv, NV10_PGRAPH_RDI_INDEX, 0x02c80000); + for (i = 0; i < 32; i++) + nv_wr32(priv, NV10_PGRAPH_RDI_DATA, 0x00000000); + nv_wait(priv, 0x400700, 0xffffffff, 0x00000000); + } + + nv_wr32(priv, NV03_PGRAPH_INTR , 0xFFFFFFFF); + nv_wr32(priv, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF); + + nv_wr32(priv, NV04_PGRAPH_DEBUG_0, 0xFFFFFFFF); + nv_wr32(priv, NV04_PGRAPH_DEBUG_0, 0x00000000); + nv_wr32(priv, NV04_PGRAPH_DEBUG_1, 0x00118700); + nv_wr32(priv, NV04_PGRAPH_DEBUG_3, 0xF3CE0475); /* 0x4 = auto ctx switch */ + nv_wr32(priv, NV10_PGRAPH_DEBUG_4, 0x00000000); + nv_wr32(priv, 0x40009C , 0x00000040); + + if (nv_device(priv)->chipset >= 0x25) { + nv_wr32(priv, 0x400890, 0x00a8cfff); + nv_wr32(priv, 0x400610, 0x304B1FB6); + nv_wr32(priv, 0x400B80, 0x1cbd3883); + nv_wr32(priv, 0x400B84, 0x44000000); + nv_wr32(priv, 0x400098, 0x40000080); + nv_wr32(priv, 0x400B88, 0x000000ff); + + } else { + nv_wr32(priv, 0x400880, 0x0008c7df); + nv_wr32(priv, 0x400094, 0x00000005); + nv_wr32(priv, 0x400B80, 0x45eae20e); + nv_wr32(priv, 0x400B84, 0x24000000); + nv_wr32(priv, 0x400098, 0x00000040); + nv_wr32(priv, NV10_PGRAPH_RDI_INDEX, 0x00E00038); + nv_wr32(priv, NV10_PGRAPH_RDI_DATA , 0x00000030); + nv_wr32(priv, NV10_PGRAPH_RDI_INDEX, 0x00E10038); + nv_wr32(priv, NV10_PGRAPH_RDI_DATA , 0x00000030); + } + + /* Turn all the tiling regions off. */ + for (i = 0; i < pfb->tile.regions; i++) + engine->tile_prog(engine, i); + + nv_wr32(priv, 0x4009a0, nv_rd32(priv, 0x100324)); + nv_wr32(priv, NV10_PGRAPH_RDI_INDEX, 0x00EA000C); + nv_wr32(priv, NV10_PGRAPH_RDI_DATA, nv_rd32(priv, 0x100324)); + + nv_wr32(priv, NV10_PGRAPH_CTX_CONTROL, 0x10000100); + nv_wr32(priv, NV10_PGRAPH_STATE , 0xFFFFFFFF); + + tmp = nv_rd32(priv, NV10_PGRAPH_SURFACE) & 0x0007ff00; + nv_wr32(priv, NV10_PGRAPH_SURFACE, tmp); + tmp = nv_rd32(priv, NV10_PGRAPH_SURFACE) | 0x00020100; + nv_wr32(priv, NV10_PGRAPH_SURFACE, tmp); + + /* begin RAM config */ + vramsz = nv_device_resource_len(nv_device(priv), 0) - 1; + nv_wr32(priv, 0x4009A4, nv_rd32(priv, 0x100200)); + nv_wr32(priv, 0x4009A8, nv_rd32(priv, 0x100204)); + nv_wr32(priv, NV10_PGRAPH_RDI_INDEX, 0x00EA0000); + nv_wr32(priv, NV10_PGRAPH_RDI_DATA , nv_rd32(priv, 0x100200)); + nv_wr32(priv, NV10_PGRAPH_RDI_INDEX, 0x00EA0004); + nv_wr32(priv, NV10_PGRAPH_RDI_DATA , nv_rd32(priv, 0x100204)); + nv_wr32(priv, 0x400820, 0); + nv_wr32(priv, 0x400824, 0); + nv_wr32(priv, 0x400864, vramsz - 1); + nv_wr32(priv, 0x400868, vramsz - 1); + + /* interesting.. the below overwrites some of the tile setup above.. */ + nv_wr32(priv, 0x400B20, 0x00000000); + nv_wr32(priv, 0x400B04, 0xFFFFFFFF); + + nv_wr32(priv, NV03_PGRAPH_ABS_UCLIP_XMIN, 0); + nv_wr32(priv, NV03_PGRAPH_ABS_UCLIP_YMIN, 0); + nv_wr32(priv, NV03_PGRAPH_ABS_UCLIP_XMAX, 0x7fff); + nv_wr32(priv, NV03_PGRAPH_ABS_UCLIP_YMAX, 0x7fff); + return 0; +} + +struct nvkm_oclass +nv20_gr_oclass = { + .handle = NV_ENGINE(GR, 0x20), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv20_gr_ctor, + .dtor = nv20_gr_dtor, + .init = nv20_gr_init, + .fini = _nvkm_gr_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv20.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv20.h new file mode 100644 index 000000000..ac4dc048f --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv20.h @@ -0,0 +1,26 @@ +#ifndef __NV20_GR_H__ +#define __NV20_GR_H__ +#include + +struct nv20_gr_priv { + struct nvkm_gr base; + struct nvkm_gpuobj *ctxtab; +}; + +struct nv20_gr_chan { + struct nvkm_gr_chan base; + int chid; +}; + +extern struct nvkm_oclass nv25_gr_sclass[]; +int nv20_gr_context_init(struct nvkm_object *); +int nv20_gr_context_fini(struct nvkm_object *, bool); + +void nv20_gr_tile_prog(struct nvkm_engine *, int); +void nv20_gr_intr(struct nvkm_subdev *); + +void nv20_gr_dtor(struct nvkm_object *); +int nv20_gr_init(struct nvkm_object *); + +int nv30_gr_init(struct nvkm_object *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv25.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv25.c new file mode 100644 index 000000000..bc362519c --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv25.c @@ -0,0 +1,158 @@ +#include "nv20.h" +#include "regs.h" + +#include + +/******************************************************************************* + * Graphics object classes + ******************************************************************************/ + +struct nvkm_oclass +nv25_gr_sclass[] = { + { 0x0012, &nv04_gr_ofuncs, NULL }, /* beta1 */ + { 0x0019, &nv04_gr_ofuncs, NULL }, /* clip */ + { 0x0030, &nv04_gr_ofuncs, NULL }, /* null */ + { 0x0039, &nv04_gr_ofuncs, NULL }, /* m2mf */ + { 0x0043, &nv04_gr_ofuncs, NULL }, /* rop */ + { 0x0044, &nv04_gr_ofuncs, NULL }, /* patt */ + { 0x004a, &nv04_gr_ofuncs, NULL }, /* gdi */ + { 0x0062, &nv04_gr_ofuncs, NULL }, /* surf2d */ + { 0x0072, &nv04_gr_ofuncs, NULL }, /* beta4 */ + { 0x0089, &nv04_gr_ofuncs, NULL }, /* sifm */ + { 0x008a, &nv04_gr_ofuncs, NULL }, /* ifc */ + { 0x0096, &nv04_gr_ofuncs, NULL }, /* celcius */ + { 0x009e, &nv04_gr_ofuncs, NULL }, /* swzsurf */ + { 0x009f, &nv04_gr_ofuncs, NULL }, /* imageblit */ + { 0x0597, &nv04_gr_ofuncs, NULL }, /* kelvin */ + {}, +}; + +/******************************************************************************* + * PGRAPH context + ******************************************************************************/ + +static int +nv25_gr_context_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv20_gr_chan *chan; + int ret, i; + + ret = nvkm_gr_context_create(parent, engine, oclass, NULL, 0x3724, + 16, NVOBJ_FLAG_ZERO_ALLOC, &chan); + *pobject = nv_object(chan); + if (ret) + return ret; + + chan->chid = nvkm_fifo_chan(parent)->chid; + + nv_wo32(chan, 0x0028, 0x00000001 | (chan->chid << 24)); + nv_wo32(chan, 0x035c, 0xffff0000); + nv_wo32(chan, 0x03c0, 0x0fff0000); + nv_wo32(chan, 0x03c4, 0x0fff0000); + nv_wo32(chan, 0x049c, 0x00000101); + nv_wo32(chan, 0x04b0, 0x00000111); + nv_wo32(chan, 0x04c8, 0x00000080); + nv_wo32(chan, 0x04cc, 0xffff0000); + nv_wo32(chan, 0x04d0, 0x00000001); + nv_wo32(chan, 0x04e4, 0x44400000); + nv_wo32(chan, 0x04fc, 0x4b800000); + for (i = 0x0510; i <= 0x051c; i += 4) + nv_wo32(chan, i, 0x00030303); + for (i = 0x0530; i <= 0x053c; i += 4) + nv_wo32(chan, i, 0x00080000); + for (i = 0x0548; i <= 0x0554; i += 4) + nv_wo32(chan, i, 0x01012000); + for (i = 0x0558; i <= 0x0564; i += 4) + nv_wo32(chan, i, 0x000105b8); + for (i = 0x0568; i <= 0x0574; i += 4) + nv_wo32(chan, i, 0x00080008); + for (i = 0x0598; i <= 0x05d4; i += 4) + nv_wo32(chan, i, 0x07ff0000); + nv_wo32(chan, 0x05e0, 0x4b7fffff); + nv_wo32(chan, 0x0620, 0x00000080); + nv_wo32(chan, 0x0624, 0x30201000); + nv_wo32(chan, 0x0628, 0x70605040); + nv_wo32(chan, 0x062c, 0xb0a09080); + nv_wo32(chan, 0x0630, 0xf0e0d0c0); + nv_wo32(chan, 0x0664, 0x00000001); + nv_wo32(chan, 0x066c, 0x00004000); + nv_wo32(chan, 0x0678, 0x00000001); + nv_wo32(chan, 0x0680, 0x00040000); + nv_wo32(chan, 0x0684, 0x00010000); + for (i = 0x1b04; i <= 0x2374; i += 16) { + nv_wo32(chan, (i + 0), 0x10700ff9); + nv_wo32(chan, (i + 4), 0x0436086c); + nv_wo32(chan, (i + 8), 0x000c001b); + } + nv_wo32(chan, 0x2704, 0x3f800000); + nv_wo32(chan, 0x2718, 0x3f800000); + nv_wo32(chan, 0x2744, 0x40000000); + nv_wo32(chan, 0x2748, 0x3f800000); + nv_wo32(chan, 0x274c, 0x3f000000); + nv_wo32(chan, 0x2754, 0x40000000); + nv_wo32(chan, 0x2758, 0x3f800000); + nv_wo32(chan, 0x2760, 0xbf800000); + nv_wo32(chan, 0x2768, 0xbf800000); + nv_wo32(chan, 0x308c, 0x000fe000); + nv_wo32(chan, 0x3108, 0x000003f8); + nv_wo32(chan, 0x3468, 0x002fe000); + for (i = 0x3484; i <= 0x34a0; i += 4) + nv_wo32(chan, i, 0x001c527c); + return 0; +} + +static struct nvkm_oclass +nv25_gr_cclass = { + .handle = NV_ENGCTX(GR, 0x25), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv25_gr_context_ctor, + .dtor = _nvkm_gr_context_dtor, + .init = nv20_gr_context_init, + .fini = nv20_gr_context_fini, + .rd32 = _nvkm_gr_context_rd32, + .wr32 = _nvkm_gr_context_wr32, + }, +}; + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +static int +nv25_gr_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv20_gr_priv *priv; + int ret; + + ret = nvkm_gr_create(parent, engine, oclass, true, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(nv_object(priv), NULL, 32 * 4, 16, + NVOBJ_FLAG_ZERO_ALLOC, &priv->ctxtab); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00001000; + nv_subdev(priv)->intr = nv20_gr_intr; + nv_engine(priv)->cclass = &nv25_gr_cclass; + nv_engine(priv)->sclass = nv25_gr_sclass; + nv_engine(priv)->tile_prog = nv20_gr_tile_prog; + return 0; +} + +struct nvkm_oclass +nv25_gr_oclass = { + .handle = NV_ENGINE(GR, 0x25), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv25_gr_ctor, + .dtor = nv20_gr_dtor, + .init = nv20_gr_init, + .fini = _nvkm_gr_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv2a.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv2a.c new file mode 100644 index 000000000..22a5096e2 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv2a.c @@ -0,0 +1,125 @@ +#include "nv20.h" +#include "regs.h" + +#include + +/******************************************************************************* + * PGRAPH context + ******************************************************************************/ + +static int +nv2a_gr_context_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv20_gr_chan *chan; + int ret, i; + + ret = nvkm_gr_context_create(parent, engine, oclass, NULL, 0x36b0, + 16, NVOBJ_FLAG_ZERO_ALLOC, &chan); + *pobject = nv_object(chan); + if (ret) + return ret; + + chan->chid = nvkm_fifo_chan(parent)->chid; + + nv_wo32(chan, 0x0000, 0x00000001 | (chan->chid << 24)); + nv_wo32(chan, 0x033c, 0xffff0000); + nv_wo32(chan, 0x03a0, 0x0fff0000); + nv_wo32(chan, 0x03a4, 0x0fff0000); + nv_wo32(chan, 0x047c, 0x00000101); + nv_wo32(chan, 0x0490, 0x00000111); + nv_wo32(chan, 0x04a8, 0x44400000); + for (i = 0x04d4; i <= 0x04e0; i += 4) + nv_wo32(chan, i, 0x00030303); + for (i = 0x04f4; i <= 0x0500; i += 4) + nv_wo32(chan, i, 0x00080000); + for (i = 0x050c; i <= 0x0518; i += 4) + nv_wo32(chan, i, 0x01012000); + for (i = 0x051c; i <= 0x0528; i += 4) + nv_wo32(chan, i, 0x000105b8); + for (i = 0x052c; i <= 0x0538; i += 4) + nv_wo32(chan, i, 0x00080008); + for (i = 0x055c; i <= 0x0598; i += 4) + nv_wo32(chan, i, 0x07ff0000); + nv_wo32(chan, 0x05a4, 0x4b7fffff); + nv_wo32(chan, 0x05fc, 0x00000001); + nv_wo32(chan, 0x0604, 0x00004000); + nv_wo32(chan, 0x0610, 0x00000001); + nv_wo32(chan, 0x0618, 0x00040000); + nv_wo32(chan, 0x061c, 0x00010000); + for (i = 0x1a9c; i <= 0x22fc; i += 16) { /*XXX: check!! */ + nv_wo32(chan, (i + 0), 0x10700ff9); + nv_wo32(chan, (i + 4), 0x0436086c); + nv_wo32(chan, (i + 8), 0x000c001b); + } + nv_wo32(chan, 0x269c, 0x3f800000); + nv_wo32(chan, 0x26b0, 0x3f800000); + nv_wo32(chan, 0x26dc, 0x40000000); + nv_wo32(chan, 0x26e0, 0x3f800000); + nv_wo32(chan, 0x26e4, 0x3f000000); + nv_wo32(chan, 0x26ec, 0x40000000); + nv_wo32(chan, 0x26f0, 0x3f800000); + nv_wo32(chan, 0x26f8, 0xbf800000); + nv_wo32(chan, 0x2700, 0xbf800000); + nv_wo32(chan, 0x3024, 0x000fe000); + nv_wo32(chan, 0x30a0, 0x000003f8); + nv_wo32(chan, 0x33fc, 0x002fe000); + for (i = 0x341c; i <= 0x3438; i += 4) + nv_wo32(chan, i, 0x001c527c); + return 0; +} + +static struct nvkm_oclass +nv2a_gr_cclass = { + .handle = NV_ENGCTX(GR, 0x2a), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv2a_gr_context_ctor, + .dtor = _nvkm_gr_context_dtor, + .init = nv20_gr_context_init, + .fini = nv20_gr_context_fini, + .rd32 = _nvkm_gr_context_rd32, + .wr32 = _nvkm_gr_context_wr32, + }, +}; + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +static int +nv2a_gr_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv20_gr_priv *priv; + int ret; + + ret = nvkm_gr_create(parent, engine, oclass, true, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(nv_object(priv), NULL, 32 * 4, 16, + NVOBJ_FLAG_ZERO_ALLOC, &priv->ctxtab); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00001000; + nv_subdev(priv)->intr = nv20_gr_intr; + nv_engine(priv)->cclass = &nv2a_gr_cclass; + nv_engine(priv)->sclass = nv25_gr_sclass; + nv_engine(priv)->tile_prog = nv20_gr_tile_prog; + return 0; +} + +struct nvkm_oclass +nv2a_gr_oclass = { + .handle = NV_ENGINE(GR, 0x2a), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv2a_gr_ctor, + .dtor = nv20_gr_dtor, + .init = nv20_gr_init, + .fini = _nvkm_gr_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv30.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv30.c new file mode 100644 index 000000000..dcc84eb54 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv30.c @@ -0,0 +1,231 @@ +#include "nv20.h" +#include "regs.h" + +#include +#include +#include + +/******************************************************************************* + * Graphics object classes + ******************************************************************************/ + +static struct nvkm_oclass +nv30_gr_sclass[] = { + { 0x0012, &nv04_gr_ofuncs, NULL }, /* beta1 */ + { 0x0019, &nv04_gr_ofuncs, NULL }, /* clip */ + { 0x0030, &nv04_gr_ofuncs, NULL }, /* null */ + { 0x0039, &nv04_gr_ofuncs, NULL }, /* m2mf */ + { 0x0043, &nv04_gr_ofuncs, NULL }, /* rop */ + { 0x0044, &nv04_gr_ofuncs, NULL }, /* patt */ + { 0x004a, &nv04_gr_ofuncs, NULL }, /* gdi */ + { 0x0062, &nv04_gr_ofuncs, NULL }, /* surf2d */ + { 0x0072, &nv04_gr_ofuncs, NULL }, /* beta4 */ + { 0x0089, &nv04_gr_ofuncs, NULL }, /* sifm */ + { 0x008a, &nv04_gr_ofuncs, NULL }, /* ifc */ + { 0x009f, &nv04_gr_ofuncs, NULL }, /* imageblit */ + { 0x0362, &nv04_gr_ofuncs, NULL }, /* surf2d (nv30) */ + { 0x0389, &nv04_gr_ofuncs, NULL }, /* sifm (nv30) */ + { 0x038a, &nv04_gr_ofuncs, NULL }, /* ifc (nv30) */ + { 0x039e, &nv04_gr_ofuncs, NULL }, /* swzsurf (nv30) */ + { 0x0397, &nv04_gr_ofuncs, NULL }, /* rankine */ + {}, +}; + +/******************************************************************************* + * PGRAPH context + ******************************************************************************/ + +static int +nv30_gr_context_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv20_gr_chan *chan; + int ret, i; + + ret = nvkm_gr_context_create(parent, engine, oclass, NULL, 0x5f48, + 16, NVOBJ_FLAG_ZERO_ALLOC, &chan); + *pobject = nv_object(chan); + if (ret) + return ret; + + chan->chid = nvkm_fifo_chan(parent)->chid; + + nv_wo32(chan, 0x0028, 0x00000001 | (chan->chid << 24)); + nv_wo32(chan, 0x0410, 0x00000101); + nv_wo32(chan, 0x0424, 0x00000111); + nv_wo32(chan, 0x0428, 0x00000060); + nv_wo32(chan, 0x0444, 0x00000080); + nv_wo32(chan, 0x0448, 0xffff0000); + nv_wo32(chan, 0x044c, 0x00000001); + nv_wo32(chan, 0x0460, 0x44400000); + nv_wo32(chan, 0x048c, 0xffff0000); + for (i = 0x04e0; i < 0x04e8; i += 4) + nv_wo32(chan, i, 0x0fff0000); + nv_wo32(chan, 0x04ec, 0x00011100); + for (i = 0x0508; i < 0x0548; i += 4) + nv_wo32(chan, i, 0x07ff0000); + nv_wo32(chan, 0x0550, 0x4b7fffff); + nv_wo32(chan, 0x058c, 0x00000080); + nv_wo32(chan, 0x0590, 0x30201000); + nv_wo32(chan, 0x0594, 0x70605040); + nv_wo32(chan, 0x0598, 0xb8a89888); + nv_wo32(chan, 0x059c, 0xf8e8d8c8); + nv_wo32(chan, 0x05b0, 0xb0000000); + for (i = 0x0600; i < 0x0640; i += 4) + nv_wo32(chan, i, 0x00010588); + for (i = 0x0640; i < 0x0680; i += 4) + nv_wo32(chan, i, 0x00030303); + for (i = 0x06c0; i < 0x0700; i += 4) + nv_wo32(chan, i, 0x0008aae4); + for (i = 0x0700; i < 0x0740; i += 4) + nv_wo32(chan, i, 0x01012000); + for (i = 0x0740; i < 0x0780; i += 4) + nv_wo32(chan, i, 0x00080008); + nv_wo32(chan, 0x085c, 0x00040000); + nv_wo32(chan, 0x0860, 0x00010000); + for (i = 0x0864; i < 0x0874; i += 4) + nv_wo32(chan, i, 0x00040004); + for (i = 0x1f18; i <= 0x3088 ; i += 16) { + nv_wo32(chan, i + 0, 0x10700ff9); + nv_wo32(chan, i + 1, 0x0436086c); + nv_wo32(chan, i + 2, 0x000c001b); + } + for (i = 0x30b8; i < 0x30c8; i += 4) + nv_wo32(chan, i, 0x0000ffff); + nv_wo32(chan, 0x344c, 0x3f800000); + nv_wo32(chan, 0x3808, 0x3f800000); + nv_wo32(chan, 0x381c, 0x3f800000); + nv_wo32(chan, 0x3848, 0x40000000); + nv_wo32(chan, 0x384c, 0x3f800000); + nv_wo32(chan, 0x3850, 0x3f000000); + nv_wo32(chan, 0x3858, 0x40000000); + nv_wo32(chan, 0x385c, 0x3f800000); + nv_wo32(chan, 0x3864, 0xbf800000); + nv_wo32(chan, 0x386c, 0xbf800000); + return 0; +} + +static struct nvkm_oclass +nv30_gr_cclass = { + .handle = NV_ENGCTX(GR, 0x30), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv30_gr_context_ctor, + .dtor = _nvkm_gr_context_dtor, + .init = nv20_gr_context_init, + .fini = nv20_gr_context_fini, + .rd32 = _nvkm_gr_context_rd32, + .wr32 = _nvkm_gr_context_wr32, + }, +}; + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +static int +nv30_gr_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv20_gr_priv *priv; + int ret; + + ret = nvkm_gr_create(parent, engine, oclass, true, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(nv_object(priv), NULL, 32 * 4, 16, + NVOBJ_FLAG_ZERO_ALLOC, &priv->ctxtab); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00001000; + nv_subdev(priv)->intr = nv20_gr_intr; + nv_engine(priv)->cclass = &nv30_gr_cclass; + nv_engine(priv)->sclass = nv30_gr_sclass; + nv_engine(priv)->tile_prog = nv20_gr_tile_prog; + return 0; +} + +int +nv30_gr_init(struct nvkm_object *object) +{ + struct nvkm_engine *engine = nv_engine(object); + struct nv20_gr_priv *priv = (void *)engine; + struct nvkm_fb *pfb = nvkm_fb(object); + int ret, i; + + ret = nvkm_gr_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, NV20_PGRAPH_CHANNEL_CTX_TABLE, priv->ctxtab->addr >> 4); + + nv_wr32(priv, NV03_PGRAPH_INTR , 0xFFFFFFFF); + nv_wr32(priv, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF); + + nv_wr32(priv, NV04_PGRAPH_DEBUG_0, 0xFFFFFFFF); + nv_wr32(priv, NV04_PGRAPH_DEBUG_0, 0x00000000); + nv_wr32(priv, NV04_PGRAPH_DEBUG_1, 0x401287c0); + nv_wr32(priv, 0x400890, 0x01b463ff); + nv_wr32(priv, NV04_PGRAPH_DEBUG_3, 0xf2de0475); + nv_wr32(priv, NV10_PGRAPH_DEBUG_4, 0x00008000); + nv_wr32(priv, NV04_PGRAPH_LIMIT_VIOL_PIX, 0xf04bdff6); + nv_wr32(priv, 0x400B80, 0x1003d888); + nv_wr32(priv, 0x400B84, 0x0c000000); + nv_wr32(priv, 0x400098, 0x00000000); + nv_wr32(priv, 0x40009C, 0x0005ad00); + nv_wr32(priv, 0x400B88, 0x62ff00ff); /* suspiciously like PGRAPH_DEBUG_2 */ + nv_wr32(priv, 0x4000a0, 0x00000000); + nv_wr32(priv, 0x4000a4, 0x00000008); + nv_wr32(priv, 0x4008a8, 0xb784a400); + nv_wr32(priv, 0x400ba0, 0x002f8685); + nv_wr32(priv, 0x400ba4, 0x00231f3f); + nv_wr32(priv, 0x4008a4, 0x40000020); + + if (nv_device(priv)->chipset == 0x34) { + nv_wr32(priv, NV10_PGRAPH_RDI_INDEX, 0x00EA0004); + nv_wr32(priv, NV10_PGRAPH_RDI_DATA , 0x00200201); + nv_wr32(priv, NV10_PGRAPH_RDI_INDEX, 0x00EA0008); + nv_wr32(priv, NV10_PGRAPH_RDI_DATA , 0x00000008); + nv_wr32(priv, NV10_PGRAPH_RDI_INDEX, 0x00EA0000); + nv_wr32(priv, NV10_PGRAPH_RDI_DATA , 0x00000032); + nv_wr32(priv, NV10_PGRAPH_RDI_INDEX, 0x00E00004); + nv_wr32(priv, NV10_PGRAPH_RDI_DATA , 0x00000002); + } + + nv_wr32(priv, 0x4000c0, 0x00000016); + + /* Turn all the tiling regions off. */ + for (i = 0; i < pfb->tile.regions; i++) + engine->tile_prog(engine, i); + + nv_wr32(priv, NV10_PGRAPH_CTX_CONTROL, 0x10000100); + nv_wr32(priv, NV10_PGRAPH_STATE , 0xFFFFFFFF); + nv_wr32(priv, 0x0040075c , 0x00000001); + + /* begin RAM config */ + /* vramsz = pci_resource_len(priv->dev->pdev, 0) - 1; */ + nv_wr32(priv, 0x4009A4, nv_rd32(priv, 0x100200)); + nv_wr32(priv, 0x4009A8, nv_rd32(priv, 0x100204)); + if (nv_device(priv)->chipset != 0x34) { + nv_wr32(priv, 0x400750, 0x00EA0000); + nv_wr32(priv, 0x400754, nv_rd32(priv, 0x100200)); + nv_wr32(priv, 0x400750, 0x00EA0004); + nv_wr32(priv, 0x400754, nv_rd32(priv, 0x100204)); + } + return 0; +} + +struct nvkm_oclass +nv30_gr_oclass = { + .handle = NV_ENGINE(GR, 0x30), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv30_gr_ctor, + .dtor = nv20_gr_dtor, + .init = nv30_gr_init, + .fini = _nvkm_gr_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv34.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv34.c new file mode 100644 index 000000000..985b7f330 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv34.c @@ -0,0 +1,159 @@ +#include "nv20.h" +#include "regs.h" + +#include + +/******************************************************************************* + * Graphics object classes + ******************************************************************************/ + +static struct nvkm_oclass +nv34_gr_sclass[] = { + { 0x0012, &nv04_gr_ofuncs, NULL }, /* beta1 */ + { 0x0019, &nv04_gr_ofuncs, NULL }, /* clip */ + { 0x0030, &nv04_gr_ofuncs, NULL }, /* null */ + { 0x0039, &nv04_gr_ofuncs, NULL }, /* m2mf */ + { 0x0043, &nv04_gr_ofuncs, NULL }, /* rop */ + { 0x0044, &nv04_gr_ofuncs, NULL }, /* patt */ + { 0x004a, &nv04_gr_ofuncs, NULL }, /* gdi */ + { 0x0062, &nv04_gr_ofuncs, NULL }, /* surf2d */ + { 0x0072, &nv04_gr_ofuncs, NULL }, /* beta4 */ + { 0x0089, &nv04_gr_ofuncs, NULL }, /* sifm */ + { 0x008a, &nv04_gr_ofuncs, NULL }, /* ifc */ + { 0x009f, &nv04_gr_ofuncs, NULL }, /* imageblit */ + { 0x0362, &nv04_gr_ofuncs, NULL }, /* surf2d (nv30) */ + { 0x0389, &nv04_gr_ofuncs, NULL }, /* sifm (nv30) */ + { 0x038a, &nv04_gr_ofuncs, NULL }, /* ifc (nv30) */ + { 0x039e, &nv04_gr_ofuncs, NULL }, /* swzsurf (nv30) */ + { 0x0697, &nv04_gr_ofuncs, NULL }, /* rankine */ + {}, +}; + +/******************************************************************************* + * PGRAPH context + ******************************************************************************/ + +static int +nv34_gr_context_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv20_gr_chan *chan; + int ret, i; + + ret = nvkm_gr_context_create(parent, engine, oclass, NULL, 0x46dc, + 16, NVOBJ_FLAG_ZERO_ALLOC, &chan); + *pobject = nv_object(chan); + if (ret) + return ret; + + chan->chid = nvkm_fifo_chan(parent)->chid; + + nv_wo32(chan, 0x0028, 0x00000001 | (chan->chid << 24)); + nv_wo32(chan, 0x040c, 0x01000101); + nv_wo32(chan, 0x0420, 0x00000111); + nv_wo32(chan, 0x0424, 0x00000060); + nv_wo32(chan, 0x0440, 0x00000080); + nv_wo32(chan, 0x0444, 0xffff0000); + nv_wo32(chan, 0x0448, 0x00000001); + nv_wo32(chan, 0x045c, 0x44400000); + nv_wo32(chan, 0x0480, 0xffff0000); + for (i = 0x04d4; i < 0x04dc; i += 4) + nv_wo32(chan, i, 0x0fff0000); + nv_wo32(chan, 0x04e0, 0x00011100); + for (i = 0x04fc; i < 0x053c; i += 4) + nv_wo32(chan, i, 0x07ff0000); + nv_wo32(chan, 0x0544, 0x4b7fffff); + nv_wo32(chan, 0x057c, 0x00000080); + nv_wo32(chan, 0x0580, 0x30201000); + nv_wo32(chan, 0x0584, 0x70605040); + nv_wo32(chan, 0x0588, 0xb8a89888); + nv_wo32(chan, 0x058c, 0xf8e8d8c8); + nv_wo32(chan, 0x05a0, 0xb0000000); + for (i = 0x05f0; i < 0x0630; i += 4) + nv_wo32(chan, i, 0x00010588); + for (i = 0x0630; i < 0x0670; i += 4) + nv_wo32(chan, i, 0x00030303); + for (i = 0x06b0; i < 0x06f0; i += 4) + nv_wo32(chan, i, 0x0008aae4); + for (i = 0x06f0; i < 0x0730; i += 4) + nv_wo32(chan, i, 0x01012000); + for (i = 0x0730; i < 0x0770; i += 4) + nv_wo32(chan, i, 0x00080008); + nv_wo32(chan, 0x0850, 0x00040000); + nv_wo32(chan, 0x0854, 0x00010000); + for (i = 0x0858; i < 0x0868; i += 4) + nv_wo32(chan, i, 0x00040004); + for (i = 0x15ac; i <= 0x271c ; i += 16) { + nv_wo32(chan, i + 0, 0x10700ff9); + nv_wo32(chan, i + 1, 0x0436086c); + nv_wo32(chan, i + 2, 0x000c001b); + } + for (i = 0x274c; i < 0x275c; i += 4) + nv_wo32(chan, i, 0x0000ffff); + nv_wo32(chan, 0x2ae0, 0x3f800000); + nv_wo32(chan, 0x2e9c, 0x3f800000); + nv_wo32(chan, 0x2eb0, 0x3f800000); + nv_wo32(chan, 0x2edc, 0x40000000); + nv_wo32(chan, 0x2ee0, 0x3f800000); + nv_wo32(chan, 0x2ee4, 0x3f000000); + nv_wo32(chan, 0x2eec, 0x40000000); + nv_wo32(chan, 0x2ef0, 0x3f800000); + nv_wo32(chan, 0x2ef8, 0xbf800000); + nv_wo32(chan, 0x2f00, 0xbf800000); + return 0; +} + +static struct nvkm_oclass +nv34_gr_cclass = { + .handle = NV_ENGCTX(GR, 0x34), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv34_gr_context_ctor, + .dtor = _nvkm_gr_context_dtor, + .init = nv20_gr_context_init, + .fini = nv20_gr_context_fini, + .rd32 = _nvkm_gr_context_rd32, + .wr32 = _nvkm_gr_context_wr32, + }, +}; + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +static int +nv34_gr_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv20_gr_priv *priv; + int ret; + + ret = nvkm_gr_create(parent, engine, oclass, true, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(nv_object(priv), NULL, 32 * 4, 16, + NVOBJ_FLAG_ZERO_ALLOC, &priv->ctxtab); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00001000; + nv_subdev(priv)->intr = nv20_gr_intr; + nv_engine(priv)->cclass = &nv34_gr_cclass; + nv_engine(priv)->sclass = nv34_gr_sclass; + nv_engine(priv)->tile_prog = nv20_gr_tile_prog; + return 0; +} + +struct nvkm_oclass +nv34_gr_oclass = { + .handle = NV_ENGINE(GR, 0x34), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv34_gr_ctor, + .dtor = nv20_gr_dtor, + .init = nv30_gr_init, + .fini = _nvkm_gr_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv35.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv35.c new file mode 100644 index 000000000..707625f19 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv35.c @@ -0,0 +1,159 @@ +#include "nv20.h" +#include "regs.h" + +#include + +/******************************************************************************* + * Graphics object classes + ******************************************************************************/ + +static struct nvkm_oclass +nv35_gr_sclass[] = { + { 0x0012, &nv04_gr_ofuncs, NULL }, /* beta1 */ + { 0x0019, &nv04_gr_ofuncs, NULL }, /* clip */ + { 0x0030, &nv04_gr_ofuncs, NULL }, /* null */ + { 0x0039, &nv04_gr_ofuncs, NULL }, /* m2mf */ + { 0x0043, &nv04_gr_ofuncs, NULL }, /* rop */ + { 0x0044, &nv04_gr_ofuncs, NULL }, /* patt */ + { 0x004a, &nv04_gr_ofuncs, NULL }, /* gdi */ + { 0x0062, &nv04_gr_ofuncs, NULL }, /* surf2d */ + { 0x0072, &nv04_gr_ofuncs, NULL }, /* beta4 */ + { 0x0089, &nv04_gr_ofuncs, NULL }, /* sifm */ + { 0x008a, &nv04_gr_ofuncs, NULL }, /* ifc */ + { 0x009f, &nv04_gr_ofuncs, NULL }, /* imageblit */ + { 0x0362, &nv04_gr_ofuncs, NULL }, /* surf2d (nv30) */ + { 0x0389, &nv04_gr_ofuncs, NULL }, /* sifm (nv30) */ + { 0x038a, &nv04_gr_ofuncs, NULL }, /* ifc (nv30) */ + { 0x039e, &nv04_gr_ofuncs, NULL }, /* swzsurf (nv30) */ + { 0x0497, &nv04_gr_ofuncs, NULL }, /* rankine */ + {}, +}; + +/******************************************************************************* + * PGRAPH context + ******************************************************************************/ + +static int +nv35_gr_context_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv20_gr_chan *chan; + int ret, i; + + ret = nvkm_gr_context_create(parent, engine, oclass, NULL, 0x577c, + 16, NVOBJ_FLAG_ZERO_ALLOC, &chan); + *pobject = nv_object(chan); + if (ret) + return ret; + + chan->chid = nvkm_fifo_chan(parent)->chid; + + nv_wo32(chan, 0x0028, 0x00000001 | (chan->chid << 24)); + nv_wo32(chan, 0x040c, 0x00000101); + nv_wo32(chan, 0x0420, 0x00000111); + nv_wo32(chan, 0x0424, 0x00000060); + nv_wo32(chan, 0x0440, 0x00000080); + nv_wo32(chan, 0x0444, 0xffff0000); + nv_wo32(chan, 0x0448, 0x00000001); + nv_wo32(chan, 0x045c, 0x44400000); + nv_wo32(chan, 0x0488, 0xffff0000); + for (i = 0x04dc; i < 0x04e4; i += 4) + nv_wo32(chan, i, 0x0fff0000); + nv_wo32(chan, 0x04e8, 0x00011100); + for (i = 0x0504; i < 0x0544; i += 4) + nv_wo32(chan, i, 0x07ff0000); + nv_wo32(chan, 0x054c, 0x4b7fffff); + nv_wo32(chan, 0x0588, 0x00000080); + nv_wo32(chan, 0x058c, 0x30201000); + nv_wo32(chan, 0x0590, 0x70605040); + nv_wo32(chan, 0x0594, 0xb8a89888); + nv_wo32(chan, 0x0598, 0xf8e8d8c8); + nv_wo32(chan, 0x05ac, 0xb0000000); + for (i = 0x0604; i < 0x0644; i += 4) + nv_wo32(chan, i, 0x00010588); + for (i = 0x0644; i < 0x0684; i += 4) + nv_wo32(chan, i, 0x00030303); + for (i = 0x06c4; i < 0x0704; i += 4) + nv_wo32(chan, i, 0x0008aae4); + for (i = 0x0704; i < 0x0744; i += 4) + nv_wo32(chan, i, 0x01012000); + for (i = 0x0744; i < 0x0784; i += 4) + nv_wo32(chan, i, 0x00080008); + nv_wo32(chan, 0x0860, 0x00040000); + nv_wo32(chan, 0x0864, 0x00010000); + for (i = 0x0868; i < 0x0878; i += 4) + nv_wo32(chan, i, 0x00040004); + for (i = 0x1f1c; i <= 0x308c ; i += 16) { + nv_wo32(chan, i + 0, 0x10700ff9); + nv_wo32(chan, i + 4, 0x0436086c); + nv_wo32(chan, i + 8, 0x000c001b); + } + for (i = 0x30bc; i < 0x30cc; i += 4) + nv_wo32(chan, i, 0x0000ffff); + nv_wo32(chan, 0x3450, 0x3f800000); + nv_wo32(chan, 0x380c, 0x3f800000); + nv_wo32(chan, 0x3820, 0x3f800000); + nv_wo32(chan, 0x384c, 0x40000000); + nv_wo32(chan, 0x3850, 0x3f800000); + nv_wo32(chan, 0x3854, 0x3f000000); + nv_wo32(chan, 0x385c, 0x40000000); + nv_wo32(chan, 0x3860, 0x3f800000); + nv_wo32(chan, 0x3868, 0xbf800000); + nv_wo32(chan, 0x3870, 0xbf800000); + return 0; +} + +static struct nvkm_oclass +nv35_gr_cclass = { + .handle = NV_ENGCTX(GR, 0x35), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv35_gr_context_ctor, + .dtor = _nvkm_gr_context_dtor, + .init = nv20_gr_context_init, + .fini = nv20_gr_context_fini, + .rd32 = _nvkm_gr_context_rd32, + .wr32 = _nvkm_gr_context_wr32, + }, +}; + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +static int +nv35_gr_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv20_gr_priv *priv; + int ret; + + ret = nvkm_gr_create(parent, engine, oclass, true, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(nv_object(priv), NULL, 32 * 4, 16, + NVOBJ_FLAG_ZERO_ALLOC, &priv->ctxtab); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00001000; + nv_subdev(priv)->intr = nv20_gr_intr; + nv_engine(priv)->cclass = &nv35_gr_cclass; + nv_engine(priv)->sclass = nv35_gr_sclass; + nv_engine(priv)->tile_prog = nv20_gr_tile_prog; + return 0; +} + +struct nvkm_oclass +nv35_gr_oclass = { + .handle = NV_ENGINE(GR, 0x35), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv35_gr_ctor, + .dtor = nv20_gr_dtor, + .init = nv30_gr_init, + .fini = _nvkm_gr_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv40.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv40.c new file mode 100644 index 000000000..7e1937980 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv40.c @@ -0,0 +1,527 @@ +/* + * 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 + +struct nv40_gr_priv { + struct nvkm_gr base; + u32 size; +}; + +struct nv40_gr_chan { + struct nvkm_gr_chan base; +}; + +static u64 +nv40_gr_units(struct nvkm_gr *gr) +{ + struct nv40_gr_priv *priv = (void *)gr; + + return nv_rd32(priv, 0x1540); +} + +/******************************************************************************* + * Graphics object classes + ******************************************************************************/ + +static int +nv40_gr_object_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_gpuobj *obj; + int ret; + + ret = nvkm_gpuobj_create(parent, engine, oclass, 0, parent, + 20, 16, 0, &obj); + *pobject = nv_object(obj); + if (ret) + return ret; + + nv_wo32(obj, 0x00, nv_mclass(obj)); + nv_wo32(obj, 0x04, 0x00000000); + nv_wo32(obj, 0x08, 0x00000000); +#ifdef __BIG_ENDIAN + nv_mo32(obj, 0x08, 0x01000000, 0x01000000); +#endif + nv_wo32(obj, 0x0c, 0x00000000); + nv_wo32(obj, 0x10, 0x00000000); + return 0; +} + +static struct nvkm_ofuncs +nv40_gr_ofuncs = { + .ctor = nv40_gr_object_ctor, + .dtor = _nvkm_gpuobj_dtor, + .init = _nvkm_gpuobj_init, + .fini = _nvkm_gpuobj_fini, + .rd32 = _nvkm_gpuobj_rd32, + .wr32 = _nvkm_gpuobj_wr32, +}; + +static struct nvkm_oclass +nv40_gr_sclass[] = { + { 0x0012, &nv40_gr_ofuncs, NULL }, /* beta1 */ + { 0x0019, &nv40_gr_ofuncs, NULL }, /* clip */ + { 0x0030, &nv40_gr_ofuncs, NULL }, /* null */ + { 0x0039, &nv40_gr_ofuncs, NULL }, /* m2mf */ + { 0x0043, &nv40_gr_ofuncs, NULL }, /* rop */ + { 0x0044, &nv40_gr_ofuncs, NULL }, /* patt */ + { 0x004a, &nv40_gr_ofuncs, NULL }, /* gdi */ + { 0x0062, &nv40_gr_ofuncs, NULL }, /* surf2d */ + { 0x0072, &nv40_gr_ofuncs, NULL }, /* beta4 */ + { 0x0089, &nv40_gr_ofuncs, NULL }, /* sifm */ + { 0x008a, &nv40_gr_ofuncs, NULL }, /* ifc */ + { 0x009f, &nv40_gr_ofuncs, NULL }, /* imageblit */ + { 0x3062, &nv40_gr_ofuncs, NULL }, /* surf2d (nv40) */ + { 0x3089, &nv40_gr_ofuncs, NULL }, /* sifm (nv40) */ + { 0x309e, &nv40_gr_ofuncs, NULL }, /* swzsurf (nv40) */ + { 0x4097, &nv40_gr_ofuncs, NULL }, /* curie */ + {}, +}; + +static struct nvkm_oclass +nv44_gr_sclass[] = { + { 0x0012, &nv40_gr_ofuncs, NULL }, /* beta1 */ + { 0x0019, &nv40_gr_ofuncs, NULL }, /* clip */ + { 0x0030, &nv40_gr_ofuncs, NULL }, /* null */ + { 0x0039, &nv40_gr_ofuncs, NULL }, /* m2mf */ + { 0x0043, &nv40_gr_ofuncs, NULL }, /* rop */ + { 0x0044, &nv40_gr_ofuncs, NULL }, /* patt */ + { 0x004a, &nv40_gr_ofuncs, NULL }, /* gdi */ + { 0x0062, &nv40_gr_ofuncs, NULL }, /* surf2d */ + { 0x0072, &nv40_gr_ofuncs, NULL }, /* beta4 */ + { 0x0089, &nv40_gr_ofuncs, NULL }, /* sifm */ + { 0x008a, &nv40_gr_ofuncs, NULL }, /* ifc */ + { 0x009f, &nv40_gr_ofuncs, NULL }, /* imageblit */ + { 0x3062, &nv40_gr_ofuncs, NULL }, /* surf2d (nv40) */ + { 0x3089, &nv40_gr_ofuncs, NULL }, /* sifm (nv40) */ + { 0x309e, &nv40_gr_ofuncs, NULL }, /* swzsurf (nv40) */ + { 0x4497, &nv40_gr_ofuncs, NULL }, /* curie */ + {}, +}; + +/******************************************************************************* + * PGRAPH context + ******************************************************************************/ + +static int +nv40_gr_context_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv40_gr_priv *priv = (void *)engine; + struct nv40_gr_chan *chan; + int ret; + + ret = nvkm_gr_context_create(parent, engine, oclass, NULL, priv->size, + 16, NVOBJ_FLAG_ZERO_ALLOC, &chan); + *pobject = nv_object(chan); + if (ret) + return ret; + + nv40_grctx_fill(nv_device(priv), nv_gpuobj(chan)); + nv_wo32(chan, 0x00000, nv_gpuobj(chan)->addr >> 4); + return 0; +} + +static int +nv40_gr_context_fini(struct nvkm_object *object, bool suspend) +{ + struct nv40_gr_priv *priv = (void *)object->engine; + struct nv40_gr_chan *chan = (void *)object; + u32 inst = 0x01000000 | nv_gpuobj(chan)->addr >> 4; + int ret = 0; + + nv_mask(priv, 0x400720, 0x00000001, 0x00000000); + + if (nv_rd32(priv, 0x40032c) == inst) { + if (suspend) { + nv_wr32(priv, 0x400720, 0x00000000); + nv_wr32(priv, 0x400784, inst); + nv_mask(priv, 0x400310, 0x00000020, 0x00000020); + nv_mask(priv, 0x400304, 0x00000001, 0x00000001); + if (!nv_wait(priv, 0x400300, 0x00000001, 0x00000000)) { + u32 insn = nv_rd32(priv, 0x400308); + nv_warn(priv, "ctxprog timeout 0x%08x\n", insn); + ret = -EBUSY; + } + } + + nv_mask(priv, 0x40032c, 0x01000000, 0x00000000); + } + + if (nv_rd32(priv, 0x400330) == inst) + nv_mask(priv, 0x400330, 0x01000000, 0x00000000); + + nv_mask(priv, 0x400720, 0x00000001, 0x00000001); + return ret; +} + +static struct nvkm_oclass +nv40_gr_cclass = { + .handle = NV_ENGCTX(GR, 0x40), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv40_gr_context_ctor, + .dtor = _nvkm_gr_context_dtor, + .init = _nvkm_gr_context_init, + .fini = nv40_gr_context_fini, + .rd32 = _nvkm_gr_context_rd32, + .wr32 = _nvkm_gr_context_wr32, + }, +}; + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +static void +nv40_gr_tile_prog(struct nvkm_engine *engine, int i) +{ + struct nvkm_fb_tile *tile = &nvkm_fb(engine)->tile.region[i]; + struct nvkm_fifo *pfifo = nvkm_fifo(engine); + struct nv40_gr_priv *priv = (void *)engine; + unsigned long flags; + + pfifo->pause(pfifo, &flags); + nv04_gr_idle(priv); + + switch (nv_device(priv)->chipset) { + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x45: + case 0x4e: + nv_wr32(priv, NV20_PGRAPH_TSIZE(i), tile->pitch); + nv_wr32(priv, NV20_PGRAPH_TLIMIT(i), tile->limit); + nv_wr32(priv, NV20_PGRAPH_TILE(i), tile->addr); + nv_wr32(priv, NV40_PGRAPH_TSIZE1(i), tile->pitch); + nv_wr32(priv, NV40_PGRAPH_TLIMIT1(i), tile->limit); + nv_wr32(priv, NV40_PGRAPH_TILE1(i), tile->addr); + switch (nv_device(priv)->chipset) { + case 0x40: + case 0x45: + nv_wr32(priv, NV20_PGRAPH_ZCOMP(i), tile->zcomp); + nv_wr32(priv, NV40_PGRAPH_ZCOMP1(i), tile->zcomp); + break; + case 0x41: + case 0x42: + case 0x43: + nv_wr32(priv, NV41_PGRAPH_ZCOMP0(i), tile->zcomp); + nv_wr32(priv, NV41_PGRAPH_ZCOMP1(i), tile->zcomp); + break; + default: + break; + } + break; + case 0x44: + case 0x4a: + nv_wr32(priv, NV20_PGRAPH_TSIZE(i), tile->pitch); + nv_wr32(priv, NV20_PGRAPH_TLIMIT(i), tile->limit); + nv_wr32(priv, NV20_PGRAPH_TILE(i), tile->addr); + break; + case 0x46: + case 0x4c: + case 0x47: + case 0x49: + case 0x4b: + case 0x63: + case 0x67: + case 0x68: + nv_wr32(priv, NV47_PGRAPH_TSIZE(i), tile->pitch); + nv_wr32(priv, NV47_PGRAPH_TLIMIT(i), tile->limit); + nv_wr32(priv, NV47_PGRAPH_TILE(i), tile->addr); + nv_wr32(priv, NV40_PGRAPH_TSIZE1(i), tile->pitch); + nv_wr32(priv, NV40_PGRAPH_TLIMIT1(i), tile->limit); + nv_wr32(priv, NV40_PGRAPH_TILE1(i), tile->addr); + switch (nv_device(priv)->chipset) { + case 0x47: + case 0x49: + case 0x4b: + nv_wr32(priv, NV47_PGRAPH_ZCOMP0(i), tile->zcomp); + nv_wr32(priv, NV47_PGRAPH_ZCOMP1(i), tile->zcomp); + break; + default: + break; + } + break; + default: + break; + } + + pfifo->start(pfifo, &flags); +} + +static void +nv40_gr_intr(struct nvkm_subdev *subdev) +{ + struct nvkm_fifo *pfifo = nvkm_fifo(subdev); + struct nvkm_engine *engine = nv_engine(subdev); + struct nvkm_object *engctx; + struct nvkm_handle *handle = NULL; + struct nv40_gr_priv *priv = (void *)subdev; + u32 stat = nv_rd32(priv, NV03_PGRAPH_INTR); + u32 nsource = nv_rd32(priv, NV03_PGRAPH_NSOURCE); + u32 nstatus = nv_rd32(priv, NV03_PGRAPH_NSTATUS); + u32 inst = nv_rd32(priv, 0x40032c) & 0x000fffff; + u32 addr = nv_rd32(priv, NV04_PGRAPH_TRAPPED_ADDR); + u32 subc = (addr & 0x00070000) >> 16; + u32 mthd = (addr & 0x00001ffc); + u32 data = nv_rd32(priv, NV04_PGRAPH_TRAPPED_DATA); + u32 class = nv_rd32(priv, 0x400160 + subc * 4) & 0xffff; + u32 show = stat; + int chid; + + engctx = nvkm_engctx_get(engine, inst); + chid = pfifo->chid(pfifo, engctx); + + if (stat & NV_PGRAPH_INTR_ERROR) { + if (nsource & NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD) { + handle = nvkm_handle_get_class(engctx, class); + if (handle && !nv_call(handle->object, mthd, data)) + show &= ~NV_PGRAPH_INTR_ERROR; + nvkm_handle_put(handle); + } + + if (nsource & NV03_PGRAPH_NSOURCE_DMA_VTX_PROTECTION) { + nv_mask(priv, 0x402000, 0, 0); + } + } + + nv_wr32(priv, NV03_PGRAPH_INTR, stat); + nv_wr32(priv, NV04_PGRAPH_FIFO, 0x00000001); + + if (show) { + nv_error(priv, "%s", ""); + nvkm_bitfield_print(nv10_gr_intr_name, show); + pr_cont(" nsource:"); + nvkm_bitfield_print(nv04_gr_nsource, nsource); + pr_cont(" nstatus:"); + nvkm_bitfield_print(nv10_gr_nstatus, nstatus); + pr_cont("\n"); + nv_error(priv, + "ch %d [0x%08x %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n", + chid, inst << 4, nvkm_client_name(engctx), subc, + class, mthd, data); + } + + nvkm_engctx_put(engctx); +} + +static int +nv40_gr_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv40_gr_priv *priv; + int ret; + + ret = nvkm_gr_create(parent, engine, oclass, true, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00001000; + nv_subdev(priv)->intr = nv40_gr_intr; + nv_engine(priv)->cclass = &nv40_gr_cclass; + if (nv44_gr_class(priv)) + nv_engine(priv)->sclass = nv44_gr_sclass; + else + nv_engine(priv)->sclass = nv40_gr_sclass; + nv_engine(priv)->tile_prog = nv40_gr_tile_prog; + + priv->base.units = nv40_gr_units; + return 0; +} + +static int +nv40_gr_init(struct nvkm_object *object) +{ + struct nvkm_engine *engine = nv_engine(object); + struct nvkm_fb *pfb = nvkm_fb(object); + struct nv40_gr_priv *priv = (void *)engine; + int ret, i, j; + u32 vramsz; + + ret = nvkm_gr_init(&priv->base); + if (ret) + return ret; + + /* generate and upload context program */ + ret = nv40_grctx_init(nv_device(priv), &priv->size); + if (ret) + return ret; + + /* No context present currently */ + nv_wr32(priv, NV40_PGRAPH_CTXCTL_CUR, 0x00000000); + + nv_wr32(priv, NV03_PGRAPH_INTR , 0xFFFFFFFF); + nv_wr32(priv, NV40_PGRAPH_INTR_EN, 0xFFFFFFFF); + + nv_wr32(priv, NV04_PGRAPH_DEBUG_0, 0xFFFFFFFF); + nv_wr32(priv, NV04_PGRAPH_DEBUG_0, 0x00000000); + nv_wr32(priv, NV04_PGRAPH_DEBUG_1, 0x401287c0); + nv_wr32(priv, NV04_PGRAPH_DEBUG_3, 0xe0de8055); + nv_wr32(priv, NV10_PGRAPH_DEBUG_4, 0x00008000); + nv_wr32(priv, NV04_PGRAPH_LIMIT_VIOL_PIX, 0x00be3c5f); + + nv_wr32(priv, NV10_PGRAPH_CTX_CONTROL, 0x10010100); + nv_wr32(priv, NV10_PGRAPH_STATE , 0xFFFFFFFF); + + j = nv_rd32(priv, 0x1540) & 0xff; + if (j) { + for (i = 0; !(j & 1); j >>= 1, i++) + ; + nv_wr32(priv, 0x405000, i); + } + + if (nv_device(priv)->chipset == 0x40) { + nv_wr32(priv, 0x4009b0, 0x83280fff); + nv_wr32(priv, 0x4009b4, 0x000000a0); + } else { + nv_wr32(priv, 0x400820, 0x83280eff); + nv_wr32(priv, 0x400824, 0x000000a0); + } + + switch (nv_device(priv)->chipset) { + case 0x40: + case 0x45: + nv_wr32(priv, 0x4009b8, 0x0078e366); + nv_wr32(priv, 0x4009bc, 0x0000014c); + break; + case 0x41: + case 0x42: /* pciid also 0x00Cx */ + /* case 0x0120: XXX (pciid) */ + nv_wr32(priv, 0x400828, 0x007596ff); + nv_wr32(priv, 0x40082c, 0x00000108); + break; + case 0x43: + nv_wr32(priv, 0x400828, 0x0072cb77); + nv_wr32(priv, 0x40082c, 0x00000108); + break; + case 0x44: + case 0x46: /* G72 */ + case 0x4a: + case 0x4c: /* G7x-based C51 */ + case 0x4e: + nv_wr32(priv, 0x400860, 0); + nv_wr32(priv, 0x400864, 0); + break; + case 0x47: /* G70 */ + case 0x49: /* G71 */ + case 0x4b: /* G73 */ + nv_wr32(priv, 0x400828, 0x07830610); + nv_wr32(priv, 0x40082c, 0x0000016A); + break; + default: + break; + } + + nv_wr32(priv, 0x400b38, 0x2ffff800); + nv_wr32(priv, 0x400b3c, 0x00006000); + + /* Tiling related stuff. */ + switch (nv_device(priv)->chipset) { + case 0x44: + case 0x4a: + nv_wr32(priv, 0x400bc4, 0x1003d888); + nv_wr32(priv, 0x400bbc, 0xb7a7b500); + break; + case 0x46: + nv_wr32(priv, 0x400bc4, 0x0000e024); + nv_wr32(priv, 0x400bbc, 0xb7a7b520); + break; + case 0x4c: + case 0x4e: + case 0x67: + nv_wr32(priv, 0x400bc4, 0x1003d888); + nv_wr32(priv, 0x400bbc, 0xb7a7b540); + break; + default: + break; + } + + /* Turn all the tiling regions off. */ + for (i = 0; i < pfb->tile.regions; i++) + engine->tile_prog(engine, i); + + /* begin RAM config */ + vramsz = nv_device_resource_len(nv_device(priv), 0) - 1; + switch (nv_device(priv)->chipset) { + case 0x40: + nv_wr32(priv, 0x4009A4, nv_rd32(priv, 0x100200)); + nv_wr32(priv, 0x4009A8, nv_rd32(priv, 0x100204)); + nv_wr32(priv, 0x4069A4, nv_rd32(priv, 0x100200)); + nv_wr32(priv, 0x4069A8, nv_rd32(priv, 0x100204)); + nv_wr32(priv, 0x400820, 0); + nv_wr32(priv, 0x400824, 0); + nv_wr32(priv, 0x400864, vramsz); + nv_wr32(priv, 0x400868, vramsz); + break; + default: + switch (nv_device(priv)->chipset) { + case 0x41: + case 0x42: + case 0x43: + case 0x45: + case 0x4e: + case 0x44: + case 0x4a: + nv_wr32(priv, 0x4009F0, nv_rd32(priv, 0x100200)); + nv_wr32(priv, 0x4009F4, nv_rd32(priv, 0x100204)); + break; + default: + nv_wr32(priv, 0x400DF0, nv_rd32(priv, 0x100200)); + nv_wr32(priv, 0x400DF4, nv_rd32(priv, 0x100204)); + break; + } + nv_wr32(priv, 0x4069F0, nv_rd32(priv, 0x100200)); + nv_wr32(priv, 0x4069F4, nv_rd32(priv, 0x100204)); + nv_wr32(priv, 0x400840, 0); + nv_wr32(priv, 0x400844, 0); + nv_wr32(priv, 0x4008A0, vramsz); + nv_wr32(priv, 0x4008A4, vramsz); + break; + } + + return 0; +} + +struct nvkm_oclass +nv40_gr_oclass = { + .handle = NV_ENGINE(GR, 0x40), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv40_gr_ctor, + .dtor = _nvkm_gr_dtor, + .init = nv40_gr_init, + .fini = _nvkm_gr_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv40.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv40.h new file mode 100644 index 000000000..d852bd6de --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv40.h @@ -0,0 +1,24 @@ +#ifndef __NV40_GR_H__ +#define __NV40_GR_H__ +#include + +#include +struct nvkm_gpuobj; + +/* 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(void *priv) +{ + struct nvkm_device *device = nv_device(priv); + + if ((device->chipset & 0xf0) == 0x60) + return 1; + + return !(0x0baf & (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/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv50.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv50.c new file mode 100644 index 000000000..270d7cd63 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv50.c @@ -0,0 +1,999 @@ +/* + * 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 + +struct nv50_gr_priv { + struct nvkm_gr base; + spinlock_t lock; + u32 size; +}; + +struct nv50_gr_chan { + struct nvkm_gr_chan base; +}; + +static u64 +nv50_gr_units(struct nvkm_gr *gr) +{ + struct nv50_gr_priv *priv = (void *)gr; + + return nv_rd32(priv, 0x1540); +} + +/******************************************************************************* + * Graphics object classes + ******************************************************************************/ + +static int +nv50_gr_object_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_gpuobj *obj; + int ret; + + ret = nvkm_gpuobj_create(parent, engine, oclass, 0, parent, + 16, 16, 0, &obj); + *pobject = nv_object(obj); + if (ret) + return ret; + + nv_wo32(obj, 0x00, nv_mclass(obj)); + nv_wo32(obj, 0x04, 0x00000000); + nv_wo32(obj, 0x08, 0x00000000); + nv_wo32(obj, 0x0c, 0x00000000); + return 0; +} + +static struct nvkm_ofuncs +nv50_gr_ofuncs = { + .ctor = nv50_gr_object_ctor, + .dtor = _nvkm_gpuobj_dtor, + .init = _nvkm_gpuobj_init, + .fini = _nvkm_gpuobj_fini, + .rd32 = _nvkm_gpuobj_rd32, + .wr32 = _nvkm_gpuobj_wr32, +}; + +static struct nvkm_oclass +nv50_gr_sclass[] = { + { 0x0030, &nv50_gr_ofuncs }, + { 0x502d, &nv50_gr_ofuncs }, + { 0x5039, &nv50_gr_ofuncs }, + { 0x5097, &nv50_gr_ofuncs }, + { 0x50c0, &nv50_gr_ofuncs }, + {} +}; + +static struct nvkm_oclass +g84_gr_sclass[] = { + { 0x0030, &nv50_gr_ofuncs }, + { 0x502d, &nv50_gr_ofuncs }, + { 0x5039, &nv50_gr_ofuncs }, + { 0x50c0, &nv50_gr_ofuncs }, + { 0x8297, &nv50_gr_ofuncs }, + {} +}; + +static struct nvkm_oclass +gt200_gr_sclass[] = { + { 0x0030, &nv50_gr_ofuncs }, + { 0x502d, &nv50_gr_ofuncs }, + { 0x5039, &nv50_gr_ofuncs }, + { 0x50c0, &nv50_gr_ofuncs }, + { 0x8397, &nv50_gr_ofuncs }, + {} +}; + +static struct nvkm_oclass +gt215_gr_sclass[] = { + { 0x0030, &nv50_gr_ofuncs }, + { 0x502d, &nv50_gr_ofuncs }, + { 0x5039, &nv50_gr_ofuncs }, + { 0x50c0, &nv50_gr_ofuncs }, + { 0x8597, &nv50_gr_ofuncs }, + { 0x85c0, &nv50_gr_ofuncs }, + {} +}; + +static struct nvkm_oclass +mcp89_gr_sclass[] = { + { 0x0030, &nv50_gr_ofuncs }, + { 0x502d, &nv50_gr_ofuncs }, + { 0x5039, &nv50_gr_ofuncs }, + { 0x50c0, &nv50_gr_ofuncs }, + { 0x85c0, &nv50_gr_ofuncs }, + { 0x8697, &nv50_gr_ofuncs }, + {} +}; + +/******************************************************************************* + * PGRAPH context + ******************************************************************************/ + +static int +nv50_gr_context_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv50_gr_priv *priv = (void *)engine; + struct nv50_gr_chan *chan; + int ret; + + ret = nvkm_gr_context_create(parent, engine, oclass, NULL, priv->size, + 0, NVOBJ_FLAG_ZERO_ALLOC, &chan); + *pobject = nv_object(chan); + if (ret) + return ret; + + nv50_grctx_fill(nv_device(priv), nv_gpuobj(chan)); + return 0; +} + +static struct nvkm_oclass +nv50_gr_cclass = { + .handle = NV_ENGCTX(GR, 0x50), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_gr_context_ctor, + .dtor = _nvkm_gr_context_dtor, + .init = _nvkm_gr_context_init, + .fini = _nvkm_gr_context_fini, + .rd32 = _nvkm_gr_context_rd32, + .wr32 = _nvkm_gr_context_wr32, + }, +}; + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +static const struct nvkm_bitfield nv50_pgr_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 char *const nv50_pgr_vstatus_0[] = { + "VFETCH", "CCACHE", "PREGEOM", "POSTGEOM", "VATTR", "STRMOUT", "VCLIP", + NULL +}; + +static const char *const nv50_pgr_vstatus_1[] = { + "TPC_RAST", "TPC_PROP", "TPC_TEX", "TPC_GEOM", "TPC_MP", NULL +}; + +static const char *const nv50_pgr_vstatus_2[] = { + "RATTR", "APLANE", "TRAST", "CLIPID", "ZCULL", "ENG2D", "RMASK", + "ROP", NULL +}; + +static void +nvkm_pgr_vstatus_print(struct nv50_gr_priv *priv, int r, + const char *const units[], u32 status) +{ + int i; + + nv_error(priv, "PGRAPH_VSTATUS%d: 0x%08x", r, status); + + for (i = 0; units[i] && status; i++) { + if ((status & 7) == 1) + pr_cont(" %s", units[i]); + status >>= 3; + } + if (status) + pr_cont(" (invalid: 0x%x)", status); + pr_cont("\n"); +} + +static int +g84_gr_tlb_flush(struct nvkm_engine *engine) +{ + struct nvkm_timer *ptimer = nvkm_timer(engine); + struct nv50_gr_priv *priv = (void *)engine; + bool idle, timeout = false; + unsigned long flags; + u64 start; + u32 tmp; + + spin_lock_irqsave(&priv->lock, flags); + nv_mask(priv, 0x400500, 0x00000001, 0x00000000); + + start = ptimer->read(ptimer); + do { + idle = true; + + for (tmp = nv_rd32(priv, 0x400380); tmp && idle; tmp >>= 3) { + if ((tmp & 7) == 1) + idle = false; + } + + for (tmp = nv_rd32(priv, 0x400384); tmp && idle; tmp >>= 3) { + if ((tmp & 7) == 1) + idle = false; + } + + for (tmp = nv_rd32(priv, 0x400388); tmp && idle; tmp >>= 3) { + if ((tmp & 7) == 1) + idle = false; + } + } while (!idle && + !(timeout = ptimer->read(ptimer) - start > 2000000000)); + + if (timeout) { + nv_error(priv, "PGRAPH TLB flush idle timeout fail\n"); + + tmp = nv_rd32(priv, 0x400700); + nv_error(priv, "PGRAPH_STATUS : 0x%08x", tmp); + nvkm_bitfield_print(nv50_pgr_status, tmp); + pr_cont("\n"); + + nvkm_pgr_vstatus_print(priv, 0, nv50_pgr_vstatus_0, + nv_rd32(priv, 0x400380)); + nvkm_pgr_vstatus_print(priv, 1, nv50_pgr_vstatus_1, + nv_rd32(priv, 0x400384)); + nvkm_pgr_vstatus_print(priv, 2, nv50_pgr_vstatus_2, + nv_rd32(priv, 0x400388)); + } + + + nv_wr32(priv, 0x100c80, 0x00000001); + if (!nv_wait(priv, 0x100c80, 0x00000001, 0x00000000)) + nv_error(priv, "vm flush timeout\n"); + nv_mask(priv, 0x400500, 0x00000001, 0x00000001); + spin_unlock_irqrestore(&priv->lock, flags); + return timeout ? -EBUSY : 0; +} + +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_priv_prop_trap(struct nv50_gr_priv *priv, + u32 ustatus_addr, u32 ustatus, u32 tp) +{ + u32 e0c = nv_rd32(priv, ustatus_addr + 0x04); + u32 e10 = nv_rd32(priv, ustatus_addr + 0x08); + u32 e14 = nv_rd32(priv, ustatus_addr + 0x0c); + u32 e18 = nv_rd32(priv, ustatus_addr + 0x10); + u32 e1c = nv_rd32(priv, ustatus_addr + 0x14); + u32 e20 = nv_rd32(priv, ustatus_addr + 0x18); + u32 e24 = nv_rd32(priv, ustatus_addr + 0x1c); + + /* CUDA memory: l[], g[] or stack. */ + if (ustatus & 0x00000080) { + if (e18 & 0x80000000) { + /* g[] read fault? */ + nv_error(priv, "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? */ + nv_error(priv, "TRAP_PROP - TP %d - CUDA_FAULT - Global write fault at address %02x%08x\n", + tp, e14, e10 | ((e18 >> 7) & 0x1f)); + e18 &= ~0x00000f80; + } else { + nv_error(priv, "TRAP_PROP - TP %d - Unknown CUDA fault at address %02x%08x\n", + tp, e14, e10); + } + ustatus &= ~0x00000080; + } + if (ustatus) { + nv_error(priv, "TRAP_PROP - TP %d -", tp); + nvkm_bitfield_print(nv50_gr_trap_prop, ustatus); + pr_cont(" - Address %02x%08x\n", e14, e10); + } + nv_error(priv, "TRAP_PROP - TP %d - e0c: %08x, e18: %08x, e1c: %08x, e20: %08x, e24: %08x\n", + tp, e0c, e18, e1c, e20, e24); +} + +static void +nv50_priv_mp_trap(struct nv50_gr_priv *priv, int tpid, int display) +{ + u32 units = nv_rd32(priv, 0x1540); + u32 addr, mp10, status, pc, oplow, ophigh; + int i; + int mps = 0; + for (i = 0; i < 4; i++) { + if (!(units & 1 << (i+24))) + continue; + if (nv_device(priv)->chipset < 0xa0) + addr = 0x408200 + (tpid << 12) + (i << 7); + else + addr = 0x408100 + (tpid << 11) + (i << 7); + mp10 = nv_rd32(priv, addr + 0x10); + status = nv_rd32(priv, addr + 0x14); + if (!status) + continue; + if (display) { + nv_rd32(priv, addr + 0x20); + pc = nv_rd32(priv, addr + 0x24); + oplow = nv_rd32(priv, addr + 0x70); + ophigh = nv_rd32(priv, addr + 0x74); + nv_error(priv, "TRAP_MP_EXEC - " + "TP %d MP %d:", tpid, i); + nvkm_bitfield_print(nv50_mp_exec_errors, status); + pr_cont(" at %06x warp %d, opcode %08x %08x\n", + pc&0xffffff, pc >> 24, + oplow, ophigh); + } + nv_wr32(priv, addr + 0x10, mp10); + nv_wr32(priv, addr + 0x14, 0); + mps++; + } + if (!mps && display) + nv_error(priv, "TRAP_MP_EXEC - TP %d: " + "No MPs claiming errors?\n", tpid); +} + +static void +nv50_priv_tp_trap(struct nv50_gr_priv *priv, int type, u32 ustatus_old, + u32 ustatus_new, int display, const char *name) +{ + int tps = 0; + u32 units = nv_rd32(priv, 0x1540); + int i, r; + u32 ustatus_addr, ustatus; + for (i = 0; i < 16; i++) { + if (!(units & (1 << i))) + continue; + if (nv_device(priv)->chipset < 0xa0) + ustatus_addr = ustatus_old + (i << 12); + else + ustatus_addr = ustatus_new + (i << 11); + ustatus = nv_rd32(priv, ustatus_addr) & 0x7fffffff; + if (!ustatus) + continue; + tps++; + switch (type) { + case 6: /* texture error... unknown for now */ + if (display) { + nv_error(priv, "magic set %d:\n", i); + for (r = ustatus_addr + 4; r <= ustatus_addr + 0x10; r += 4) + nv_error(priv, "\t0x%08x: 0x%08x\n", r, + nv_rd32(priv, r)); + if (ustatus) { + nv_error(priv, "%s - TP%d:", name, i); + nvkm_bitfield_print(nv50_tex_traps, + ustatus); + pr_cont("\n"); + ustatus = 0; + } + } + break; + case 7: /* MP error */ + if (ustatus & 0x04030000) { + nv50_priv_mp_trap(priv, i, display); + ustatus &= ~0x04030000; + } + if (ustatus && display) { + nv_error(priv, "%s - TP%d:", name, i); + nvkm_bitfield_print(nv50_mpc_traps, ustatus); + pr_cont("\n"); + ustatus = 0; + } + break; + case 8: /* PROP error */ + if (display) + nv50_priv_prop_trap( + priv, ustatus_addr, ustatus, i); + ustatus = 0; + break; + } + if (ustatus) { + if (display) + nv_error(priv, "%s - TP%d: Unhandled ustatus 0x%08x\n", name, i, ustatus); + } + nv_wr32(priv, ustatus_addr, 0xc0000000); + } + + if (!tps && display) + nv_warn(priv, "%s - No TPs claiming errors?\n", name); +} + +static int +nv50_gr_trap_handler(struct nv50_gr_priv *priv, u32 display, + int chid, u64 inst, struct nvkm_object *engctx) +{ + u32 status = nv_rd32(priv, 0x400108); + u32 ustatus; + + if (!status && display) { + nv_error(priv, "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 = nv_rd32(priv, 0x400804) & 0x7fffffff; + if (!ustatus && display) { + nv_error(priv, "TRAP_DISPATCH - no ustatus?\n"); + } + + nv_wr32(priv, 0x400500, 0x00000000); + + /* Known to be triggered by screwed up NOTIFY and COND... */ + if (ustatus & 0x00000001) { + u32 addr = nv_rd32(priv, 0x400808); + u32 subc = (addr & 0x00070000) >> 16; + u32 mthd = (addr & 0x00001ffc); + u32 datal = nv_rd32(priv, 0x40080c); + u32 datah = nv_rd32(priv, 0x400810); + u32 class = nv_rd32(priv, 0x400814); + u32 r848 = nv_rd32(priv, 0x400848); + + nv_error(priv, "TRAP DISPATCH_FAULT\n"); + if (display && (addr & 0x80000000)) { + nv_error(priv, + "ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x%08x 400808 0x%08x 400848 0x%08x\n", + chid, inst, + nvkm_client_name(engctx), subc, + class, mthd, datah, datal, addr, r848); + } else + if (display) { + nv_error(priv, "no stuck command?\n"); + } + + nv_wr32(priv, 0x400808, 0); + nv_wr32(priv, 0x4008e8, nv_rd32(priv, 0x4008e8) & 3); + nv_wr32(priv, 0x400848, 0); + ustatus &= ~0x00000001; + } + + if (ustatus & 0x00000002) { + u32 addr = nv_rd32(priv, 0x40084c); + u32 subc = (addr & 0x00070000) >> 16; + u32 mthd = (addr & 0x00001ffc); + u32 data = nv_rd32(priv, 0x40085c); + u32 class = nv_rd32(priv, 0x400814); + + nv_error(priv, "TRAP DISPATCH_QUERY\n"); + if (display && (addr & 0x80000000)) { + nv_error(priv, + "ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x 40084c 0x%08x\n", + chid, inst, + nvkm_client_name(engctx), subc, + class, mthd, data, addr); + } else + if (display) { + nv_error(priv, "no stuck command?\n"); + } + + nv_wr32(priv, 0x40084c, 0); + ustatus &= ~0x00000002; + } + + if (ustatus && display) { + nv_error(priv, "TRAP_DISPATCH (unknown " + "0x%08x)\n", ustatus); + } + + nv_wr32(priv, 0x400804, 0xc0000000); + nv_wr32(priv, 0x400108, 0x001); + status &= ~0x001; + if (!status) + return 0; + } + + /* M2MF: Memory to memory copy engine. */ + if (status & 0x002) { + u32 ustatus = nv_rd32(priv, 0x406800) & 0x7fffffff; + if (display) { + nv_error(priv, "TRAP_M2MF"); + nvkm_bitfield_print(nv50_gr_trap_m2mf, ustatus); + pr_cont("\n"); + nv_error(priv, "TRAP_M2MF %08x %08x %08x %08x\n", + nv_rd32(priv, 0x406804), nv_rd32(priv, 0x406808), + nv_rd32(priv, 0x40680c), nv_rd32(priv, 0x406810)); + + } + + /* No sane way found yet -- just reset the bugger. */ + nv_wr32(priv, 0x400040, 2); + nv_wr32(priv, 0x400040, 0); + nv_wr32(priv, 0x406800, 0xc0000000); + nv_wr32(priv, 0x400108, 0x002); + status &= ~0x002; + } + + /* VFETCH: Fetches data from vertex buffers. */ + if (status & 0x004) { + u32 ustatus = nv_rd32(priv, 0x400c04) & 0x7fffffff; + if (display) { + nv_error(priv, "TRAP_VFETCH"); + nvkm_bitfield_print(nv50_gr_trap_vfetch, ustatus); + pr_cont("\n"); + nv_error(priv, "TRAP_VFETCH %08x %08x %08x %08x\n", + nv_rd32(priv, 0x400c00), nv_rd32(priv, 0x400c08), + nv_rd32(priv, 0x400c0c), nv_rd32(priv, 0x400c10)); + } + + nv_wr32(priv, 0x400c04, 0xc0000000); + nv_wr32(priv, 0x400108, 0x004); + status &= ~0x004; + } + + /* STRMOUT: DirectX streamout / OpenGL transform feedback. */ + if (status & 0x008) { + ustatus = nv_rd32(priv, 0x401800) & 0x7fffffff; + if (display) { + nv_error(priv, "TRAP_STRMOUT"); + nvkm_bitfield_print(nv50_gr_trap_strmout, ustatus); + pr_cont("\n"); + nv_error(priv, "TRAP_STRMOUT %08x %08x %08x %08x\n", + nv_rd32(priv, 0x401804), nv_rd32(priv, 0x401808), + nv_rd32(priv, 0x40180c), nv_rd32(priv, 0x401810)); + + } + + /* No sane way found yet -- just reset the bugger. */ + nv_wr32(priv, 0x400040, 0x80); + nv_wr32(priv, 0x400040, 0); + nv_wr32(priv, 0x401800, 0xc0000000); + nv_wr32(priv, 0x400108, 0x008); + status &= ~0x008; + } + + /* CCACHE: Handles code and c[] caches and fills them. */ + if (status & 0x010) { + ustatus = nv_rd32(priv, 0x405018) & 0x7fffffff; + if (display) { + nv_error(priv, "TRAP_CCACHE"); + nvkm_bitfield_print(nv50_gr_trap_ccache, ustatus); + pr_cont("\n"); + nv_error(priv, "TRAP_CCACHE %08x %08x %08x %08x" + " %08x %08x %08x\n", + nv_rd32(priv, 0x405000), nv_rd32(priv, 0x405004), + nv_rd32(priv, 0x405008), nv_rd32(priv, 0x40500c), + nv_rd32(priv, 0x405010), nv_rd32(priv, 0x405014), + nv_rd32(priv, 0x40501c)); + + } + + nv_wr32(priv, 0x405018, 0xc0000000); + nv_wr32(priv, 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 = nv_rd32(priv, 0x402000) & 0x7fffffff; + if (display) + nv_error(priv, "TRAP_UNKC04 0x%08x\n", ustatus); + nv_wr32(priv, 0x402000, 0xc0000000); + /* no status modifiction on purpose */ + } + + /* TEXTURE: CUDA texturing units */ + if (status & 0x040) { + nv50_priv_tp_trap(priv, 6, 0x408900, 0x408600, display, + "TRAP_TEXTURE"); + nv_wr32(priv, 0x400108, 0x040); + status &= ~0x040; + } + + /* MP: CUDA execution engines. */ + if (status & 0x080) { + nv50_priv_tp_trap(priv, 7, 0x408314, 0x40831c, display, + "TRAP_MP"); + nv_wr32(priv, 0x400108, 0x080); + status &= ~0x080; + } + + /* PROP: Handles TP-initiated uncached memory accesses: + * l[], g[], stack, 2d surfaces, render targets. */ + if (status & 0x100) { + nv50_priv_tp_trap(priv, 8, 0x408e08, 0x408708, display, + "TRAP_PROP"); + nv_wr32(priv, 0x400108, 0x100); + status &= ~0x100; + } + + if (status) { + if (display) + nv_error(priv, "TRAP: unknown 0x%08x\n", status); + nv_wr32(priv, 0x400108, status); + } + + return 1; +} + +static void +nv50_gr_intr(struct nvkm_subdev *subdev) +{ + struct nvkm_fifo *pfifo = nvkm_fifo(subdev); + struct nvkm_engine *engine = nv_engine(subdev); + struct nvkm_object *engctx; + struct nvkm_handle *handle = NULL; + struct nv50_gr_priv *priv = (void *)subdev; + u32 stat = nv_rd32(priv, 0x400100); + u32 inst = nv_rd32(priv, 0x40032c) & 0x0fffffff; + u32 addr = nv_rd32(priv, 0x400704); + u32 subc = (addr & 0x00070000) >> 16; + u32 mthd = (addr & 0x00001ffc); + u32 data = nv_rd32(priv, 0x400708); + u32 class = nv_rd32(priv, 0x400814); + u32 show = stat, show_bitfield = stat; + int chid; + + engctx = nvkm_engctx_get(engine, inst); + chid = pfifo->chid(pfifo, engctx); + + if (stat & 0x00000010) { + handle = nvkm_handle_get_class(engctx, class); + if (handle && !nv_call(handle->object, mthd, data)) + show &= ~0x00000010; + nvkm_handle_put(handle); + } + + if (show & 0x00100000) { + u32 ecode = nv_rd32(priv, 0x400110); + nv_error(priv, "DATA_ERROR "); + nvkm_enum_print(nv50_data_error_names, ecode); + pr_cont("\n"); + show_bitfield &= ~0x00100000; + } + + if (stat & 0x00200000) { + if (!nv50_gr_trap_handler(priv, show, chid, (u64)inst << 12, + engctx)) + show &= ~0x00200000; + show_bitfield &= ~0x00200000; + } + + nv_wr32(priv, 0x400100, stat); + nv_wr32(priv, 0x400500, 0x00010001); + + if (show) { + show &= show_bitfield; + if (show) { + nv_error(priv, "%s", ""); + nvkm_bitfield_print(nv50_gr_intr_name, show); + pr_cont("\n"); + } + nv_error(priv, + "ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n", + chid, (u64)inst << 12, nvkm_client_name(engctx), + subc, class, mthd, data); + } + + if (nv_rd32(priv, 0x400824) & (1 << 31)) + nv_wr32(priv, 0x400824, nv_rd32(priv, 0x400824) & ~(1 << 31)); + + nvkm_engctx_put(engctx); +} + +static int +nv50_gr_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv50_gr_priv *priv; + int ret; + + ret = nvkm_gr_create(parent, engine, oclass, true, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00201000; + nv_subdev(priv)->intr = nv50_gr_intr; + nv_engine(priv)->cclass = &nv50_gr_cclass; + + priv->base.units = nv50_gr_units; + + switch (nv_device(priv)->chipset) { + case 0x50: + nv_engine(priv)->sclass = nv50_gr_sclass; + break; + case 0x84: + case 0x86: + case 0x92: + case 0x94: + case 0x96: + case 0x98: + nv_engine(priv)->sclass = g84_gr_sclass; + break; + case 0xa0: + case 0xaa: + case 0xac: + nv_engine(priv)->sclass = gt200_gr_sclass; + break; + case 0xa3: + case 0xa5: + case 0xa8: + nv_engine(priv)->sclass = gt215_gr_sclass; + break; + case 0xaf: + nv_engine(priv)->sclass = mcp89_gr_sclass; + break; + + } + + /* unfortunate hw bug workaround... */ + if (nv_device(priv)->chipset != 0x50 && + nv_device(priv)->chipset != 0xac) + nv_engine(priv)->tlb_flush = g84_gr_tlb_flush; + + spin_lock_init(&priv->lock); + return 0; +} + +static int +nv50_gr_init(struct nvkm_object *object) +{ + struct nv50_gr_priv *priv = (void *)object; + int ret, units, i; + + ret = nvkm_gr_init(&priv->base); + if (ret) + return ret; + + /* NV_PGRAPH_DEBUG_3_HW_CTX_SWITCH_ENABLED */ + nv_wr32(priv, 0x40008c, 0x00000004); + + /* reset/enable traps and interrupts */ + nv_wr32(priv, 0x400804, 0xc0000000); + nv_wr32(priv, 0x406800, 0xc0000000); + nv_wr32(priv, 0x400c04, 0xc0000000); + nv_wr32(priv, 0x401800, 0xc0000000); + nv_wr32(priv, 0x405018, 0xc0000000); + nv_wr32(priv, 0x402000, 0xc0000000); + + units = nv_rd32(priv, 0x001540); + for (i = 0; i < 16; i++) { + if (!(units & (1 << i))) + continue; + + if (nv_device(priv)->chipset < 0xa0) { + nv_wr32(priv, 0x408900 + (i << 12), 0xc0000000); + nv_wr32(priv, 0x408e08 + (i << 12), 0xc0000000); + nv_wr32(priv, 0x408314 + (i << 12), 0xc0000000); + } else { + nv_wr32(priv, 0x408600 + (i << 11), 0xc0000000); + nv_wr32(priv, 0x408708 + (i << 11), 0xc0000000); + nv_wr32(priv, 0x40831c + (i << 11), 0xc0000000); + } + } + + nv_wr32(priv, 0x400108, 0xffffffff); + nv_wr32(priv, 0x400138, 0xffffffff); + nv_wr32(priv, 0x400100, 0xffffffff); + nv_wr32(priv, 0x40013c, 0xffffffff); + nv_wr32(priv, 0x400500, 0x00010001); + + /* upload context program, initialise ctxctl defaults */ + ret = nv50_grctx_init(nv_device(priv), &priv->size); + if (ret) + return ret; + + nv_wr32(priv, 0x400824, 0x00000000); + nv_wr32(priv, 0x400828, 0x00000000); + nv_wr32(priv, 0x40082c, 0x00000000); + nv_wr32(priv, 0x400830, 0x00000000); + nv_wr32(priv, 0x40032c, 0x00000000); + nv_wr32(priv, 0x400330, 0x00000000); + + /* some unknown zcull magic */ + switch (nv_device(priv)->chipset & 0xf0) { + case 0x50: + case 0x80: + case 0x90: + nv_wr32(priv, 0x402ca8, 0x00000800); + break; + case 0xa0: + default: + if (nv_device(priv)->chipset == 0xa0 || + nv_device(priv)->chipset == 0xaa || + nv_device(priv)->chipset == 0xac) { + nv_wr32(priv, 0x402ca8, 0x00000802); + } else { + nv_wr32(priv, 0x402cc0, 0x00000000); + nv_wr32(priv, 0x402ca8, 0x00000002); + } + + break; + } + + /* zero out zcull regions */ + for (i = 0; i < 8; i++) { + nv_wr32(priv, 0x402c20 + (i * 0x10), 0x00000000); + nv_wr32(priv, 0x402c24 + (i * 0x10), 0x00000000); + nv_wr32(priv, 0x402c28 + (i * 0x10), 0x00000000); + nv_wr32(priv, 0x402c2c + (i * 0x10), 0x00000000); + } + return 0; +} + +struct nvkm_oclass +nv50_gr_oclass = { + .handle = NV_ENGINE(GR, 0x50), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_gr_ctor, + .dtor = _nvkm_gr_dtor, + .init = nv50_gr_init, + .fini = _nvkm_gr_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv50.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv50.h new file mode 100644 index 000000000..bcf786f6b --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv50.h @@ -0,0 +1,9 @@ +#ifndef __NV50_GR_H__ +#define __NV50_GR_H__ +#include +struct nvkm_device; +struct nvkm_gpuobj; + +int nv50_grctx_init(struct nvkm_device *, u32 *size); +void nv50_grctx_fill(struct nvkm_device *, struct nvkm_gpuobj *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/regs.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/regs.h new file mode 100644 index 000000000..90a9873ce --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/gr/regs.h @@ -0,0 +1,274 @@ +#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/kernel/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/Kbuild new file mode 100644 index 000000000..61b7b5f98 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/Kbuild @@ -0,0 +1,5 @@ +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/kernel/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/g84.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/g84.c new file mode 100644 index 000000000..0df889fa2 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/g84.c @@ -0,0 +1,94 @@ +/* + * 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 + +struct g84_mpeg_priv { + struct nvkm_mpeg base; +}; + +struct g84_mpeg_chan { + struct nvkm_mpeg_chan base; +}; + +/******************************************************************************* + * MPEG object classes + ******************************************************************************/ + +static struct nvkm_oclass +g84_mpeg_sclass[] = { + { 0x8274, &nv50_mpeg_ofuncs }, + {} +}; + +/******************************************************************************* + * PMPEG context + ******************************************************************************/ + +static struct nvkm_oclass +g84_mpeg_cclass = { + .handle = NV_ENGCTX(MPEG, 0x84), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_mpeg_context_ctor, + .dtor = _nvkm_mpeg_context_dtor, + .init = _nvkm_mpeg_context_init, + .fini = _nvkm_mpeg_context_fini, + .rd32 = _nvkm_mpeg_context_rd32, + .wr32 = _nvkm_mpeg_context_wr32, + }, +}; + +/******************************************************************************* + * PMPEG engine/subdev functions + ******************************************************************************/ + +static int +g84_mpeg_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct g84_mpeg_priv *priv; + int ret; + + ret = nvkm_mpeg_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00000002; + nv_subdev(priv)->intr = nv50_mpeg_intr; + nv_engine(priv)->cclass = &g84_mpeg_cclass; + nv_engine(priv)->sclass = g84_mpeg_sclass; + return 0; +} + +struct nvkm_oclass +g84_mpeg_oclass = { + .handle = NV_ENGINE(MPEG, 0x84), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = g84_mpeg_ctor, + .dtor = _nvkm_mpeg_dtor, + .init = nv50_mpeg_init, + .fini = _nvkm_mpeg_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv31.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv31.c new file mode 100644 index 000000000..b5bef0718 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv31.c @@ -0,0 +1,304 @@ +/* + * 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_ctor(struct nvkm_object *parent, + struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_gpuobj *obj; + int ret; + + ret = nvkm_gpuobj_create(parent, engine, oclass, 0, parent, + 20, 16, 0, &obj); + *pobject = nv_object(obj); + if (ret) + return ret; + + nv_wo32(obj, 0x00, nv_mclass(obj)); + nv_wo32(obj, 0x04, 0x00000000); + nv_wo32(obj, 0x08, 0x00000000); + nv_wo32(obj, 0x0c, 0x00000000); + return 0; +} + +static int +nv31_mpeg_mthd_dma(struct nvkm_object *object, u32 mthd, void *arg, u32 len) +{ + struct nvkm_instmem *imem = nvkm_instmem(object); + struct nv31_mpeg_priv *priv = (void *)object->engine; + u32 inst = *(u32 *)arg << 4; + u32 dma0 = nv_ro32(imem, inst + 0); + u32 dma1 = nv_ro32(imem, inst + 4); + u32 dma2 = nv_ro32(imem, inst + 8); + u32 base = (dma2 & 0xfffff000) | (dma0 >> 20); + u32 size = dma1 + 1; + + /* only allow linear DMA objects */ + if (!(dma0 & 0x00002000)) + return -EINVAL; + + if (mthd == 0x0190) { + /* DMA_CMD */ + nv_mask(priv, 0x00b300, 0x00010000, (dma0 & 0x00030000) ? 0x00010000 : 0); + nv_wr32(priv, 0x00b334, base); + nv_wr32(priv, 0x00b324, size); + } else + if (mthd == 0x01a0) { + /* DMA_DATA */ + nv_mask(priv, 0x00b300, 0x00020000, (dma0 & 0x00030000) ? 0x00020000 : 0); + nv_wr32(priv, 0x00b360, base); + nv_wr32(priv, 0x00b364, size); + } else { + /* DMA_IMAGE, VRAM only */ + if (dma0 & 0x00030000) + return -EINVAL; + + nv_wr32(priv, 0x00b370, base); + nv_wr32(priv, 0x00b374, size); + } + + return 0; +} + +struct nvkm_ofuncs +nv31_mpeg_ofuncs = { + .ctor = nv31_mpeg_object_ctor, + .dtor = _nvkm_gpuobj_dtor, + .init = _nvkm_gpuobj_init, + .fini = _nvkm_gpuobj_fini, + .rd32 = _nvkm_gpuobj_rd32, + .wr32 = _nvkm_gpuobj_wr32, +}; + +static struct nvkm_omthds +nv31_mpeg_omthds[] = { + { 0x0190, 0x0190, nv31_mpeg_mthd_dma }, + { 0x01a0, 0x01a0, nv31_mpeg_mthd_dma }, + { 0x01b0, 0x01b0, nv31_mpeg_mthd_dma }, + {} +}; + +struct nvkm_oclass +nv31_mpeg_sclass[] = { + { 0x3174, &nv31_mpeg_ofuncs, nv31_mpeg_omthds }, + {} +}; + +/******************************************************************************* + * PMPEG context + ******************************************************************************/ + +static int +nv31_mpeg_context_ctor(struct nvkm_object *parent, + struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv31_mpeg_priv *priv = (void *)engine; + struct nv31_mpeg_chan *chan; + unsigned long flags; + int ret; + + ret = nvkm_object_create(parent, engine, oclass, 0, &chan); + *pobject = nv_object(chan); + if (ret) + return ret; + + spin_lock_irqsave(&nv_engine(priv)->lock, flags); + if (priv->chan) { + spin_unlock_irqrestore(&nv_engine(priv)->lock, flags); + nvkm_object_destroy(&chan->base); + *pobject = NULL; + return -EBUSY; + } + priv->chan = chan; + spin_unlock_irqrestore(&nv_engine(priv)->lock, flags); + return 0; +} + +static void +nv31_mpeg_context_dtor(struct nvkm_object *object) +{ + struct nv31_mpeg_priv *priv = (void *)object->engine; + struct nv31_mpeg_chan *chan = (void *)object; + unsigned long flags; + + spin_lock_irqsave(&nv_engine(priv)->lock, flags); + priv->chan = NULL; + spin_unlock_irqrestore(&nv_engine(priv)->lock, flags); + nvkm_object_destroy(&chan->base); +} + +struct nvkm_oclass +nv31_mpeg_cclass = { + .handle = NV_ENGCTX(MPEG, 0x31), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv31_mpeg_context_ctor, + .dtor = nv31_mpeg_context_dtor, + .init = nvkm_object_init, + .fini = nvkm_object_fini, + }, +}; + +/******************************************************************************* + * PMPEG engine/subdev functions + ******************************************************************************/ + +void +nv31_mpeg_tile_prog(struct nvkm_engine *engine, int i) +{ + struct nvkm_fb_tile *tile = &nvkm_fb(engine)->tile.region[i]; + struct nv31_mpeg_priv *priv = (void *)engine; + + nv_wr32(priv, 0x00b008 + (i * 0x10), tile->pitch); + nv_wr32(priv, 0x00b004 + (i * 0x10), tile->limit); + nv_wr32(priv, 0x00b000 + (i * 0x10), tile->addr); +} + +void +nv31_mpeg_intr(struct nvkm_subdev *subdev) +{ + struct nv31_mpeg_priv *priv = (void *)subdev; + struct nvkm_fifo *pfifo = nvkm_fifo(subdev); + struct nvkm_handle *handle; + struct nvkm_object *engctx; + u32 stat = nv_rd32(priv, 0x00b100); + u32 type = nv_rd32(priv, 0x00b230); + u32 mthd = nv_rd32(priv, 0x00b234); + u32 data = nv_rd32(priv, 0x00b238); + u32 show = stat; + unsigned long flags; + + spin_lock_irqsave(&nv_engine(priv)->lock, flags); + engctx = nv_object(priv->chan); + + if (stat & 0x01000000) { + /* happens on initial binding of the object */ + if (type == 0x00000020 && mthd == 0x0000) { + nv_mask(priv, 0x00b308, 0x00000000, 0x00000000); + show &= ~0x01000000; + } + + if (type == 0x00000010 && engctx) { + handle = nvkm_handle_get_class(engctx, 0x3174); + if (handle && !nv_call(handle->object, mthd, data)) + show &= ~0x01000000; + nvkm_handle_put(handle); + } + } + + nv_wr32(priv, 0x00b100, stat); + nv_wr32(priv, 0x00b230, 0x00000001); + + if (show) { + nv_error(priv, "ch %d [%s] 0x%08x 0x%08x 0x%08x 0x%08x\n", + pfifo->chid(pfifo, engctx), + nvkm_client_name(engctx), stat, type, mthd, data); + } + + spin_unlock_irqrestore(&nv_engine(priv)->lock, flags); +} + +static int +nv31_mpeg_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv31_mpeg_priv *priv; + int ret; + + ret = nvkm_mpeg_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00000002; + nv_subdev(priv)->intr = nv31_mpeg_intr; + nv_engine(priv)->cclass = &nv31_mpeg_cclass; + nv_engine(priv)->sclass = nv31_mpeg_sclass; + nv_engine(priv)->tile_prog = nv31_mpeg_tile_prog; + return 0; +} + +int +nv31_mpeg_init(struct nvkm_object *object) +{ + struct nvkm_engine *engine = nv_engine(object); + struct nv31_mpeg_priv *priv = (void *)object; + struct nvkm_fb *pfb = nvkm_fb(object); + int ret, i; + + ret = nvkm_mpeg_init(&priv->base); + if (ret) + return ret; + + /* VPE init */ + nv_wr32(priv, 0x00b0e0, 0x00000020); /* nvidia: rd 0x01, wr 0x20 */ + nv_wr32(priv, 0x00b0e8, 0x00000020); /* nvidia: rd 0x01, wr 0x20 */ + + for (i = 0; i < pfb->tile.regions; i++) + engine->tile_prog(engine, i); + + /* PMPEG init */ + nv_wr32(priv, 0x00b32c, 0x00000000); + nv_wr32(priv, 0x00b314, 0x00000100); + nv_wr32(priv, 0x00b220, 0x00000031); + nv_wr32(priv, 0x00b300, 0x02001ec1); + nv_mask(priv, 0x00b32c, 0x00000001, 0x00000001); + + nv_wr32(priv, 0x00b100, 0xffffffff); + nv_wr32(priv, 0x00b140, 0xffffffff); + + if (!nv_wait(priv, 0x00b200, 0x00000001, 0x00000000)) { + nv_error(priv, "timeout 0x%08x\n", nv_rd32(priv, 0x00b200)); + return -EBUSY; + } + + return 0; +} + +struct nvkm_oclass +nv31_mpeg_oclass = { + .handle = NV_ENGINE(MPEG, 0x31), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv31_mpeg_ctor, + .dtor = _nvkm_mpeg_dtor, + .init = nv31_mpeg_init, + .fini = _nvkm_mpeg_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv31.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv31.h new file mode 100644 index 000000000..782b796d7 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv31.h @@ -0,0 +1,13 @@ +#ifndef __NV31_MPEG_H__ +#define __NV31_MPEG_H__ +#include + +struct nv31_mpeg_chan { + struct nvkm_object base; +}; + +struct nv31_mpeg_priv { + struct nvkm_mpeg base; + struct nv31_mpeg_chan *chan; +}; +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv40.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv40.c new file mode 100644 index 000000000..9508bf9e1 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv40.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 "nv31.h" + +#include + +/******************************************************************************* + * MPEG object classes + ******************************************************************************/ + +static int +nv40_mpeg_mthd_dma(struct nvkm_object *object, u32 mthd, void *arg, u32 len) +{ + struct nvkm_instmem *imem = nvkm_instmem(object); + struct nv31_mpeg_priv *priv = (void *)object->engine; + u32 inst = *(u32 *)arg << 4; + u32 dma0 = nv_ro32(imem, inst + 0); + u32 dma1 = nv_ro32(imem, inst + 4); + u32 dma2 = nv_ro32(imem, inst + 8); + u32 base = (dma2 & 0xfffff000) | (dma0 >> 20); + u32 size = dma1 + 1; + + /* only allow linear DMA objects */ + if (!(dma0 & 0x00002000)) + return -EINVAL; + + if (mthd == 0x0190) { + /* DMA_CMD */ + nv_mask(priv, 0x00b300, 0x00030000, (dma0 & 0x00030000)); + nv_wr32(priv, 0x00b334, base); + nv_wr32(priv, 0x00b324, size); + } else + if (mthd == 0x01a0) { + /* DMA_DATA */ + nv_mask(priv, 0x00b300, 0x000c0000, (dma0 & 0x00030000) << 2); + nv_wr32(priv, 0x00b360, base); + nv_wr32(priv, 0x00b364, size); + } else { + /* DMA_IMAGE, VRAM only */ + if (dma0 & 0x00030000) + return -EINVAL; + + nv_wr32(priv, 0x00b370, base); + nv_wr32(priv, 0x00b374, size); + } + + return 0; +} + +static struct nvkm_omthds +nv40_mpeg_omthds[] = { + { 0x0190, 0x0190, nv40_mpeg_mthd_dma }, + { 0x01a0, 0x01a0, nv40_mpeg_mthd_dma }, + { 0x01b0, 0x01b0, nv40_mpeg_mthd_dma }, + {} +}; + +struct nvkm_oclass +nv40_mpeg_sclass[] = { + { 0x3174, &nv31_mpeg_ofuncs, nv40_mpeg_omthds }, + {} +}; + +/******************************************************************************* + * PMPEG engine/subdev functions + ******************************************************************************/ + +static void +nv40_mpeg_intr(struct nvkm_subdev *subdev) +{ + struct nv31_mpeg_priv *priv = (void *)subdev; + u32 stat; + + if ((stat = nv_rd32(priv, 0x00b100))) + nv31_mpeg_intr(subdev); + + if ((stat = nv_rd32(priv, 0x00b800))) { + nv_error(priv, "PMSRCH 0x%08x\n", stat); + nv_wr32(priv, 0x00b800, stat); + } +} + +static int +nv40_mpeg_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv31_mpeg_priv *priv; + int ret; + + ret = nvkm_mpeg_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00000002; + nv_subdev(priv)->intr = nv40_mpeg_intr; + nv_engine(priv)->cclass = &nv31_mpeg_cclass; + nv_engine(priv)->sclass = nv40_mpeg_sclass; + nv_engine(priv)->tile_prog = nv31_mpeg_tile_prog; + return 0; +} + +struct nvkm_oclass +nv40_mpeg_oclass = { + .handle = NV_ENGINE(MPEG, 0x40), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv40_mpeg_ctor, + .dtor = _nvkm_mpeg_dtor, + .init = nv31_mpeg_init, + .fini = _nvkm_mpeg_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv44.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv44.c new file mode 100644 index 000000000..4720ac884 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv44.c @@ -0,0 +1,185 @@ +/* + * 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 + +struct nv44_mpeg_priv { + struct nvkm_mpeg base; +}; + +struct nv44_mpeg_chan { + struct nvkm_mpeg_chan base; +}; + +/******************************************************************************* + * PMPEG context + ******************************************************************************/ + +static int +nv44_mpeg_context_ctor(struct nvkm_object *parent, + struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv44_mpeg_chan *chan; + int ret; + + ret = nvkm_mpeg_context_create(parent, engine, oclass, NULL, 264 * 4, + 16, NVOBJ_FLAG_ZERO_ALLOC, &chan); + *pobject = nv_object(chan); + if (ret) + return ret; + + nv_wo32(&chan->base.base, 0x78, 0x02001ec1); + return 0; +} + +static int +nv44_mpeg_context_fini(struct nvkm_object *object, bool suspend) +{ + + struct nv44_mpeg_priv *priv = (void *)object->engine; + struct nv44_mpeg_chan *chan = (void *)object; + u32 inst = 0x80000000 | nv_gpuobj(chan)->addr >> 4; + + nv_mask(priv, 0x00b32c, 0x00000001, 0x00000000); + if (nv_rd32(priv, 0x00b318) == inst) + nv_mask(priv, 0x00b318, 0x80000000, 0x00000000); + nv_mask(priv, 0x00b32c, 0x00000001, 0x00000001); + return 0; +} + +static struct nvkm_oclass +nv44_mpeg_cclass = { + .handle = NV_ENGCTX(MPEG, 0x44), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv44_mpeg_context_ctor, + .dtor = _nvkm_mpeg_context_dtor, + .init = _nvkm_mpeg_context_init, + .fini = nv44_mpeg_context_fini, + .rd32 = _nvkm_mpeg_context_rd32, + .wr32 = _nvkm_mpeg_context_wr32, + }, +}; + +/******************************************************************************* + * PMPEG engine/subdev functions + ******************************************************************************/ + +static void +nv44_mpeg_intr(struct nvkm_subdev *subdev) +{ + struct nvkm_fifo *pfifo = nvkm_fifo(subdev); + struct nvkm_engine *engine = nv_engine(subdev); + struct nvkm_object *engctx; + struct nvkm_handle *handle; + struct nv44_mpeg_priv *priv = (void *)subdev; + u32 inst = nv_rd32(priv, 0x00b318) & 0x000fffff; + u32 stat = nv_rd32(priv, 0x00b100); + u32 type = nv_rd32(priv, 0x00b230); + u32 mthd = nv_rd32(priv, 0x00b234); + u32 data = nv_rd32(priv, 0x00b238); + u32 show = stat; + int chid; + + engctx = nvkm_engctx_get(engine, inst); + chid = pfifo->chid(pfifo, engctx); + + if (stat & 0x01000000) { + /* happens on initial binding of the object */ + if (type == 0x00000020 && mthd == 0x0000) { + nv_mask(priv, 0x00b308, 0x00000000, 0x00000000); + show &= ~0x01000000; + } + + if (type == 0x00000010) { + handle = nvkm_handle_get_class(engctx, 0x3174); + if (handle && !nv_call(handle->object, mthd, data)) + show &= ~0x01000000; + nvkm_handle_put(handle); + } + } + + nv_wr32(priv, 0x00b100, stat); + nv_wr32(priv, 0x00b230, 0x00000001); + + if (show) { + nv_error(priv, + "ch %d [0x%08x %s] 0x%08x 0x%08x 0x%08x 0x%08x\n", + chid, inst << 4, nvkm_client_name(engctx), stat, + type, mthd, data); + } + + nvkm_engctx_put(engctx); +} + +static void +nv44_mpeg_me_intr(struct nvkm_subdev *subdev) +{ + struct nv44_mpeg_priv *priv = (void *)subdev; + u32 stat; + + if ((stat = nv_rd32(priv, 0x00b100))) + nv44_mpeg_intr(subdev); + + if ((stat = nv_rd32(priv, 0x00b800))) { + nv_error(priv, "PMSRCH 0x%08x\n", stat); + nv_wr32(priv, 0x00b800, stat); + } +} + +static int +nv44_mpeg_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv44_mpeg_priv *priv; + int ret; + + ret = nvkm_mpeg_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00000002; + nv_subdev(priv)->intr = nv44_mpeg_me_intr; + nv_engine(priv)->cclass = &nv44_mpeg_cclass; + nv_engine(priv)->sclass = nv40_mpeg_sclass; + nv_engine(priv)->tile_prog = nv31_mpeg_tile_prog; + return 0; +} + +struct nvkm_oclass +nv44_mpeg_oclass = { + .handle = NV_ENGINE(MPEG, 0x44), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv44_mpeg_ctor, + .dtor = _nvkm_mpeg_dtor, + .init = nv31_mpeg_init, + .fini = _nvkm_mpeg_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv50.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv50.c new file mode 100644 index 000000000..b3463f373 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv50.c @@ -0,0 +1,225 @@ +/* + * 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 nv50_mpeg_priv { + struct nvkm_mpeg base; +}; + +struct nv50_mpeg_chan { + struct nvkm_mpeg_chan base; +}; + +/******************************************************************************* + * MPEG object classes + ******************************************************************************/ + +static int +nv50_mpeg_object_ctor(struct nvkm_object *parent, + struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_gpuobj *obj; + int ret; + + ret = nvkm_gpuobj_create(parent, engine, oclass, 0, parent, + 16, 16, 0, &obj); + *pobject = nv_object(obj); + if (ret) + return ret; + + nv_wo32(obj, 0x00, nv_mclass(obj)); + nv_wo32(obj, 0x04, 0x00000000); + nv_wo32(obj, 0x08, 0x00000000); + nv_wo32(obj, 0x0c, 0x00000000); + return 0; +} + +struct nvkm_ofuncs +nv50_mpeg_ofuncs = { + .ctor = nv50_mpeg_object_ctor, + .dtor = _nvkm_gpuobj_dtor, + .init = _nvkm_gpuobj_init, + .fini = _nvkm_gpuobj_fini, + .rd32 = _nvkm_gpuobj_rd32, + .wr32 = _nvkm_gpuobj_wr32, +}; + +static struct nvkm_oclass +nv50_mpeg_sclass[] = { + { 0x3174, &nv50_mpeg_ofuncs }, + {} +}; + +/******************************************************************************* + * PMPEG context + ******************************************************************************/ + +int +nv50_mpeg_context_ctor(struct nvkm_object *parent, + struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_bar *bar = nvkm_bar(parent); + struct nv50_mpeg_chan *chan; + int ret; + + ret = nvkm_mpeg_context_create(parent, engine, oclass, NULL, 128 * 4, + 0, NVOBJ_FLAG_ZERO_ALLOC, &chan); + *pobject = nv_object(chan); + if (ret) + return ret; + + nv_wo32(chan, 0x0070, 0x00801ec1); + nv_wo32(chan, 0x007c, 0x0000037c); + bar->flush(bar); + return 0; +} + +static struct nvkm_oclass +nv50_mpeg_cclass = { + .handle = NV_ENGCTX(MPEG, 0x50), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_mpeg_context_ctor, + .dtor = _nvkm_mpeg_context_dtor, + .init = _nvkm_mpeg_context_init, + .fini = _nvkm_mpeg_context_fini, + .rd32 = _nvkm_mpeg_context_rd32, + .wr32 = _nvkm_mpeg_context_wr32, + }, +}; + +/******************************************************************************* + * PMPEG engine/subdev functions + ******************************************************************************/ + +void +nv50_mpeg_intr(struct nvkm_subdev *subdev) +{ + struct nv50_mpeg_priv *priv = (void *)subdev; + u32 stat = nv_rd32(priv, 0x00b100); + u32 type = nv_rd32(priv, 0x00b230); + u32 mthd = nv_rd32(priv, 0x00b234); + u32 data = nv_rd32(priv, 0x00b238); + u32 show = stat; + + if (stat & 0x01000000) { + /* happens on initial binding of the object */ + if (type == 0x00000020 && mthd == 0x0000) { + nv_wr32(priv, 0x00b308, 0x00000100); + show &= ~0x01000000; + } + } + + if (show) { + nv_info(priv, "0x%08x 0x%08x 0x%08x 0x%08x\n", + stat, type, mthd, data); + } + + nv_wr32(priv, 0x00b100, stat); + nv_wr32(priv, 0x00b230, 0x00000001); +} + +static void +nv50_vpe_intr(struct nvkm_subdev *subdev) +{ + struct nv50_mpeg_priv *priv = (void *)subdev; + + if (nv_rd32(priv, 0x00b100)) + nv50_mpeg_intr(subdev); + + if (nv_rd32(priv, 0x00b800)) { + u32 stat = nv_rd32(priv, 0x00b800); + nv_info(priv, "PMSRCH: 0x%08x\n", stat); + nv_wr32(priv, 0xb800, stat); + } +} + +static int +nv50_mpeg_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv50_mpeg_priv *priv; + int ret; + + ret = nvkm_mpeg_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00400002; + nv_subdev(priv)->intr = nv50_vpe_intr; + nv_engine(priv)->cclass = &nv50_mpeg_cclass; + nv_engine(priv)->sclass = nv50_mpeg_sclass; + return 0; +} + +int +nv50_mpeg_init(struct nvkm_object *object) +{ + struct nv50_mpeg_priv *priv = (void *)object; + int ret; + + ret = nvkm_mpeg_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, 0x00b32c, 0x00000000); + nv_wr32(priv, 0x00b314, 0x00000100); + nv_wr32(priv, 0x00b0e0, 0x0000001a); + + nv_wr32(priv, 0x00b220, 0x00000044); + nv_wr32(priv, 0x00b300, 0x00801ec1); + nv_wr32(priv, 0x00b390, 0x00000000); + nv_wr32(priv, 0x00b394, 0x00000000); + nv_wr32(priv, 0x00b398, 0x00000000); + nv_mask(priv, 0x00b32c, 0x00000001, 0x00000001); + + nv_wr32(priv, 0x00b100, 0xffffffff); + nv_wr32(priv, 0x00b140, 0xffffffff); + + if (!nv_wait(priv, 0x00b200, 0x00000001, 0x00000000)) { + nv_error(priv, "timeout 0x%08x\n", nv_rd32(priv, 0x00b200)); + return -EBUSY; + } + + return 0; +} + +struct nvkm_oclass +nv50_mpeg_oclass = { + .handle = NV_ENGINE(MPEG, 0x50), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_mpeg_ctor, + .dtor = _nvkm_mpeg_dtor, + .init = nv50_mpeg_init, + .fini = _nvkm_mpeg_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/mspdec/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/mspdec/Kbuild new file mode 100644 index 000000000..c59c83a67 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/mspdec/Kbuild @@ -0,0 +1,3 @@ +nvkm-y += nvkm/engine/mspdec/g98.o +nvkm-y += nvkm/engine/mspdec/gf100.o +nvkm-y += nvkm/engine/mspdec/gk104.o diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/mspdec/g98.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/mspdec/g98.c new file mode 100644 index 000000000..217457779 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/mspdec/g98.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, Maarten Lankhorst, Ilia Mirkin + */ +#include +#include + +struct g98_mspdec_priv { + struct nvkm_falcon base; +}; + +/******************************************************************************* + * MSPDEC object classes + ******************************************************************************/ + +static struct nvkm_oclass +g98_mspdec_sclass[] = { + { 0x88b2, &nvkm_object_ofuncs }, + { 0x85b2, &nvkm_object_ofuncs }, + {}, +}; + +/******************************************************************************* + * PMSPDEC context + ******************************************************************************/ + +static struct nvkm_oclass +g98_mspdec_cclass = { + .handle = NV_ENGCTX(MSPDEC, 0x98), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_falcon_context_ctor, + .dtor = _nvkm_falcon_context_dtor, + .init = _nvkm_falcon_context_init, + .fini = _nvkm_falcon_context_fini, + .rd32 = _nvkm_falcon_context_rd32, + .wr32 = _nvkm_falcon_context_wr32, + }, +}; + +/******************************************************************************* + * PMSPDEC engine/subdev functions + ******************************************************************************/ + +static int +g98_mspdec_init(struct nvkm_object *object) +{ + struct g98_mspdec_priv *priv = (void *)object; + int ret; + + ret = nvkm_falcon_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, 0x085010, 0x0000ffd2); + nv_wr32(priv, 0x08501c, 0x0000fff2); + return 0; +} + +static int +g98_mspdec_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct g98_mspdec_priv *priv; + int ret; + + ret = nvkm_falcon_create(parent, engine, oclass, 0x085000, true, + "PMSPDEC", "mspdec", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x01020000; + nv_engine(priv)->cclass = &g98_mspdec_cclass; + nv_engine(priv)->sclass = g98_mspdec_sclass; + return 0; +} + +struct nvkm_oclass +g98_mspdec_oclass = { + .handle = NV_ENGINE(MSPDEC, 0x98), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = g98_mspdec_ctor, + .dtor = _nvkm_falcon_dtor, + .init = g98_mspdec_init, + .fini = _nvkm_falcon_fini, + .rd32 = _nvkm_falcon_rd32, + .wr32 = _nvkm_falcon_wr32, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/mspdec/gf100.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/mspdec/gf100.c new file mode 100644 index 000000000..c814a5f65 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/mspdec/gf100.c @@ -0,0 +1,109 @@ +/* + * 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 +#include + +struct gf100_mspdec_priv { + struct nvkm_falcon base; +}; + +/******************************************************************************* + * MSPDEC object classes + ******************************************************************************/ + +static struct nvkm_oclass +gf100_mspdec_sclass[] = { + { 0x90b2, &nvkm_object_ofuncs }, + {}, +}; + +/******************************************************************************* + * PMSPDEC context + ******************************************************************************/ + +static struct nvkm_oclass +gf100_mspdec_cclass = { + .handle = NV_ENGCTX(MSPDEC, 0xc0), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_falcon_context_ctor, + .dtor = _nvkm_falcon_context_dtor, + .init = _nvkm_falcon_context_init, + .fini = _nvkm_falcon_context_fini, + .rd32 = _nvkm_falcon_context_rd32, + .wr32 = _nvkm_falcon_context_wr32, + }, +}; + +/******************************************************************************* + * PMSPDEC engine/subdev functions + ******************************************************************************/ + +static int +gf100_mspdec_init(struct nvkm_object *object) +{ + struct gf100_mspdec_priv *priv = (void *)object; + int ret; + + ret = nvkm_falcon_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, 0x085010, 0x0000fff2); + nv_wr32(priv, 0x08501c, 0x0000fff2); + return 0; +} + +static int +gf100_mspdec_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gf100_mspdec_priv *priv; + int ret; + + ret = nvkm_falcon_create(parent, engine, oclass, 0x085000, true, + "PMSPDEC", "mspdec", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00020000; + nv_subdev(priv)->intr = nvkm_falcon_intr; + nv_engine(priv)->cclass = &gf100_mspdec_cclass; + nv_engine(priv)->sclass = gf100_mspdec_sclass; + return 0; +} + +struct nvkm_oclass +gf100_mspdec_oclass = { + .handle = NV_ENGINE(MSPDEC, 0xc0), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_mspdec_ctor, + .dtor = _nvkm_falcon_dtor, + .init = gf100_mspdec_init, + .fini = _nvkm_falcon_fini, + .rd32 = _nvkm_falcon_rd32, + .wr32 = _nvkm_falcon_wr32, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/mspdec/gk104.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/mspdec/gk104.c new file mode 100644 index 000000000..979920650 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/mspdec/gk104.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 +#include + +struct gk104_mspdec_priv { + struct nvkm_falcon base; +}; + +/******************************************************************************* + * MSPDEC object classes + ******************************************************************************/ + +static struct nvkm_oclass +gk104_mspdec_sclass[] = { + { 0x95b2, &nvkm_object_ofuncs }, + {}, +}; + +/******************************************************************************* + * PMSPDEC context + ******************************************************************************/ + +static struct nvkm_oclass +gk104_mspdec_cclass = { + .handle = NV_ENGCTX(MSPDEC, 0xe0), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_falcon_context_ctor, + .dtor = _nvkm_falcon_context_dtor, + .init = _nvkm_falcon_context_init, + .fini = _nvkm_falcon_context_fini, + .rd32 = _nvkm_falcon_context_rd32, + .wr32 = _nvkm_falcon_context_wr32, + }, +}; + +/******************************************************************************* + * PMSPDEC engine/subdev functions + ******************************************************************************/ + +static int +gk104_mspdec_init(struct nvkm_object *object) +{ + struct gk104_mspdec_priv *priv = (void *)object; + int ret; + + ret = nvkm_falcon_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, 0x085010, 0x0000fff2); + nv_wr32(priv, 0x08501c, 0x0000fff2); + return 0; +} + +static int +gk104_mspdec_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gk104_mspdec_priv *priv; + int ret; + + ret = nvkm_falcon_create(parent, engine, oclass, 0x085000, true, + "PMSPDEC", "mspdec", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00020000; + nv_subdev(priv)->intr = nvkm_falcon_intr; + nv_engine(priv)->cclass = &gk104_mspdec_cclass; + nv_engine(priv)->sclass = gk104_mspdec_sclass; + return 0; +} + +struct nvkm_oclass +gk104_mspdec_oclass = { + .handle = NV_ENGINE(MSPDEC, 0xe0), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gk104_mspdec_ctor, + .dtor = _nvkm_falcon_dtor, + .init = gk104_mspdec_init, + .fini = _nvkm_falcon_fini, + .rd32 = _nvkm_falcon_rd32, + .wr32 = _nvkm_falcon_wr32, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/msppp/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/msppp/Kbuild new file mode 100644 index 000000000..4576a9eee --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/msppp/Kbuild @@ -0,0 +1,2 @@ +nvkm-y += nvkm/engine/msppp/g98.o +nvkm-y += nvkm/engine/msppp/gf100.o diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/msppp/g98.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/msppp/g98.c new file mode 100644 index 000000000..7a602a2de --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/msppp/g98.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, Maarten Lankhorst, Ilia Mirkin + */ +#include +#include + +struct g98_msppp_priv { + struct nvkm_falcon base; +}; + +/******************************************************************************* + * MSPPP object classes + ******************************************************************************/ + +static struct nvkm_oclass +g98_msppp_sclass[] = { + { 0x88b3, &nvkm_object_ofuncs }, + { 0x85b3, &nvkm_object_ofuncs }, + {}, +}; + +/******************************************************************************* + * PMSPPP context + ******************************************************************************/ + +static struct nvkm_oclass +g98_msppp_cclass = { + .handle = NV_ENGCTX(MSPPP, 0x98), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_falcon_context_ctor, + .dtor = _nvkm_falcon_context_dtor, + .init = _nvkm_falcon_context_init, + .fini = _nvkm_falcon_context_fini, + .rd32 = _nvkm_falcon_context_rd32, + .wr32 = _nvkm_falcon_context_wr32, + }, +}; + +/******************************************************************************* + * PMSPPP engine/subdev functions + ******************************************************************************/ + +static int +g98_msppp_init(struct nvkm_object *object) +{ + struct g98_msppp_priv *priv = (void *)object; + int ret; + + ret = nvkm_falcon_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, 0x086010, 0x0000ffd2); + nv_wr32(priv, 0x08601c, 0x0000fff2); + return 0; +} + +static int +g98_msppp_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct g98_msppp_priv *priv; + int ret; + + ret = nvkm_falcon_create(parent, engine, oclass, 0x086000, true, + "PMSPPP", "msppp", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00400002; + nv_engine(priv)->cclass = &g98_msppp_cclass; + nv_engine(priv)->sclass = g98_msppp_sclass; + return 0; +} + +struct nvkm_oclass +g98_msppp_oclass = { + .handle = NV_ENGINE(MSPPP, 0x98), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = g98_msppp_ctor, + .dtor = _nvkm_falcon_dtor, + .init = g98_msppp_init, + .fini = _nvkm_falcon_fini, + .rd32 = _nvkm_falcon_rd32, + .wr32 = _nvkm_falcon_wr32, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/msppp/gf100.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/msppp/gf100.c new file mode 100644 index 000000000..6047baee1 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/msppp/gf100.c @@ -0,0 +1,109 @@ +/* + * 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 +#include + +struct gf100_msppp_priv { + struct nvkm_falcon base; +}; + +/******************************************************************************* + * MSPPP object classes + ******************************************************************************/ + +static struct nvkm_oclass +gf100_msppp_sclass[] = { + { 0x90b3, &nvkm_object_ofuncs }, + {}, +}; + +/******************************************************************************* + * PMSPPP context + ******************************************************************************/ + +static struct nvkm_oclass +gf100_msppp_cclass = { + .handle = NV_ENGCTX(MSPPP, 0xc0), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_falcon_context_ctor, + .dtor = _nvkm_falcon_context_dtor, + .init = _nvkm_falcon_context_init, + .fini = _nvkm_falcon_context_fini, + .rd32 = _nvkm_falcon_context_rd32, + .wr32 = _nvkm_falcon_context_wr32, + }, +}; + +/******************************************************************************* + * PMSPPP engine/subdev functions + ******************************************************************************/ + +static int +gf100_msppp_init(struct nvkm_object *object) +{ + struct gf100_msppp_priv *priv = (void *)object; + int ret; + + ret = nvkm_falcon_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, 0x086010, 0x0000fff2); + nv_wr32(priv, 0x08601c, 0x0000fff2); + return 0; +} + +static int +gf100_msppp_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gf100_msppp_priv *priv; + int ret; + + ret = nvkm_falcon_create(parent, engine, oclass, 0x086000, true, + "PMSPPP", "msppp", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00000002; + nv_subdev(priv)->intr = nvkm_falcon_intr; + nv_engine(priv)->cclass = &gf100_msppp_cclass; + nv_engine(priv)->sclass = gf100_msppp_sclass; + return 0; +} + +struct nvkm_oclass +gf100_msppp_oclass = { + .handle = NV_ENGINE(MSPPP, 0xc0), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_msppp_ctor, + .dtor = _nvkm_falcon_dtor, + .init = gf100_msppp_init, + .fini = _nvkm_falcon_fini, + .rd32 = _nvkm_falcon_rd32, + .wr32 = _nvkm_falcon_wr32, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/msvld/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/msvld/Kbuild new file mode 100644 index 000000000..0c9811009 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/msvld/Kbuild @@ -0,0 +1,3 @@ +nvkm-y += nvkm/engine/msvld/g98.o +nvkm-y += nvkm/engine/msvld/gf100.o +nvkm-y += nvkm/engine/msvld/gk104.o diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/msvld/g98.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/msvld/g98.c new file mode 100644 index 000000000..c8a6b4ef5 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/msvld/g98.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, Maarten Lankhorst, Ilia Mirkin + */ +#include +#include + +struct g98_msvld_priv { + struct nvkm_falcon base; +}; + +/******************************************************************************* + * MSVLD object classes + ******************************************************************************/ + +static struct nvkm_oclass +g98_msvld_sclass[] = { + { 0x88b1, &nvkm_object_ofuncs }, + { 0x85b1, &nvkm_object_ofuncs }, + { 0x86b1, &nvkm_object_ofuncs }, + {}, +}; + +/******************************************************************************* + * PMSVLD context + ******************************************************************************/ + +static struct nvkm_oclass +g98_msvld_cclass = { + .handle = NV_ENGCTX(MSVLD, 0x98), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_falcon_context_ctor, + .dtor = _nvkm_falcon_context_dtor, + .init = _nvkm_falcon_context_init, + .fini = _nvkm_falcon_context_fini, + .rd32 = _nvkm_falcon_context_rd32, + .wr32 = _nvkm_falcon_context_wr32, + }, +}; + +/******************************************************************************* + * PMSVLD engine/subdev functions + ******************************************************************************/ + +static int +g98_msvld_init(struct nvkm_object *object) +{ + struct g98_msvld_priv *priv = (void *)object; + int ret; + + ret = nvkm_falcon_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, 0x084010, 0x0000ffd2); + nv_wr32(priv, 0x08401c, 0x0000fff2); + return 0; +} + +static int +g98_msvld_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct g98_msvld_priv *priv; + int ret; + + ret = nvkm_falcon_create(parent, engine, oclass, 0x084000, true, + "PMSVLD", "msvld", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x04008000; + nv_engine(priv)->cclass = &g98_msvld_cclass; + nv_engine(priv)->sclass = g98_msvld_sclass; + return 0; +} + +struct nvkm_oclass +g98_msvld_oclass = { + .handle = NV_ENGINE(MSVLD, 0x98), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = g98_msvld_ctor, + .dtor = _nvkm_falcon_dtor, + .init = g98_msvld_init, + .fini = _nvkm_falcon_fini, + .rd32 = _nvkm_falcon_rd32, + .wr32 = _nvkm_falcon_wr32, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/msvld/gf100.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/msvld/gf100.c new file mode 100644 index 000000000..b8d1e0f52 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/msvld/gf100.c @@ -0,0 +1,109 @@ +/* + * 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 +#include + +struct gf100_msvld_priv { + struct nvkm_falcon base; +}; + +/******************************************************************************* + * MSVLD object classes + ******************************************************************************/ + +static struct nvkm_oclass +gf100_msvld_sclass[] = { + { 0x90b1, &nvkm_object_ofuncs }, + {}, +}; + +/******************************************************************************* + * PMSVLD context + ******************************************************************************/ + +static struct nvkm_oclass +gf100_msvld_cclass = { + .handle = NV_ENGCTX(MSVLD, 0xc0), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_falcon_context_ctor, + .dtor = _nvkm_falcon_context_dtor, + .init = _nvkm_falcon_context_init, + .fini = _nvkm_falcon_context_fini, + .rd32 = _nvkm_falcon_context_rd32, + .wr32 = _nvkm_falcon_context_wr32, + }, +}; + +/******************************************************************************* + * PMSVLD engine/subdev functions + ******************************************************************************/ + +static int +gf100_msvld_init(struct nvkm_object *object) +{ + struct gf100_msvld_priv *priv = (void *)object; + int ret; + + ret = nvkm_falcon_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, 0x084010, 0x0000fff2); + nv_wr32(priv, 0x08401c, 0x0000fff2); + return 0; +} + +static int +gf100_msvld_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gf100_msvld_priv *priv; + int ret; + + ret = nvkm_falcon_create(parent, engine, oclass, 0x084000, true, + "PMSVLD", "msvld", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00008000; + nv_subdev(priv)->intr = nvkm_falcon_intr; + nv_engine(priv)->cclass = &gf100_msvld_cclass; + nv_engine(priv)->sclass = gf100_msvld_sclass; + return 0; +} + +struct nvkm_oclass +gf100_msvld_oclass = { + .handle = NV_ENGINE(MSVLD, 0xc0), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_msvld_ctor, + .dtor = _nvkm_falcon_dtor, + .init = gf100_msvld_init, + .fini = _nvkm_falcon_fini, + .rd32 = _nvkm_falcon_rd32, + .wr32 = _nvkm_falcon_wr32, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/msvld/gk104.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/msvld/gk104.c new file mode 100644 index 000000000..a0b092783 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/msvld/gk104.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 +#include + +struct gk104_msvld_priv { + struct nvkm_falcon base; +}; + +/******************************************************************************* + * MSVLD object classes + ******************************************************************************/ + +static struct nvkm_oclass +gk104_msvld_sclass[] = { + { 0x95b1, &nvkm_object_ofuncs }, + {}, +}; + +/******************************************************************************* + * PMSVLD context + ******************************************************************************/ + +static struct nvkm_oclass +gk104_msvld_cclass = { + .handle = NV_ENGCTX(MSVLD, 0xe0), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_falcon_context_ctor, + .dtor = _nvkm_falcon_context_dtor, + .init = _nvkm_falcon_context_init, + .fini = _nvkm_falcon_context_fini, + .rd32 = _nvkm_falcon_context_rd32, + .wr32 = _nvkm_falcon_context_wr32, + }, +}; + +/******************************************************************************* + * PMSVLD engine/subdev functions + ******************************************************************************/ + +static int +gk104_msvld_init(struct nvkm_object *object) +{ + struct gk104_msvld_priv *priv = (void *)object; + int ret; + + ret = nvkm_falcon_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, 0x084010, 0x0000fff2); + nv_wr32(priv, 0x08401c, 0x0000fff2); + return 0; +} + +static int +gk104_msvld_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gk104_msvld_priv *priv; + int ret; + + ret = nvkm_falcon_create(parent, engine, oclass, 0x084000, true, + "PMSVLD", "msvld", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00008000; + nv_subdev(priv)->intr = nvkm_falcon_intr; + nv_engine(priv)->cclass = &gk104_msvld_cclass; + nv_engine(priv)->sclass = gk104_msvld_sclass; + return 0; +} + +struct nvkm_oclass +gk104_msvld_oclass = { + .handle = NV_ENGINE(MSVLD, 0xe0), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gk104_msvld_ctor, + .dtor = _nvkm_falcon_dtor, + .init = gk104_msvld_init, + .fini = _nvkm_falcon_fini, + .rd32 = _nvkm_falcon_rd32, + .wr32 = _nvkm_falcon_wr32, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/Kbuild new file mode 100644 index 000000000..413b6091e --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/Kbuild @@ -0,0 +1,9 @@ +nvkm-y += nvkm/engine/pm/base.o +nvkm-y += nvkm/engine/pm/daemon.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/gt215.o +nvkm-y += nvkm/engine/pm/gf100.o +nvkm-y += nvkm/engine/pm/gk104.o +nvkm-y += nvkm/engine/pm/gk110.o diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/base.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/base.c new file mode 100644 index 000000000..2006c4459 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/base.c @@ -0,0 +1,476 @@ +/* + * 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 + +#define QUAD_MASK 0x0f +#define QUAD_FREE 0x01 + +static struct nvkm_perfsig * +nvkm_perfsig_find_(struct nvkm_perfdom *dom, const char *name, u32 size) +{ + char path[64]; + int i; + + if (name[0] != '/') { + for (i = 0; i < dom->signal_nr; i++) { + if ( dom->signal[i].name && + !strncmp(name, dom->signal[i].name, size)) + return &dom->signal[i]; + } + } else { + for (i = 0; i < dom->signal_nr; i++) { + snprintf(path, sizeof(path), "/%s/%02x", dom->name, i); + if (!strncmp(name, path, size)) + return &dom->signal[i]; + } + } + + return NULL; +} + +struct nvkm_perfsig * +nvkm_perfsig_find(struct nvkm_pm *ppm, const char *name, u32 size, + struct nvkm_perfdom **pdom) +{ + struct nvkm_perfdom *dom = *pdom; + struct nvkm_perfsig *sig; + + if (dom == NULL) { + list_for_each_entry(dom, &ppm->domains, head) { + sig = nvkm_perfsig_find_(dom, name, size); + if (sig) { + *pdom = dom; + return sig; + } + } + + return NULL; + } + + return nvkm_perfsig_find_(dom, name, size); +} + +struct nvkm_perfctr * +nvkm_perfsig_wrap(struct nvkm_pm *ppm, const char *name, + struct nvkm_perfdom **pdom) +{ + struct nvkm_perfsig *sig; + struct nvkm_perfctr *ctr; + + sig = nvkm_perfsig_find(ppm, name, strlen(name), pdom); + if (!sig) + return NULL; + + ctr = kzalloc(sizeof(*ctr), GFP_KERNEL); + if (ctr) { + ctr->signal[0] = sig; + ctr->logic_op = 0xaaaa; + } + + return ctr; +} + +/******************************************************************************* + * Perfmon object classes + ******************************************************************************/ +static int +nvkm_perfctr_query(struct nvkm_object *object, void *data, u32 size) +{ + union { + struct nvif_perfctr_query_v0 v0; + } *args = data; + struct nvkm_device *device = nv_device(object); + struct nvkm_pm *ppm = (void *)object->engine; + struct nvkm_perfdom *dom = NULL, *chk; + const bool all = nvkm_boolopt(device->cfgopt, "NvPmShowAll", false); + const bool raw = nvkm_boolopt(device->cfgopt, "NvPmUnnamed", all); + const char *name; + int tmp = 0, di, si; + int ret; + + nv_ioctl(object, "perfctr query size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(object, "perfctr query vers %d iter %08x\n", + args->v0.version, args->v0.iter); + di = (args->v0.iter & 0xff000000) >> 24; + si = (args->v0.iter & 0x00ffffff) - 1; + } else + return ret; + + list_for_each_entry(chk, &ppm->domains, head) { + if (tmp++ == di) { + dom = chk; + break; + } + } + + if (dom == NULL || si >= (int)dom->signal_nr) + return -EINVAL; + + if (si >= 0) { + if (raw || !(name = dom->signal[si].name)) { + snprintf(args->v0.name, sizeof(args->v0.name), + "/%s/%02x", dom->name, si); + } else { + strncpy(args->v0.name, name, sizeof(args->v0.name)); + } + } + + do { + while (++si < dom->signal_nr) { + if (all || dom->signal[si].name) { + args->v0.iter = (di << 24) | ++si; + return 0; + } + } + si = -1; + di = di + 1; + dom = list_entry(dom->head.next, typeof(*dom), head); + } while (&dom->head != &ppm->domains); + + args->v0.iter = 0xffffffff; + return 0; +} + +static int +nvkm_perfctr_sample(struct nvkm_object *object, void *data, u32 size) +{ + union { + struct nvif_perfctr_sample none; + } *args = data; + struct nvkm_pm *ppm = (void *)object->engine; + struct nvkm_perfctr *ctr, *tmp; + struct nvkm_perfdom *dom; + int ret; + + nv_ioctl(object, "perfctr sample size %d\n", size); + if (nvif_unvers(args->none)) { + nv_ioctl(object, "perfctr sample\n"); + } else + return ret; + ppm->sequence++; + + list_for_each_entry(dom, &ppm->domains, head) { + /* sample previous batch of counters */ + if (dom->quad != QUAD_MASK) { + dom->func->next(ppm, dom); + tmp = NULL; + while (!list_empty(&dom->list)) { + ctr = list_first_entry(&dom->list, + typeof(*ctr), head); + if (ctr->slot < 0) break; + if ( tmp && tmp == ctr) break; + if (!tmp) tmp = ctr; + dom->func->read(ppm, dom, ctr); + ctr->slot = -1; + list_move_tail(&ctr->head, &dom->list); + } + } + + dom->quad = QUAD_MASK; + + /* setup next batch of counters for sampling */ + list_for_each_entry(ctr, &dom->list, head) { + ctr->slot = ffs(dom->quad) - 1; + if (ctr->slot < 0) + break; + dom->quad &= ~(QUAD_FREE << ctr->slot); + dom->func->init(ppm, dom, ctr); + } + + if (dom->quad != QUAD_MASK) + dom->func->next(ppm, dom); + } + + return 0; +} + +static int +nvkm_perfctr_read(struct nvkm_object *object, void *data, u32 size) +{ + union { + struct nvif_perfctr_read_v0 v0; + } *args = data; + struct nvkm_perfctr *ctr = (void *)object; + int ret; + + nv_ioctl(object, "perfctr read size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(object, "perfctr read vers %d\n", args->v0.version); + } else + return ret; + + if (!ctr->clk) + return -EAGAIN; + + args->v0.clk = ctr->clk; + args->v0.ctr = ctr->ctr; + return 0; +} + +static int +nvkm_perfctr_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size) +{ + switch (mthd) { + case NVIF_PERFCTR_V0_QUERY: + return nvkm_perfctr_query(object, data, size); + case NVIF_PERFCTR_V0_SAMPLE: + return nvkm_perfctr_sample(object, data, size); + case NVIF_PERFCTR_V0_READ: + return nvkm_perfctr_read(object, data, size); + default: + break; + } + return -EINVAL; +} + +static void +nvkm_perfctr_dtor(struct nvkm_object *object) +{ + struct nvkm_perfctr *ctr = (void *)object; + if (ctr->head.next) + list_del(&ctr->head); + nvkm_object_destroy(&ctr->base); +} + +static int +nvkm_perfctr_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + union { + struct nvif_perfctr_v0 v0; + } *args = data; + struct nvkm_pm *ppm = (void *)engine; + struct nvkm_perfdom *dom = NULL; + struct nvkm_perfsig *sig[4] = {}; + struct nvkm_perfctr *ctr; + int ret, i; + + nv_ioctl(parent, "create perfctr size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(parent, "create perfctr vers %d logic_op %04x\n", + args->v0.version, args->v0.logic_op); + } else + return ret; + + for (i = 0; i < ARRAY_SIZE(args->v0.name) && args->v0.name[i][0]; i++) { + sig[i] = nvkm_perfsig_find(ppm, args->v0.name[i], + strnlen(args->v0.name[i], + sizeof(args->v0.name[i])), + &dom); + if (!sig[i]) + return -EINVAL; + } + + ret = nvkm_object_create(parent, engine, oclass, 0, &ctr); + *pobject = nv_object(ctr); + if (ret) + return ret; + + ctr->slot = -1; + ctr->logic_op = args->v0.logic_op; + ctr->signal[0] = sig[0]; + ctr->signal[1] = sig[1]; + ctr->signal[2] = sig[2]; + ctr->signal[3] = sig[3]; + if (dom) + list_add_tail(&ctr->head, &dom->list); + return 0; +} + +static struct nvkm_ofuncs +nvkm_perfctr_ofuncs = { + .ctor = nvkm_perfctr_ctor, + .dtor = nvkm_perfctr_dtor, + .init = nvkm_object_init, + .fini = nvkm_object_fini, + .mthd = nvkm_perfctr_mthd, +}; + +struct nvkm_oclass +nvkm_pm_sclass[] = { + { .handle = NVIF_IOCTL_NEW_V0_PERFCTR, + .ofuncs = &nvkm_perfctr_ofuncs, + }, + {}, +}; + +/******************************************************************************* + * PPM context + ******************************************************************************/ +static void +nvkm_perfctx_dtor(struct nvkm_object *object) +{ + struct nvkm_pm *ppm = (void *)object->engine; + mutex_lock(&nv_subdev(ppm)->mutex); + nvkm_engctx_destroy(&ppm->context->base); + ppm->context = NULL; + mutex_unlock(&nv_subdev(ppm)->mutex); +} + +static int +nvkm_perfctx_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_pm *ppm = (void *)engine; + struct nvkm_perfctx *ctx; + int ret; + + ret = nvkm_engctx_create(parent, engine, oclass, NULL, 0, 0, 0, &ctx); + *pobject = nv_object(ctx); + if (ret) + return ret; + + mutex_lock(&nv_subdev(ppm)->mutex); + if (ppm->context == NULL) + ppm->context = ctx; + mutex_unlock(&nv_subdev(ppm)->mutex); + + if (ctx != ppm->context) + return -EBUSY; + + return 0; +} + +struct nvkm_oclass +nvkm_pm_cclass = { + .handle = NV_ENGCTX(PM, 0x00), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nvkm_perfctx_ctor, + .dtor = nvkm_perfctx_dtor, + .init = _nvkm_engctx_init, + .fini = _nvkm_engctx_fini, + }, +}; + +/******************************************************************************* + * PPM engine/subdev functions + ******************************************************************************/ +int +nvkm_perfdom_new(struct nvkm_pm *ppm, 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 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(sizeof(*dom) + sdom->signal_nr * + sizeof(*dom->signal), 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, &ppm->domains); + INIT_LIST_HEAD(&dom->list); + dom->func = sdom->func; + dom->addr = addr; + dom->quad = QUAD_MASK; + dom->signal_nr = sdom->signal_nr; + + ssig = (sdom++)->signal; + while (ssig->name) { + dom->signal[ssig->signal].name = ssig->name; + ssig++; + } + + addr += size_domain; + } + + mask &= ~(1 << i); + } + + return 0; +} + +int +_nvkm_pm_fini(struct nvkm_object *object, bool suspend) +{ + struct nvkm_pm *ppm = (void *)object; + return nvkm_engine_fini(&ppm->base, suspend); +} + +int +_nvkm_pm_init(struct nvkm_object *object) +{ + struct nvkm_pm *ppm = (void *)object; + return nvkm_engine_init(&ppm->base); +} + +void +_nvkm_pm_dtor(struct nvkm_object *object) +{ + struct nvkm_pm *ppm = (void *)object; + struct nvkm_perfdom *dom, *tmp; + + list_for_each_entry_safe(dom, tmp, &ppm->domains, head) { + list_del(&dom->head); + kfree(dom); + } + + nvkm_engine_destroy(&ppm->base); +} + +int +nvkm_pm_create_(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, int length, void **pobject) +{ + struct nvkm_pm *ppm; + int ret; + + ret = nvkm_engine_create_(parent, engine, oclass, true, "PPM", + "pm", length, pobject); + ppm = *pobject; + if (ret) + return ret; + + INIT_LIST_HEAD(&ppm->domains); + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/daemon.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/daemon.c new file mode 100644 index 000000000..a7a5f3a3c --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/daemon.c @@ -0,0 +1,108 @@ +/* + * 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 void +pwr_perfctr_init(struct nvkm_pm *ppm, struct nvkm_perfdom *dom, + struct nvkm_perfctr *ctr) +{ + u32 mask = 0x00000000; + u32 ctrl = 0x00000001; + int i; + + for (i = 0; i < ARRAY_SIZE(ctr->signal) && ctr->signal[i]; i++) + mask |= 1 << (ctr->signal[i] - dom->signal); + + nv_wr32(ppm, 0x10a504 + (ctr->slot * 0x10), mask); + nv_wr32(ppm, 0x10a50c + (ctr->slot * 0x10), ctrl); + nv_wr32(ppm, 0x10a50c + (ppm->last * 0x10), 0x00000003); +} + +static void +pwr_perfctr_read(struct nvkm_pm *ppm, struct nvkm_perfdom *dom, + struct nvkm_perfctr *ctr) +{ + ctr->ctr = ppm->pwr[ctr->slot]; + ctr->clk = ppm->pwr[ppm->last]; +} + +static void +pwr_perfctr_next(struct nvkm_pm *ppm, struct nvkm_perfdom *dom) +{ + int i; + + for (i = 0; i <= ppm->last; i++) { + ppm->pwr[i] = nv_rd32(ppm, 0x10a508 + (i * 0x10)); + nv_wr32(ppm, 0x10a508 + (i * 0x10), 0x80000000); + } +} + +static const struct nvkm_funcdom +pwr_perfctr_func = { + .init = pwr_perfctr_init, + .read = pwr_perfctr_read, + .next = pwr_perfctr_next, +}; + +const struct nvkm_specdom +gt215_pm_pwr[] = { + { 0x20, (const struct nvkm_specsig[]) { + { 0x00, "pwr_gr_idle" }, + { 0x04, "pwr_bsp_idle" }, + { 0x05, "pwr_vp_idle" }, + { 0x06, "pwr_ppp_idle" }, + { 0x13, "pwr_ce0_idle" }, + {} + }, &pwr_perfctr_func }, + {} +}; + +const struct nvkm_specdom +gf100_pm_pwr[] = { + { 0x20, (const struct nvkm_specsig[]) { + { 0x00, "pwr_gr_idle" }, + { 0x04, "pwr_bsp_idle" }, + { 0x05, "pwr_vp_idle" }, + { 0x06, "pwr_ppp_idle" }, + { 0x13, "pwr_ce0_idle" }, + { 0x14, "pwr_ce1_idle" }, + {} + }, &pwr_perfctr_func }, + {} +}; + +const struct nvkm_specdom +gk104_pm_pwr[] = { + { 0x20, (const struct nvkm_specsig[]) { + { 0x00, "pwr_gr_idle" }, + { 0x04, "pwr_bsp_idle" }, + { 0x05, "pwr_vp_idle" }, + { 0x06, "pwr_ppp_idle" }, + { 0x13, "pwr_ce0_idle" }, + { 0x14, "pwr_ce1_idle" }, + { 0x15, "pwr_ce2_idle" }, + {} + }, &pwr_perfctr_func }, + {} +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/g84.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/g84.c new file mode 100644 index 000000000..d54c6705b --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/g84.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 "nv40.h" + +static const struct nvkm_specdom +g84_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 }, + { 0x20, (const struct nvkm_specsig[]) { + {} + }, &nv40_perfctr_func }, + { 0x20, (const struct nvkm_specsig[]) { + {} + }, &nv40_perfctr_func }, + { 0x20, (const struct nvkm_specsig[]) { + {} + }, &nv40_perfctr_func }, + {} +}; + +struct nvkm_oclass * +g84_pm_oclass = &(struct nv40_pm_oclass) { + .base.handle = NV_ENGINE(PM, 0x84), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv40_pm_ctor, + .dtor = _nvkm_pm_dtor, + .init = _nvkm_pm_init, + .fini = _nvkm_pm_fini, + }, + .doms = g84_pm, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/gf100.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/gf100.c new file mode 100644 index 000000000..008fed73d --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/gf100.c @@ -0,0 +1,159 @@ +/* + * 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_specdom +gf100_pm_hub[] = { + {} +}; + +static const struct nvkm_specdom +gf100_pm_gpc[] = { + {} +}; + +static const struct nvkm_specdom +gf100_pm_part[] = { + {} +}; + +static void +gf100_perfctr_init(struct nvkm_pm *ppm, struct nvkm_perfdom *dom, + struct nvkm_perfctr *ctr) +{ + struct gf100_pm_priv *priv = (void *)ppm; + struct gf100_pm_cntr *cntr = (void *)ctr; + u32 log = ctr->logic_op; + u32 src = 0x00000000; + int i; + + for (i = 0; i < 4 && ctr->signal[i]; i++) + src |= (ctr->signal[i] - dom->signal) << (i * 8); + + nv_wr32(priv, dom->addr + 0x09c, 0x00040002); + nv_wr32(priv, dom->addr + 0x100, 0x00000000); + nv_wr32(priv, dom->addr + 0x040 + (cntr->base.slot * 0x08), src); + nv_wr32(priv, dom->addr + 0x044 + (cntr->base.slot * 0x08), log); +} + +static void +gf100_perfctr_read(struct nvkm_pm *ppm, struct nvkm_perfdom *dom, + struct nvkm_perfctr *ctr) +{ + struct gf100_pm_priv *priv = (void *)ppm; + struct gf100_pm_cntr *cntr = (void *)ctr; + + switch (cntr->base.slot) { + case 0: cntr->base.ctr = nv_rd32(priv, dom->addr + 0x08c); break; + case 1: cntr->base.ctr = nv_rd32(priv, dom->addr + 0x088); break; + case 2: cntr->base.ctr = nv_rd32(priv, dom->addr + 0x080); break; + case 3: cntr->base.ctr = nv_rd32(priv, dom->addr + 0x090); break; + } + cntr->base.clk = nv_rd32(priv, dom->addr + 0x070); +} + +static void +gf100_perfctr_next(struct nvkm_pm *ppm, struct nvkm_perfdom *dom) +{ + struct gf100_pm_priv *priv = (void *)ppm; + nv_wr32(priv, dom->addr + 0x06c, dom->signal_nr - 0x40 + 0x27); + nv_wr32(priv, dom->addr + 0x0ec, 0x00000011); +} + +const struct nvkm_funcdom +gf100_perfctr_func = { + .init = gf100_perfctr_init, + .read = gf100_perfctr_read, + .next = gf100_perfctr_next, +}; + +int +gf100_pm_fini(struct nvkm_object *object, bool suspend) +{ + struct gf100_pm_priv *priv = (void *)object; + nv_mask(priv, 0x000200, 0x10000000, 0x00000000); + nv_mask(priv, 0x000200, 0x10000000, 0x10000000); + return nvkm_pm_fini(&priv->base, suspend); +} + +static int +gf100_pm_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gf100_pm_priv *priv; + u32 mask; + int ret; + + ret = nvkm_pm_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + ret = nvkm_perfdom_new(&priv->base, "pwr", 0, 0, 0, 0, gf100_pm_pwr); + if (ret) + return ret; + + /* HUB */ + ret = nvkm_perfdom_new(&priv->base, "hub", 0, 0x1b0000, 0, 0x200, + gf100_pm_hub); + if (ret) + return ret; + + /* GPC */ + mask = (1 << nv_rd32(priv, 0x022430)) - 1; + mask &= ~nv_rd32(priv, 0x022504); + mask &= ~nv_rd32(priv, 0x022584); + + ret = nvkm_perfdom_new(&priv->base, "gpc", mask, 0x180000, + 0x1000, 0x200, gf100_pm_gpc); + if (ret) + return ret; + + /* PART */ + mask = (1 << nv_rd32(priv, 0x022438)) - 1; + mask &= ~nv_rd32(priv, 0x022548); + mask &= ~nv_rd32(priv, 0x0225c8); + + ret = nvkm_perfdom_new(&priv->base, "part", mask, 0x1a0000, + 0x1000, 0x200, gf100_pm_part); + if (ret) + return ret; + + nv_engine(priv)->cclass = &nvkm_pm_cclass; + nv_engine(priv)->sclass = nvkm_pm_sclass; + priv->base.last = 7; + return 0; +} + +struct nvkm_oclass +gf100_pm_oclass = { + .handle = NV_ENGINE(PM, 0xc0), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_pm_ctor, + .dtor = _nvkm_pm_dtor, + .init = _nvkm_pm_init, + .fini = gf100_pm_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/gf100.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/gf100.h new file mode 100644 index 000000000..6a01fc7fe --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/gf100.h @@ -0,0 +1,15 @@ +#ifndef __NVKM_PM_NVC0_H__ +#define __NVKM_PM_NVC0_H__ +#include "priv.h" + +struct gf100_pm_priv { + struct nvkm_pm base; +}; + +struct gf100_pm_cntr { + struct nvkm_perfctr base; +}; + +extern const struct nvkm_funcdom gf100_perfctr_func; +int gf100_pm_fini(struct nvkm_object *, bool); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/gk104.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/gk104.c new file mode 100644 index 000000000..75b9ff3d1 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/gk104.c @@ -0,0 +1,148 @@ +/* + * 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_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 }, + {} +}; + +static const struct nvkm_specdom +gk104_pm_part[] = { + { 0x60, (const struct nvkm_specsig[]) { + { 0x47, "part00_user_0" }, + {} + }, &gf100_perfctr_func }, + { 0x60, (const struct nvkm_specsig[]) { + { 0x47, "part01_user_0" }, + {} + }, &gf100_perfctr_func }, + {} +}; + +static int +gk104_pm_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gf100_pm_priv *priv; + u32 mask; + int ret; + + ret = nvkm_pm_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + /* PDAEMON */ + ret = nvkm_perfdom_new(&priv->base, "pwr", 0, 0, 0, 0, gk104_pm_pwr); + if (ret) + return ret; + + /* HUB */ + ret = nvkm_perfdom_new(&priv->base, "hub", 0, 0x1b0000, 0, 0x200, + gk104_pm_hub); + if (ret) + return ret; + + /* GPC */ + mask = (1 << nv_rd32(priv, 0x022430)) - 1; + mask &= ~nv_rd32(priv, 0x022504); + mask &= ~nv_rd32(priv, 0x022584); + + ret = nvkm_perfdom_new(&priv->base, "gpc", mask, 0x180000, + 0x1000, 0x200, gk104_pm_gpc); + if (ret) + return ret; + + /* PART */ + mask = (1 << nv_rd32(priv, 0x022438)) - 1; + mask &= ~nv_rd32(priv, 0x022548); + mask &= ~nv_rd32(priv, 0x0225c8); + + ret = nvkm_perfdom_new(&priv->base, "part", mask, 0x1a0000, + 0x1000, 0x200, gk104_pm_part); + if (ret) + return ret; + + nv_engine(priv)->cclass = &nvkm_pm_cclass; + nv_engine(priv)->sclass = nvkm_pm_sclass; + priv->base.last = 7; + return 0; +} + +struct nvkm_oclass +gk104_pm_oclass = { + .handle = NV_ENGINE(PM, 0xe0), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gk104_pm_ctor, + .dtor = _nvkm_pm_dtor, + .init = _nvkm_pm_init, + .fini = gf100_pm_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/gk110.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/gk110.c new file mode 100644 index 000000000..6820176e5 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/gk110.c @@ -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 + */ +#include "gf100.h" + +static int +gk110_pm_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gf100_pm_priv *priv; + int ret; + + ret = nvkm_pm_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + ret = nvkm_perfdom_new(&priv->base, "pwr", 0, 0, 0, 0, gk104_pm_pwr); + if (ret) + return ret; + + nv_engine(priv)->cclass = &nvkm_pm_cclass; + nv_engine(priv)->sclass = nvkm_pm_sclass; + return 0; +} + +struct nvkm_oclass +gk110_pm_oclass = { + .handle = NV_ENGINE(PM, 0xf0), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gk110_pm_ctor, + .dtor = _nvkm_pm_dtor, + .init = _nvkm_pm_init, + .fini = gf100_pm_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/gt215.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/gt215.c new file mode 100644 index 000000000..d065bfc59 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/gt215.c @@ -0,0 +1,83 @@ +/* + * 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_specdom +gt215_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 }, + { 0x20, (const struct nvkm_specsig[]) { + {} + }, &nv40_perfctr_func }, + { 0x20, (const struct nvkm_specsig[]) { + {} + }, &nv40_perfctr_func }, + { 0x20, (const struct nvkm_specsig[]) { + {} + }, &nv40_perfctr_func }, + {} +}; + +static int +gt215_pm_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **object) +{ + int ret = nv40_pm_ctor(parent, engine, oclass, data, size, object); + if (ret == 0) { + struct nv40_pm_priv *priv = (void *)*object; + ret = nvkm_perfdom_new(&priv->base, "pwr", 0, 0, 0, 0, + gt215_pm_pwr); + if (ret) + return ret; + + priv->base.last = 3; + } + return ret; +} + +struct nvkm_oclass * +gt215_pm_oclass = &(struct nv40_pm_oclass) { + .base.handle = NV_ENGINE(PM, 0xa3), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gt215_pm_ctor, + .dtor = _nvkm_pm_dtor, + .init = _nvkm_pm_init, + .fini = _nvkm_pm_fini, + }, + .doms = gt215_pm, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/nv40.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/nv40.c new file mode 100644 index 000000000..ff22f06b2 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/nv40.c @@ -0,0 +1,130 @@ +/* + * 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 *ppm, struct nvkm_perfdom *dom, + struct nvkm_perfctr *ctr) +{ + struct nv40_pm_priv *priv = (void *)ppm; + struct nv40_pm_cntr *cntr = (void *)ctr; + u32 log = ctr->logic_op; + u32 src = 0x00000000; + int i; + + for (i = 0; i < 4 && ctr->signal[i]; i++) + src |= (ctr->signal[i] - dom->signal) << (i * 8); + + nv_wr32(priv, 0x00a7c0 + dom->addr, 0x00000001); + nv_wr32(priv, 0x00a400 + dom->addr + (cntr->base.slot * 0x40), src); + nv_wr32(priv, 0x00a420 + dom->addr + (cntr->base.slot * 0x40), log); +} + +static void +nv40_perfctr_read(struct nvkm_pm *ppm, struct nvkm_perfdom *dom, + struct nvkm_perfctr *ctr) +{ + struct nv40_pm_priv *priv = (void *)ppm; + struct nv40_pm_cntr *cntr = (void *)ctr; + + switch (cntr->base.slot) { + case 0: cntr->base.ctr = nv_rd32(priv, 0x00a700 + dom->addr); break; + case 1: cntr->base.ctr = nv_rd32(priv, 0x00a6c0 + dom->addr); break; + case 2: cntr->base.ctr = nv_rd32(priv, 0x00a680 + dom->addr); break; + case 3: cntr->base.ctr = nv_rd32(priv, 0x00a740 + dom->addr); break; + } + cntr->base.clk = nv_rd32(priv, 0x00a600 + dom->addr); +} + +static void +nv40_perfctr_next(struct nvkm_pm *ppm, struct nvkm_perfdom *dom) +{ + struct nv40_pm_priv *priv = (void *)ppm; + if (priv->sequence != ppm->sequence) { + nv_wr32(priv, 0x400084, 0x00000020); + priv->sequence = ppm->sequence; + } +} + +const struct nvkm_funcdom +nv40_perfctr_func = { + .init = nv40_perfctr_init, + .read = nv40_perfctr_read, + .next = nv40_perfctr_next, +}; + +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_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv40_pm_oclass *mclass = (void *)oclass; + struct nv40_pm_priv *priv; + int ret; + + ret = nvkm_pm_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + ret = nvkm_perfdom_new(&priv->base, "pm", 0, 0, 0, 4, mclass->doms); + if (ret) + return ret; + + nv_engine(priv)->cclass = &nvkm_pm_cclass; + nv_engine(priv)->sclass = nvkm_pm_sclass; + return 0; +} + +struct nvkm_oclass * +nv40_pm_oclass = &(struct nv40_pm_oclass) { + .base.handle = NV_ENGINE(PM, 0x40), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv40_pm_ctor, + .dtor = _nvkm_pm_dtor, + .init = _nvkm_pm_init, + .fini = _nvkm_pm_fini, + }, + .doms = nv40_pm, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/nv40.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/nv40.h new file mode 100644 index 000000000..2338e1504 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/nv40.h @@ -0,0 +1,24 @@ +#ifndef __NVKM_PM_NV40_H__ +#define __NVKM_PM_NV40_H__ +#include "priv.h" + +struct nv40_pm_oclass { + struct nvkm_oclass base; + const struct nvkm_specdom *doms; +}; + +struct nv40_pm_priv { + struct nvkm_pm base; + u32 sequence; +}; + +int nv40_pm_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *data, u32 size, + struct nvkm_object **pobject); + +struct nv40_pm_cntr { + struct nvkm_perfctr base; +}; + +extern const struct nvkm_funcdom nv40_perfctr_func; +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/nv50.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/nv50.c new file mode 100644 index 000000000..6af83b5d1 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/nv50.c @@ -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 + */ +#include "nv40.h" + +static const struct nvkm_specdom +nv50_pm[] = { + { 0x040, (const struct nvkm_specsig[]) { + {} + }, &nv40_perfctr_func }, + { 0x100, (const struct nvkm_specsig[]) { + { 0xc8, "gr_idle" }, + {} + }, &nv40_perfctr_func }, + { 0x100, (const struct nvkm_specsig[]) { + {} + }, &nv40_perfctr_func }, + { 0x020, (const struct nvkm_specsig[]) { + {} + }, &nv40_perfctr_func }, + { 0x040, (const struct nvkm_specsig[]) { + {} + }, &nv40_perfctr_func }, + {} +}; + +struct nvkm_oclass * +nv50_pm_oclass = &(struct nv40_pm_oclass) { + .base.handle = NV_ENGINE(PM, 0x50), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv40_pm_ctor, + .dtor = _nvkm_pm_dtor, + .init = _nvkm_pm_init, + .fini = _nvkm_pm_fini, + }, + .doms = nv50_pm, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/priv.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/priv.h new file mode 100644 index 000000000..1e6eff2a6 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/pm/priv.h @@ -0,0 +1,90 @@ +#ifndef __NVKM_PM_PRIV_H__ +#define __NVKM_PM_PRIV_H__ +#include + +struct nvkm_perfctr { + struct nvkm_object base; + struct list_head head; + struct nvkm_perfsig *signal[4]; + int slot; + u32 logic_op; + u32 clk; + u32 ctr; +}; + +extern struct nvkm_oclass nvkm_pm_sclass[]; + +#include + +struct nvkm_perfctx { + struct nvkm_engctx base; +}; + +extern struct nvkm_oclass nvkm_pm_cclass; + +struct nvkm_specsig { + u8 signal; + const char *name; +}; + +struct nvkm_perfsig { + const char *name; +}; + +struct nvkm_perfdom; +struct nvkm_perfctr * +nvkm_perfsig_wrap(struct nvkm_pm *, const char *, struct nvkm_perfdom **); + +struct nvkm_specdom { + u16 signal_nr; + const struct nvkm_specsig *signal; + const struct nvkm_funcdom *func; +}; + +extern const struct nvkm_specdom gt215_pm_pwr[]; +extern const struct nvkm_specdom gf100_pm_pwr[]; +extern const struct nvkm_specdom gk104_pm_pwr[]; + +struct nvkm_perfdom { + struct list_head head; + struct list_head list; + const struct nvkm_funcdom *func; + char name[32]; + u32 addr; + u8 quad; + u32 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_pm_create(p,e,o,d) \ + nvkm_pm_create_((p), (e), (o), sizeof(**d), (void **)d) +#define nvkm_pm_dtor(p) ({ \ + struct nvkm_pm *c = (p); \ + _nvkm_pm_dtor(nv_object(c)); \ +}) +#define nvkm_pm_init(p) ({ \ + struct nvkm_pm *c = (p); \ + _nvkm_pm_init(nv_object(c)); \ +}) +#define nvkm_pm_fini(p,s) ({ \ + struct nvkm_pm *c = (p); \ + _nvkm_pm_fini(nv_object(c), (s)); \ +}) + +int nvkm_pm_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, int, void **); +void _nvkm_pm_dtor(struct nvkm_object *); +int _nvkm_pm_init(struct nvkm_object *); +int _nvkm_pm_fini(struct nvkm_object *, bool); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/sec/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/sec/Kbuild new file mode 100644 index 000000000..552d40a46 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/sec/Kbuild @@ -0,0 +1 @@ +nvkm-y += nvkm/engine/sec/g98.o diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/sec/fuc/g98.fuc0s b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/sec/fuc/g98.fuc0s new file mode 100644 index 000000000..06ee06071 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/sec/fuc/g98.fuc0s @@ -0,0 +1,698 @@ +/* + * fuc microcode for g98 psec 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_psec_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_psec_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/kernel/drivers/gpu/drm/nouveau/nvkm/engine/sec/fuc/g98.fuc0s.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/sec/fuc/g98.fuc0s.h new file mode 100644 index 000000000..5d65c4fbb --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/sec/fuc/g98.fuc0s.h @@ -0,0 +1,584 @@ +uint32_t g98_psec_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, +}; + +uint32_t g98_psec_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/kernel/drivers/gpu/drm/nouveau/nvkm/engine/sec/g98.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/sec/g98.c new file mode 100644 index 000000000..9d5c1b8b1 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/sec/g98.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 +#include +#include "fuc/g98.fuc0s.h" + +#include +#include +#include + +struct g98_sec_priv { + struct nvkm_falcon base; +}; + +/******************************************************************************* + * Crypt object classes + ******************************************************************************/ + +static struct nvkm_oclass +g98_sec_sclass[] = { + { 0x88b4, &nvkm_object_ofuncs }, + {}, +}; + +/******************************************************************************* + * PSEC context + ******************************************************************************/ + +static struct nvkm_oclass +g98_sec_cclass = { + .handle = NV_ENGCTX(SEC, 0x98), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_falcon_context_ctor, + .dtor = _nvkm_falcon_context_dtor, + .init = _nvkm_falcon_context_init, + .fini = _nvkm_falcon_context_fini, + .rd32 = _nvkm_falcon_context_rd32, + .wr32 = _nvkm_falcon_context_wr32, + }, +}; + +/******************************************************************************* + * PSEC engine/subdev functions + ******************************************************************************/ + +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_subdev *subdev) +{ + struct nvkm_fifo *pfifo = nvkm_fifo(subdev); + struct nvkm_engine *engine = nv_engine(subdev); + struct nvkm_object *engctx; + struct g98_sec_priv *priv = (void *)subdev; + u32 disp = nv_rd32(priv, 0x08701c); + u32 stat = nv_rd32(priv, 0x087008) & disp & ~(disp >> 16); + u32 inst = nv_rd32(priv, 0x087050) & 0x3fffffff; + u32 ssta = nv_rd32(priv, 0x087040) & 0x0000ffff; + u32 addr = nv_rd32(priv, 0x087040) >> 16; + u32 mthd = (addr & 0x07ff) << 2; + u32 subc = (addr & 0x3800) >> 11; + u32 data = nv_rd32(priv, 0x087044); + int chid; + + engctx = nvkm_engctx_get(engine, inst); + chid = pfifo->chid(pfifo, engctx); + + if (stat & 0x00000040) { + nv_error(priv, "DISPATCH_ERROR ["); + nvkm_enum_print(g98_sec_isr_error_name, ssta); + pr_cont("] ch %d [0x%010llx %s] subc %d mthd 0x%04x data 0x%08x\n", + chid, (u64)inst << 12, nvkm_client_name(engctx), + subc, mthd, data); + nv_wr32(priv, 0x087004, 0x00000040); + stat &= ~0x00000040; + } + + if (stat) { + nv_error(priv, "unhandled intr 0x%08x\n", stat); + nv_wr32(priv, 0x087004, stat); + } + + nvkm_engctx_put(engctx); +} + +static int +g98_sec_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct g98_sec_priv *priv; + int ret; + + ret = nvkm_falcon_create(parent, engine, oclass, 0x087000, true, + "PSEC", "sec", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00004000; + nv_subdev(priv)->intr = g98_sec_intr; + nv_engine(priv)->cclass = &g98_sec_cclass; + nv_engine(priv)->sclass = g98_sec_sclass; + nv_falcon(priv)->code.data = g98_psec_code; + nv_falcon(priv)->code.size = sizeof(g98_psec_code); + nv_falcon(priv)->data.data = g98_psec_data; + nv_falcon(priv)->data.size = sizeof(g98_psec_data); + return 0; +} + +struct nvkm_oclass +g98_sec_oclass = { + .handle = NV_ENGINE(SEC, 0x98), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = g98_sec_ctor, + .dtor = _nvkm_falcon_dtor, + .init = _nvkm_falcon_init, + .fini = _nvkm_falcon_fini, + .rd32 = _nvkm_falcon_rd32, + .wr32 = _nvkm_falcon_wr32, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/sw/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/sw/Kbuild new file mode 100644 index 000000000..bdc3a0590 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/sw/Kbuild @@ -0,0 +1,4 @@ +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 diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/sw/gf100.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/sw/gf100.c new file mode 100644 index 000000000..533d5d8ed --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/sw/gf100.c @@ -0,0 +1,141 @@ +/* + * 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 + +/******************************************************************************* + * software object classes + ******************************************************************************/ + +static int +gf100_sw_mthd_vblsem_offset(struct nvkm_object *object, u32 mthd, + void *args, u32 size) +{ + struct nv50_sw_chan *chan = (void *)nv_engctx(object->parent); + u64 data = *(u32 *)args; + if (mthd == 0x0400) { + chan->vblank.offset &= 0x00ffffffffULL; + chan->vblank.offset |= data << 32; + } else { + chan->vblank.offset &= 0xff00000000ULL; + chan->vblank.offset |= data; + } + return 0; +} + +static int +gf100_sw_mthd_mp_control(struct nvkm_object *object, u32 mthd, + void *args, u32 size) +{ + struct nv50_sw_chan *chan = (void *)nv_engctx(object->parent); + struct nv50_sw_priv *priv = (void *)nv_object(chan)->engine; + u32 data = *(u32 *)args; + + switch (mthd) { + case 0x600: + nv_wr32(priv, 0x419e00, data); /* MP.PM_UNK000 */ + break; + case 0x644: + if (data & ~0x1ffffe) + return -EINVAL; + nv_wr32(priv, 0x419e44, data); /* MP.TRAP_WARP_ERROR_EN */ + break; + case 0x6ac: + nv_wr32(priv, 0x419eac, data); /* MP.PM_UNK0AC */ + break; + default: + return -EINVAL; + } + return 0; +} + +static struct nvkm_omthds +gf100_sw_omthds[] = { + { 0x0400, 0x0400, gf100_sw_mthd_vblsem_offset }, + { 0x0404, 0x0404, gf100_sw_mthd_vblsem_offset }, + { 0x0408, 0x0408, nv50_sw_mthd_vblsem_value }, + { 0x040c, 0x040c, nv50_sw_mthd_vblsem_release }, + { 0x0500, 0x0500, nv50_sw_mthd_flip }, + { 0x0600, 0x0600, gf100_sw_mthd_mp_control }, + { 0x0644, 0x0644, gf100_sw_mthd_mp_control }, + { 0x06ac, 0x06ac, gf100_sw_mthd_mp_control }, + {} +}; + +static struct nvkm_oclass +gf100_sw_sclass[] = { + { 0x906e, &nvkm_object_ofuncs, gf100_sw_omthds }, + {} +}; + +/******************************************************************************* + * software context + ******************************************************************************/ + +static int +gf100_sw_vblsem_release(struct nvkm_notify *notify) +{ + struct nv50_sw_chan *chan = + container_of(notify, typeof(*chan), vblank.notify[notify->index]); + struct nv50_sw_priv *priv = (void *)nv_object(chan)->engine; + struct nvkm_bar *bar = nvkm_bar(priv); + + nv_wr32(priv, 0x001718, 0x80000000 | chan->vblank.channel); + bar->flush(bar); + nv_wr32(priv, 0x06000c, upper_32_bits(chan->vblank.offset)); + nv_wr32(priv, 0x060010, lower_32_bits(chan->vblank.offset)); + nv_wr32(priv, 0x060014, chan->vblank.value); + + return NVKM_NOTIFY_DROP; +} + +static struct nv50_sw_cclass +gf100_sw_cclass = { + .base.handle = NV_ENGCTX(SW, 0xc0), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_sw_context_ctor, + .dtor = nv50_sw_context_dtor, + .init = _nvkm_sw_context_init, + .fini = _nvkm_sw_context_fini, + }, + .vblank = gf100_sw_vblsem_release, +}; + +/******************************************************************************* + * software engine/subdev functions + ******************************************************************************/ + +struct nvkm_oclass * +gf100_sw_oclass = &(struct nv50_sw_oclass) { + .base.handle = NV_ENGINE(SW, 0xc0), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_sw_ctor, + .dtor = _nvkm_sw_dtor, + .init = _nvkm_sw_init, + .fini = _nvkm_sw_fini, + }, + .cclass = &gf100_sw_cclass.base, + .sclass = gf100_sw_sclass, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv04.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv04.c new file mode 100644 index 000000000..897024421 --- /dev/null +++ b/kernel/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 + */ +#include +#include + +struct nv04_sw_priv { + struct nvkm_sw base; +}; + +struct nv04_sw_chan { + struct nvkm_sw_chan base; +}; + +/******************************************************************************* + * software object classes + ******************************************************************************/ + +static int +nv04_sw_set_ref(struct nvkm_object *object, u32 mthd, void *data, u32 size) +{ + struct nvkm_object *channel = (void *)nv_engctx(object->parent); + struct nvkm_fifo_chan *fifo = (void *)channel->parent; + atomic_set(&fifo->refcnt, *(u32*)data); + return 0; +} + +static int +nv04_sw_flip(struct nvkm_object *object, u32 mthd, void *args, u32 size) +{ + struct nv04_sw_chan *chan = (void *)nv_engctx(object->parent); + if (chan->base.flip) + return chan->base.flip(chan->base.flip_data); + return -EINVAL; +} + +static struct nvkm_omthds +nv04_sw_omthds[] = { + { 0x0150, 0x0150, nv04_sw_set_ref }, + { 0x0500, 0x0500, nv04_sw_flip }, + {} +}; + +static struct nvkm_oclass +nv04_sw_sclass[] = { + { 0x006e, &nvkm_object_ofuncs, nv04_sw_omthds }, + {} +}; + +/******************************************************************************* + * software context + ******************************************************************************/ + +static int +nv04_sw_context_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv04_sw_chan *chan; + int ret; + + ret = nvkm_sw_context_create(parent, engine, oclass, &chan); + *pobject = nv_object(chan); + if (ret) + return ret; + + return 0; +} + +static struct nvkm_oclass +nv04_sw_cclass = { + .handle = NV_ENGCTX(SW, 0x04), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_sw_context_ctor, + .dtor = _nvkm_sw_context_dtor, + .init = _nvkm_sw_context_init, + .fini = _nvkm_sw_context_fini, + }, +}; + +/******************************************************************************* + * software engine/subdev functions + ******************************************************************************/ + +void +nv04_sw_intr(struct nvkm_subdev *subdev) +{ + nv_mask(subdev, 0x000100, 0x80000000, 0x00000000); +} + +static int +nv04_sw_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv04_sw_priv *priv; + int ret; + + ret = nvkm_sw_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_engine(priv)->cclass = &nv04_sw_cclass; + nv_engine(priv)->sclass = nv04_sw_sclass; + nv_subdev(priv)->intr = nv04_sw_intr; + return 0; +} + +struct nvkm_oclass * +nv04_sw_oclass = &(struct nvkm_oclass) { + .handle = NV_ENGINE(SW, 0x04), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_sw_ctor, + .dtor = _nvkm_sw_dtor, + .init = _nvkm_sw_init, + .fini = _nvkm_sw_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv10.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv10.c new file mode 100644 index 000000000..c61153a3f --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv10.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 + +struct nv10_sw_priv { + struct nvkm_sw base; +}; + +struct nv10_sw_chan { + struct nvkm_sw_chan base; +}; + +/******************************************************************************* + * software object classes + ******************************************************************************/ + +static int +nv10_sw_flip(struct nvkm_object *object, u32 mthd, void *args, u32 size) +{ + struct nv10_sw_chan *chan = (void *)nv_engctx(object->parent); + if (chan->base.flip) + return chan->base.flip(chan->base.flip_data); + return -EINVAL; +} + +static struct nvkm_omthds +nv10_sw_omthds[] = { + { 0x0500, 0x0500, nv10_sw_flip }, + {} +}; + +static struct nvkm_oclass +nv10_sw_sclass[] = { + { 0x016e, &nvkm_object_ofuncs, nv10_sw_omthds }, + {} +}; + +/******************************************************************************* + * software context + ******************************************************************************/ + +static int +nv10_sw_context_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv10_sw_chan *chan; + int ret; + + ret = nvkm_sw_context_create(parent, engine, oclass, &chan); + *pobject = nv_object(chan); + if (ret) + return ret; + + return 0; +} + +static struct nvkm_oclass +nv10_sw_cclass = { + .handle = NV_ENGCTX(SW, 0x04), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv10_sw_context_ctor, + .dtor = _nvkm_sw_context_dtor, + .init = _nvkm_sw_context_init, + .fini = _nvkm_sw_context_fini, + }, +}; + +/******************************************************************************* + * software engine/subdev functions + ******************************************************************************/ + +static int +nv10_sw_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv10_sw_priv *priv; + int ret; + + ret = nvkm_sw_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_engine(priv)->cclass = &nv10_sw_cclass; + nv_engine(priv)->sclass = nv10_sw_sclass; + nv_subdev(priv)->intr = nv04_sw_intr; + return 0; +} + +struct nvkm_oclass * +nv10_sw_oclass = &(struct nvkm_oclass) { + .handle = NV_ENGINE(SW, 0x10), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv10_sw_ctor, + .dtor = _nvkm_sw_dtor, + .init = _nvkm_sw_init, + .fini = _nvkm_sw_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv50.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv50.c new file mode 100644 index 000000000..401fcd730 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv50.c @@ -0,0 +1,234 @@ +/* + * 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 object classes + ******************************************************************************/ + +static int +nv50_sw_mthd_dma_vblsem(struct nvkm_object *object, u32 mthd, + void *args, u32 size) +{ + struct nv50_sw_chan *chan = (void *)nv_engctx(object->parent); + struct nvkm_fifo_chan *fifo = (void *)nv_object(chan)->parent; + struct nvkm_handle *handle; + int ret = -EINVAL; + + handle = nvkm_namedb_get(nv_namedb(fifo), *(u32 *)args); + if (!handle) + return -ENOENT; + + if (nv_iclass(handle->object, NV_GPUOBJ_CLASS)) { + struct nvkm_gpuobj *gpuobj = nv_gpuobj(handle->object); + chan->vblank.ctxdma = gpuobj->node->offset >> 4; + ret = 0; + } + nvkm_namedb_put(handle); + return ret; +} + +static int +nv50_sw_mthd_vblsem_offset(struct nvkm_object *object, u32 mthd, + void *args, u32 size) +{ + struct nv50_sw_chan *chan = (void *)nv_engctx(object->parent); + chan->vblank.offset = *(u32 *)args; + return 0; +} + +int +nv50_sw_mthd_vblsem_value(struct nvkm_object *object, u32 mthd, + void *args, u32 size) +{ + struct nv50_sw_chan *chan = (void *)nv_engctx(object->parent); + chan->vblank.value = *(u32 *)args; + return 0; +} + +int +nv50_sw_mthd_vblsem_release(struct nvkm_object *object, u32 mthd, + void *args, u32 size) +{ + struct nv50_sw_chan *chan = (void *)nv_engctx(object->parent); + u32 head = *(u32 *)args; + if (head >= nvkm_disp(chan)->vblank.index_nr) + return -EINVAL; + + nvkm_notify_get(&chan->vblank.notify[head]); + return 0; +} + +int +nv50_sw_mthd_flip(struct nvkm_object *object, u32 mthd, void *args, u32 size) +{ + struct nv50_sw_chan *chan = (void *)nv_engctx(object->parent); + if (chan->base.flip) + return chan->base.flip(chan->base.flip_data); + return -EINVAL; +} + +static struct nvkm_omthds +nv50_sw_omthds[] = { + { 0x018c, 0x018c, nv50_sw_mthd_dma_vblsem }, + { 0x0400, 0x0400, nv50_sw_mthd_vblsem_offset }, + { 0x0404, 0x0404, nv50_sw_mthd_vblsem_value }, + { 0x0408, 0x0408, nv50_sw_mthd_vblsem_release }, + { 0x0500, 0x0500, nv50_sw_mthd_flip }, + {} +}; + +static struct nvkm_oclass +nv50_sw_sclass[] = { + { 0x506e, &nvkm_object_ofuncs, nv50_sw_omthds }, + {} +}; + +/******************************************************************************* + * software context + ******************************************************************************/ + +static int +nv50_sw_vblsem_release(struct nvkm_notify *notify) +{ + struct nv50_sw_chan *chan = + container_of(notify, typeof(*chan), vblank.notify[notify->index]); + struct nv50_sw_priv *priv = (void *)nv_object(chan)->engine; + struct nvkm_bar *bar = nvkm_bar(priv); + + nv_wr32(priv, 0x001704, chan->vblank.channel); + nv_wr32(priv, 0x001710, 0x80000000 | chan->vblank.ctxdma); + bar->flush(bar); + + if (nv_device(priv)->chipset == 0x50) { + nv_wr32(priv, 0x001570, chan->vblank.offset); + nv_wr32(priv, 0x001574, chan->vblank.value); + } else { + nv_wr32(priv, 0x060010, chan->vblank.offset); + nv_wr32(priv, 0x060014, chan->vblank.value); + } + + return NVKM_NOTIFY_DROP; +} + +void +nv50_sw_context_dtor(struct nvkm_object *object) +{ + struct nv50_sw_chan *chan = (void *)object; + int i; + + for (i = 0; i < ARRAY_SIZE(chan->vblank.notify); i++) + nvkm_notify_fini(&chan->vblank.notify[i]); + + nvkm_sw_context_destroy(&chan->base); +} + +int +nv50_sw_context_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_disp *pdisp = nvkm_disp(parent); + struct nv50_sw_cclass *pclass = (void *)oclass; + struct nv50_sw_chan *chan; + int ret, i; + + ret = nvkm_sw_context_create(parent, engine, oclass, &chan); + *pobject = nv_object(chan); + if (ret) + return ret; + + for (i = 0; pdisp && i < pdisp->vblank.index_nr; i++) { + ret = nvkm_notify_init(NULL, &pdisp->vblank, pclass->vblank, + 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; + } + + chan->vblank.channel = nv_gpuobj(parent->parent)->addr >> 12; + return 0; +} + +static struct nv50_sw_cclass +nv50_sw_cclass = { + .base.handle = NV_ENGCTX(SW, 0x50), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_sw_context_ctor, + .dtor = nv50_sw_context_dtor, + .init = _nvkm_sw_context_init, + .fini = _nvkm_sw_context_fini, + }, + .vblank = nv50_sw_vblsem_release, +}; + +/******************************************************************************* + * software engine/subdev functions + ******************************************************************************/ + +int +nv50_sw_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv50_sw_oclass *pclass = (void *)oclass; + struct nv50_sw_priv *priv; + int ret; + + ret = nvkm_sw_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_engine(priv)->cclass = pclass->cclass; + nv_engine(priv)->sclass = pclass->sclass; + nv_subdev(priv)->intr = nv04_sw_intr; + return 0; +} + +struct nvkm_oclass * +nv50_sw_oclass = &(struct nv50_sw_oclass) { + .base.handle = NV_ENGINE(SW, 0x50), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_sw_ctor, + .dtor = _nvkm_sw_dtor, + .init = _nvkm_sw_init, + .fini = _nvkm_sw_fini, + }, + .cclass = &nv50_sw_cclass.base, + .sclass = nv50_sw_sclass, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv50.h b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv50.h new file mode 100644 index 000000000..d8adc1108 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv50.h @@ -0,0 +1,45 @@ +#ifndef __NVKM_SW_NV50_H__ +#define __NVKM_SW_NV50_H__ +#include +#include + +struct nv50_sw_oclass { + struct nvkm_oclass base; + struct nvkm_oclass *cclass; + struct nvkm_oclass *sclass; +}; + +struct nv50_sw_priv { + struct nvkm_sw base; +}; + +int nv50_sw_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); + +struct nv50_sw_cclass { + struct nvkm_oclass base; + int (*vblank)(struct nvkm_notify *); +}; + +struct nv50_sw_chan { + struct nvkm_sw_chan base; + struct { + struct nvkm_notify notify[4]; + u32 channel; + u32 ctxdma; + u64 offset; + u32 value; + } vblank; +}; + +int nv50_sw_context_ctor(struct nvkm_object *, + struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); +void nv50_sw_context_dtor(struct nvkm_object *); + +int nv50_sw_mthd_vblsem_value(struct nvkm_object *, u32, void *, u32); +int nv50_sw_mthd_vblsem_release(struct nvkm_object *, u32, void *, u32); +int nv50_sw_mthd_flip(struct nvkm_object *, u32, void *, u32); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/vp/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/vp/Kbuild new file mode 100644 index 000000000..6b390eb92 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/vp/Kbuild @@ -0,0 +1 @@ +nvkm-y += nvkm/engine/vp/g84.o diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/vp/g84.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/vp/g84.c new file mode 100644 index 000000000..45f4e186b --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/vp/g84.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, Ilia Mirkin + */ +#include +#include + +#include + +/******************************************************************************* + * VP object classes + ******************************************************************************/ + +static struct nvkm_oclass +g84_vp_sclass[] = { + { 0x7476, &nvkm_object_ofuncs }, + {}, +}; + +/******************************************************************************* + * PVP context + ******************************************************************************/ + +static struct nvkm_oclass +g84_vp_cclass = { + .handle = NV_ENGCTX(VP, 0x84), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_xtensa_engctx_ctor, + .dtor = _nvkm_engctx_dtor, + .init = _nvkm_engctx_init, + .fini = _nvkm_engctx_fini, + .rd32 = _nvkm_engctx_rd32, + .wr32 = _nvkm_engctx_wr32, + }, +}; + +/******************************************************************************* + * PVP engine/subdev functions + ******************************************************************************/ + +static int +g84_vp_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_xtensa *priv; + int ret; + + ret = nvkm_xtensa_create(parent, engine, oclass, 0xf000, true, + "PVP", "vp", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x01020000; + nv_engine(priv)->cclass = &g84_vp_cclass; + nv_engine(priv)->sclass = g84_vp_sclass; + priv->fifo_val = 0x111; + priv->unkd28 = 0x9c544; + return 0; +} + +struct nvkm_oclass +g84_vp_oclass = { + .handle = NV_ENGINE(VP, 0x84), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = g84_vp_ctor, + .dtor = _nvkm_xtensa_dtor, + .init = _nvkm_xtensa_init, + .fini = _nvkm_xtensa_fini, + .rd32 = _nvkm_xtensa_rd32, + .wr32 = _nvkm_xtensa_wr32, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/engine/xtensa.c b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/xtensa.c new file mode 100644 index 000000000..cea90df53 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/engine/xtensa.c @@ -0,0 +1,172 @@ +/* + * 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 + +u32 +_nvkm_xtensa_rd32(struct nvkm_object *object, u64 addr) +{ + struct nvkm_xtensa *xtensa = (void *)object; + return nv_rd32(xtensa, xtensa->addr + addr); +} + +void +_nvkm_xtensa_wr32(struct nvkm_object *object, u64 addr, u32 data) +{ + struct nvkm_xtensa *xtensa = (void *)object; + nv_wr32(xtensa, xtensa->addr + addr, data); +} + +int +_nvkm_xtensa_engctx_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_engctx *engctx; + int ret; + + ret = nvkm_engctx_create(parent, engine, oclass, NULL, 0x10000, 0x1000, + NVOBJ_FLAG_ZERO_ALLOC, &engctx); + *pobject = nv_object(engctx); + return ret; +} + +void +_nvkm_xtensa_intr(struct nvkm_subdev *subdev) +{ + struct nvkm_xtensa *xtensa = (void *)subdev; + u32 unk104 = nv_ro32(xtensa, 0xd04); + u32 intr = nv_ro32(xtensa, 0xc20); + u32 chan = nv_ro32(xtensa, 0xc28); + u32 unk10c = nv_ro32(xtensa, 0xd0c); + + if (intr & 0x10) + nv_warn(xtensa, "Watchdog interrupt, engine hung.\n"); + nv_wo32(xtensa, 0xc20, intr); + intr = nv_ro32(xtensa, 0xc20); + if (unk104 == 0x10001 && unk10c == 0x200 && chan && !intr) { + nv_debug(xtensa, "Enabling FIFO_CTRL\n"); + nv_mask(xtensa, xtensa->addr + 0xd94, 0, xtensa->fifo_val); + } +} + +int +nvkm_xtensa_create_(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, u32 addr, bool enable, + const char *iname, const char *fname, + int length, void **pobject) +{ + struct nvkm_xtensa *xtensa; + int ret; + + ret = nvkm_engine_create_(parent, engine, oclass, enable, iname, + fname, length, pobject); + xtensa = *pobject; + if (ret) + return ret; + + nv_subdev(xtensa)->intr = _nvkm_xtensa_intr; + xtensa->addr = addr; + return 0; +} + +int +_nvkm_xtensa_init(struct nvkm_object *object) +{ + struct nvkm_device *device = nv_device(object); + struct nvkm_xtensa *xtensa = (void *)object; + const struct firmware *fw; + char name[32]; + int i, ret; + u32 tmp; + + ret = nvkm_engine_init(&xtensa->base); + if (ret) + return ret; + + if (!xtensa->gpu_fw) { + snprintf(name, sizeof(name), "nouveau/nv84_xuc%03x", + xtensa->addr >> 12); + + ret = request_firmware(&fw, name, nv_device_base(device)); + if (ret) { + nv_warn(xtensa, "unable to load firmware %s\n", name); + return ret; + } + + if (fw->size > 0x40000) { + nv_warn(xtensa, "firmware %s too large\n", name); + release_firmware(fw); + return -EINVAL; + } + + ret = nvkm_gpuobj_new(object, NULL, 0x40000, 0x1000, 0, + &xtensa->gpu_fw); + if (ret) { + release_firmware(fw); + return ret; + } + + nv_debug(xtensa, "Loading firmware to address: 0x%llx\n", + xtensa->gpu_fw->addr); + + for (i = 0; i < fw->size / 4; i++) + nv_wo32(xtensa->gpu_fw, i * 4, *((u32 *)fw->data + i)); + release_firmware(fw); + } + + nv_wo32(xtensa, 0xd10, 0x1fffffff); /* ?? */ + nv_wo32(xtensa, 0xd08, 0x0fffffff); /* ?? */ + + nv_wo32(xtensa, 0xd28, xtensa->unkd28); /* ?? */ + nv_wo32(xtensa, 0xc20, 0x3f); /* INTR */ + nv_wo32(xtensa, 0xd84, 0x3f); /* INTR_EN */ + + nv_wo32(xtensa, 0xcc0, xtensa->gpu_fw->addr >> 8); /* XT_REGION_BASE */ + nv_wo32(xtensa, 0xcc4, 0x1c); /* XT_REGION_SETUP */ + nv_wo32(xtensa, 0xcc8, xtensa->gpu_fw->size >> 8); /* XT_REGION_LIMIT */ + + tmp = nv_rd32(xtensa, 0x0); + nv_wo32(xtensa, 0xde0, tmp); /* SCRATCH_H2X */ + + nv_wo32(xtensa, 0xce8, 0xf); /* XT_REGION_SETUP */ + + nv_wo32(xtensa, 0xc20, 0x3f); /* INTR */ + nv_wo32(xtensa, 0xd84, 0x3f); /* INTR_EN */ + return 0; +} + +int +_nvkm_xtensa_fini(struct nvkm_object *object, bool suspend) +{ + struct nvkm_xtensa *xtensa = (void *)object; + + nv_wo32(xtensa, 0xd84, 0); /* INTR_EN */ + nv_wo32(xtensa, 0xd94, 0); /* FIFO_CTRL */ + + if (!suspend) + nvkm_gpuobj_ref(NULL, &xtensa->gpu_fw); + + return nvkm_engine_fini(&xtensa->base, suspend); +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/Kbuild new file mode 100644 index 000000000..a1bb3e487 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/Kbuild @@ -0,0 +1,19 @@ +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/fb/Kbuild +include $(src)/nvkm/subdev/fuse/Kbuild +include $(src)/nvkm/subdev/gpio/Kbuild +include $(src)/nvkm/subdev/i2c/Kbuild +include $(src)/nvkm/subdev/ibus/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/pmu/Kbuild +include $(src)/nvkm/subdev/therm/Kbuild +include $(src)/nvkm/subdev/timer/Kbuild +include $(src)/nvkm/subdev/volt/Kbuild diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bar/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bar/Kbuild new file mode 100644 index 000000000..1ab554a0b --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bar/Kbuild @@ -0,0 +1,4 @@ +nvkm-y += nvkm/subdev/bar/base.o +nvkm-y += nvkm/subdev/bar/nv50.o +nvkm-y += nvkm/subdev/bar/gf100.o +nvkm-y += nvkm/subdev/bar/gk20a.o diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bar/base.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bar/base.c new file mode 100644 index 000000000..3502d0012 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bar/base.c @@ -0,0 +1,144 @@ +/* + * 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 + +struct nvkm_barobj { + struct nvkm_object base; + struct nvkm_vma vma; + void __iomem *iomem; +}; + +static int +nvkm_barobj_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_device *device = nv_device(parent); + struct nvkm_bar *bar = nvkm_bar(device); + struct nvkm_mem *mem = data; + struct nvkm_barobj *barobj; + int ret; + + ret = nvkm_object_create(parent, engine, oclass, 0, &barobj); + *pobject = nv_object(barobj); + if (ret) + return ret; + + ret = bar->kmap(bar, mem, NV_MEM_ACCESS_RW, &barobj->vma); + if (ret) + return ret; + + barobj->iomem = ioremap(nv_device_resource_start(device, 3) + + (u32)barobj->vma.offset, mem->size << 12); + if (!barobj->iomem) { + nv_warn(bar, "PRAMIN ioremap failed\n"); + return -ENOMEM; + } + + return 0; +} + +static void +nvkm_barobj_dtor(struct nvkm_object *object) +{ + struct nvkm_bar *bar = nvkm_bar(object); + struct nvkm_barobj *barobj = (void *)object; + if (barobj->vma.node) { + if (barobj->iomem) + iounmap(barobj->iomem); + bar->unmap(bar, &barobj->vma); + } + nvkm_object_destroy(&barobj->base); +} + +static u32 +nvkm_barobj_rd32(struct nvkm_object *object, u64 addr) +{ + struct nvkm_barobj *barobj = (void *)object; + return ioread32_native(barobj->iomem + addr); +} + +static void +nvkm_barobj_wr32(struct nvkm_object *object, u64 addr, u32 data) +{ + struct nvkm_barobj *barobj = (void *)object; + iowrite32_native(data, barobj->iomem + addr); +} + +static struct nvkm_oclass +nvkm_barobj_oclass = { + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nvkm_barobj_ctor, + .dtor = nvkm_barobj_dtor, + .init = nvkm_object_init, + .fini = nvkm_object_fini, + .rd32 = nvkm_barobj_rd32, + .wr32 = nvkm_barobj_wr32, + }, +}; + +int +nvkm_bar_alloc(struct nvkm_bar *bar, struct nvkm_object *parent, + struct nvkm_mem *mem, struct nvkm_object **pobject) +{ + struct nvkm_object *gpuobj; + int ret = nvkm_object_ctor(parent, &parent->engine->subdev.object, + &nvkm_barobj_oclass, mem, 0, &gpuobj); + if (ret == 0) + *pobject = gpuobj; + return ret; +} + +int +nvkm_bar_create_(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, int length, void **pobject) +{ + struct nvkm_bar *bar; + int ret; + + ret = nvkm_subdev_create_(parent, engine, oclass, 0, "BARCTL", + "bar", length, pobject); + bar = *pobject; + if (ret) + return ret; + + return 0; +} + +void +nvkm_bar_destroy(struct nvkm_bar *bar) +{ + nvkm_subdev_destroy(&bar->base); +} + +void +_nvkm_bar_dtor(struct nvkm_object *object) +{ + struct nvkm_bar *bar = (void *)object; + nvkm_bar_destroy(bar); +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.c new file mode 100644 index 000000000..12a1aebd9 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.c @@ -0,0 +1,219 @@ +/* + * 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 + +struct gf100_bar_priv_vm { + struct nvkm_gpuobj *mem; + struct nvkm_gpuobj *pgd; + struct nvkm_vm *vm; +}; + +struct gf100_bar_priv { + struct nvkm_bar base; + spinlock_t lock; + struct gf100_bar_priv_vm bar[2]; +}; + +static int +gf100_bar_kmap(struct nvkm_bar *bar, struct nvkm_mem *mem, u32 flags, + struct nvkm_vma *vma) +{ + struct gf100_bar_priv *priv = (void *)bar; + int ret; + + ret = nvkm_vm_get(priv->bar[0].vm, mem->size << 12, 12, flags, vma); + if (ret) + return ret; + + nvkm_vm_map(vma, mem); + return 0; +} + +static int +gf100_bar_umap(struct nvkm_bar *bar, struct nvkm_mem *mem, u32 flags, + struct nvkm_vma *vma) +{ + struct gf100_bar_priv *priv = (void *)bar; + int ret; + + ret = nvkm_vm_get(priv->bar[1].vm, mem->size << 12, + mem->page_shift, flags, vma); + if (ret) + return ret; + + nvkm_vm_map(vma, mem); + return 0; +} + +static void +gf100_bar_unmap(struct nvkm_bar *bar, struct nvkm_vma *vma) +{ + nvkm_vm_unmap(vma); + nvkm_vm_put(vma); +} + +static int +gf100_bar_ctor_vm(struct gf100_bar_priv *priv, struct gf100_bar_priv_vm *bar_vm, + int bar_nr) +{ + struct nvkm_device *device = nv_device(&priv->base); + struct nvkm_vm *vm; + resource_size_t bar_len; + int ret; + + ret = nvkm_gpuobj_new(nv_object(priv), NULL, 0x1000, 0, 0, + &bar_vm->mem); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(nv_object(priv), NULL, 0x8000, 0, 0, + &bar_vm->pgd); + if (ret) + return ret; + + bar_len = nv_device_resource_len(device, bar_nr); + + ret = nvkm_vm_new(device, 0, bar_len, 0, &vm); + if (ret) + return ret; + + atomic_inc(&vm->engref[NVDEV_SUBDEV_BAR]); + + /* + * Bootstrap page table lookup. + */ + if (bar_nr == 3) { + ret = nvkm_gpuobj_new(nv_object(priv), NULL, + (bar_len >> 12) * 8, 0x1000, + NVOBJ_FLAG_ZERO_ALLOC, + &vm->pgt[0].obj[0]); + vm->pgt[0].refcount[0] = 1; + if (ret) + return ret; + } + + ret = nvkm_vm_ref(vm, &bar_vm->vm, bar_vm->pgd); + nvkm_vm_ref(NULL, &vm, NULL); + if (ret) + return ret; + + nv_wo32(bar_vm->mem, 0x0200, lower_32_bits(bar_vm->pgd->addr)); + nv_wo32(bar_vm->mem, 0x0204, upper_32_bits(bar_vm->pgd->addr)); + nv_wo32(bar_vm->mem, 0x0208, lower_32_bits(bar_len - 1)); + nv_wo32(bar_vm->mem, 0x020c, upper_32_bits(bar_len - 1)); + return 0; +} + +int +gf100_bar_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_device *device = nv_device(parent); + struct gf100_bar_priv *priv; + bool has_bar3 = nv_device_resource_len(device, 3) != 0; + int ret; + + ret = nvkm_bar_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + /* BAR3 */ + if (has_bar3) { + ret = gf100_bar_ctor_vm(priv, &priv->bar[0], 3); + if (ret) + return ret; + } + + /* BAR1 */ + ret = gf100_bar_ctor_vm(priv, &priv->bar[1], 1); + if (ret) + return ret; + + if (has_bar3) { + priv->base.alloc = nvkm_bar_alloc; + priv->base.kmap = gf100_bar_kmap; + } + priv->base.umap = gf100_bar_umap; + priv->base.unmap = gf100_bar_unmap; + priv->base.flush = g84_bar_flush; + spin_lock_init(&priv->lock); + return 0; +} + +void +gf100_bar_dtor(struct nvkm_object *object) +{ + struct gf100_bar_priv *priv = (void *)object; + + nvkm_vm_ref(NULL, &priv->bar[1].vm, priv->bar[1].pgd); + nvkm_gpuobj_ref(NULL, &priv->bar[1].pgd); + nvkm_gpuobj_ref(NULL, &priv->bar[1].mem); + + if (priv->bar[0].vm) { + nvkm_gpuobj_ref(NULL, &priv->bar[0].vm->pgt[0].obj[0]); + nvkm_vm_ref(NULL, &priv->bar[0].vm, priv->bar[0].pgd); + } + nvkm_gpuobj_ref(NULL, &priv->bar[0].pgd); + nvkm_gpuobj_ref(NULL, &priv->bar[0].mem); + + nvkm_bar_destroy(&priv->base); +} + +int +gf100_bar_init(struct nvkm_object *object) +{ + struct gf100_bar_priv *priv = (void *)object; + int ret; + + ret = nvkm_bar_init(&priv->base); + if (ret) + return ret; + + nv_mask(priv, 0x000200, 0x00000100, 0x00000000); + nv_mask(priv, 0x000200, 0x00000100, 0x00000100); + + nv_wr32(priv, 0x001704, 0x80000000 | priv->bar[1].mem->addr >> 12); + if (priv->bar[0].mem) + nv_wr32(priv, 0x001714, + 0xc0000000 | priv->bar[0].mem->addr >> 12); + return 0; +} + +struct nvkm_oclass +gf100_bar_oclass = { + .handle = NV_SUBDEV(BAR, 0xc0), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_bar_ctor, + .dtor = gf100_bar_dtor, + .init = gf100_bar_init, + .fini = _nvkm_bar_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gk20a.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gk20a.c new file mode 100644 index 000000000..148f739a2 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gk20a.c @@ -0,0 +1,50 @@ +/* + * 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 "priv.h" + +int +gk20a_bar_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_bar *bar; + int ret; + + ret = gf100_bar_ctor(parent, engine, oclass, data, size, pobject); + if (ret) + return ret; + + bar = (struct nvkm_bar *)*pobject; + bar->iomap_uncached = true; + return 0; +} + +struct nvkm_oclass +gk20a_bar_oclass = { + .handle = NV_SUBDEV(BAR, 0xea), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gk20a_bar_ctor, + .dtor = gf100_bar_dtor, + .init = gf100_bar_init, + .fini = _nvkm_bar_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bar/nv50.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bar/nv50.c new file mode 100644 index 000000000..8548adb91 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bar/nv50.c @@ -0,0 +1,271 @@ +/* + * 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 +#include + +struct nv50_bar_priv { + struct nvkm_bar base; + spinlock_t lock; + struct nvkm_gpuobj *mem; + struct nvkm_gpuobj *pad; + struct nvkm_gpuobj *pgd; + struct nvkm_vm *bar1_vm; + struct nvkm_gpuobj *bar1; + struct nvkm_vm *bar3_vm; + struct nvkm_gpuobj *bar3; +}; + +static int +nv50_bar_kmap(struct nvkm_bar *bar, struct nvkm_mem *mem, u32 flags, + struct nvkm_vma *vma) +{ + struct nv50_bar_priv *priv = (void *)bar; + int ret; + + ret = nvkm_vm_get(priv->bar3_vm, mem->size << 12, 12, flags, vma); + if (ret) + return ret; + + nvkm_vm_map(vma, mem); + return 0; +} + +static int +nv50_bar_umap(struct nvkm_bar *bar, struct nvkm_mem *mem, u32 flags, + struct nvkm_vma *vma) +{ + struct nv50_bar_priv *priv = (void *)bar; + int ret; + + ret = nvkm_vm_get(priv->bar1_vm, mem->size << 12, 12, flags, vma); + if (ret) + return ret; + + nvkm_vm_map(vma, mem); + return 0; +} + +static void +nv50_bar_unmap(struct nvkm_bar *bar, struct nvkm_vma *vma) +{ + nvkm_vm_unmap(vma); + nvkm_vm_put(vma); +} + +static void +nv50_bar_flush(struct nvkm_bar *bar) +{ + struct nv50_bar_priv *priv = (void *)bar; + unsigned long flags; + spin_lock_irqsave(&priv->lock, flags); + nv_wr32(priv, 0x00330c, 0x00000001); + if (!nv_wait(priv, 0x00330c, 0x00000002, 0x00000000)) + nv_warn(priv, "flush timeout\n"); + spin_unlock_irqrestore(&priv->lock, flags); +} + +void +g84_bar_flush(struct nvkm_bar *bar) +{ + struct nv50_bar_priv *priv = (void *)bar; + unsigned long flags; + spin_lock_irqsave(&priv->lock, flags); + nv_wr32(bar, 0x070000, 0x00000001); + if (!nv_wait(priv, 0x070000, 0x00000002, 0x00000000)) + nv_warn(priv, "flush timeout\n"); + spin_unlock_irqrestore(&priv->lock, flags); +} + +static int +nv50_bar_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_device *device = nv_device(parent); + struct nvkm_object *heap; + struct nvkm_vm *vm; + struct nv50_bar_priv *priv; + u64 start, limit; + int ret; + + ret = nvkm_bar_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(nv_object(priv), NULL, 0x20000, 0, + NVOBJ_FLAG_HEAP, &priv->mem); + heap = nv_object(priv->mem); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(nv_object(priv), heap, + (device->chipset == 0x50) ? 0x1400 : 0x0200, + 0, 0, &priv->pad); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(nv_object(priv), heap, 0x4000, 0, 0, &priv->pgd); + if (ret) + return ret; + + /* BAR3 */ + start = 0x0100000000ULL; + limit = start + nv_device_resource_len(device, 3); + + ret = nvkm_vm_new(device, start, limit, start, &vm); + if (ret) + return ret; + + atomic_inc(&vm->engref[NVDEV_SUBDEV_BAR]); + + ret = nvkm_gpuobj_new(nv_object(priv), heap, + ((limit-- - start) >> 12) * 8, 0x1000, + NVOBJ_FLAG_ZERO_ALLOC, &vm->pgt[0].obj[0]); + vm->pgt[0].refcount[0] = 1; + if (ret) + return ret; + + ret = nvkm_vm_ref(vm, &priv->bar3_vm, priv->pgd); + nvkm_vm_ref(NULL, &vm, NULL); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(nv_object(priv), heap, 24, 16, 0, &priv->bar3); + if (ret) + return ret; + + nv_wo32(priv->bar3, 0x00, 0x7fc00000); + nv_wo32(priv->bar3, 0x04, lower_32_bits(limit)); + nv_wo32(priv->bar3, 0x08, lower_32_bits(start)); + nv_wo32(priv->bar3, 0x0c, upper_32_bits(limit) << 24 | + upper_32_bits(start)); + nv_wo32(priv->bar3, 0x10, 0x00000000); + nv_wo32(priv->bar3, 0x14, 0x00000000); + + /* BAR1 */ + start = 0x0000000000ULL; + limit = start + nv_device_resource_len(device, 1); + + ret = nvkm_vm_new(device, start, limit--, start, &vm); + if (ret) + return ret; + + atomic_inc(&vm->engref[NVDEV_SUBDEV_BAR]); + + ret = nvkm_vm_ref(vm, &priv->bar1_vm, priv->pgd); + nvkm_vm_ref(NULL, &vm, NULL); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(nv_object(priv), heap, 24, 16, 0, &priv->bar1); + if (ret) + return ret; + + nv_wo32(priv->bar1, 0x00, 0x7fc00000); + nv_wo32(priv->bar1, 0x04, lower_32_bits(limit)); + nv_wo32(priv->bar1, 0x08, lower_32_bits(start)); + nv_wo32(priv->bar1, 0x0c, upper_32_bits(limit) << 24 | + upper_32_bits(start)); + nv_wo32(priv->bar1, 0x10, 0x00000000); + nv_wo32(priv->bar1, 0x14, 0x00000000); + + priv->base.alloc = nvkm_bar_alloc; + priv->base.kmap = nv50_bar_kmap; + priv->base.umap = nv50_bar_umap; + priv->base.unmap = nv50_bar_unmap; + if (device->chipset == 0x50) + priv->base.flush = nv50_bar_flush; + else + priv->base.flush = g84_bar_flush; + spin_lock_init(&priv->lock); + return 0; +} + +static void +nv50_bar_dtor(struct nvkm_object *object) +{ + struct nv50_bar_priv *priv = (void *)object; + nvkm_gpuobj_ref(NULL, &priv->bar1); + nvkm_vm_ref(NULL, &priv->bar1_vm, priv->pgd); + nvkm_gpuobj_ref(NULL, &priv->bar3); + if (priv->bar3_vm) { + nvkm_gpuobj_ref(NULL, &priv->bar3_vm->pgt[0].obj[0]); + nvkm_vm_ref(NULL, &priv->bar3_vm, priv->pgd); + } + nvkm_gpuobj_ref(NULL, &priv->pgd); + nvkm_gpuobj_ref(NULL, &priv->pad); + nvkm_gpuobj_ref(NULL, &priv->mem); + nvkm_bar_destroy(&priv->base); +} + +static int +nv50_bar_init(struct nvkm_object *object) +{ + struct nv50_bar_priv *priv = (void *)object; + int ret, i; + + ret = nvkm_bar_init(&priv->base); + if (ret) + return ret; + + nv_mask(priv, 0x000200, 0x00000100, 0x00000000); + nv_mask(priv, 0x000200, 0x00000100, 0x00000100); + nv_wr32(priv, 0x100c80, 0x00060001); + if (!nv_wait(priv, 0x100c80, 0x00000001, 0x00000000)) { + nv_error(priv, "vm flush timeout\n"); + return -EBUSY; + } + + nv_wr32(priv, 0x001704, 0x00000000 | priv->mem->addr >> 12); + nv_wr32(priv, 0x001704, 0x40000000 | priv->mem->addr >> 12); + nv_wr32(priv, 0x001708, 0x80000000 | priv->bar1->node->offset >> 4); + nv_wr32(priv, 0x00170c, 0x80000000 | priv->bar3->node->offset >> 4); + for (i = 0; i < 8; i++) + nv_wr32(priv, 0x001900 + (i * 4), 0x00000000); + return 0; +} + +static int +nv50_bar_fini(struct nvkm_object *object, bool suspend) +{ + struct nv50_bar_priv *priv = (void *)object; + return nvkm_bar_fini(&priv->base, suspend); +} + +struct nvkm_oclass +nv50_bar_oclass = { + .handle = NV_SUBDEV(BAR, 0x50), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_bar_ctor, + .dtor = nv50_bar_dtor, + .init = nv50_bar_init, + .fini = nv50_bar_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bar/priv.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bar/priv.h new file mode 100644 index 000000000..aa85f61b4 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bar/priv.h @@ -0,0 +1,30 @@ +#ifndef __NVKM_BAR_PRIV_H__ +#define __NVKM_BAR_PRIV_H__ +#include + +#define nvkm_bar_create(p,e,o,d) \ + nvkm_bar_create_((p), (e), (o), sizeof(**d), (void **)d) +#define nvkm_bar_init(p) \ + nvkm_subdev_init(&(p)->base) +#define nvkm_bar_fini(p,s) \ + nvkm_subdev_fini(&(p)->base, (s)) + +int nvkm_bar_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, int, void **); +void nvkm_bar_destroy(struct nvkm_bar *); + +void _nvkm_bar_dtor(struct nvkm_object *); +#define _nvkm_bar_init _nvkm_subdev_init +#define _nvkm_bar_fini _nvkm_subdev_fini + +int nvkm_bar_alloc(struct nvkm_bar *, struct nvkm_object *, + struct nvkm_mem *, struct nvkm_object **); + +void g84_bar_flush(struct nvkm_bar *); + +int gf100_bar_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); +void gf100_bar_dtor(struct nvkm_object *); +int gf100_bar_init(struct nvkm_object *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/Kbuild new file mode 100644 index 000000000..64730d5e9 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/Kbuild @@ -0,0 +1,37 @@ +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/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/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/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/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/M0203.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/M0203.c new file mode 100644 index 000000000..08eb03fbc --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/M0203.c @@ -0,0 +1,128 @@ +/* + * 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 = nv_ro16(bios, bit_M.offset + 0x03); + if (data) { + *ver = nv_ro08(bios, data + 0x00); + switch (*ver) { + case 0x10: + *hdr = nv_ro08(bios, data + 0x01); + *len = nv_ro08(bios, data + 0x02); + *cnt = nv_ro08(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 = nv_ro08(bios, data + 0x04); + info->pointer = nv_ro16(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 = (nv_ro08(bios, data + 0x00) & 0x0f) >> 0; + info->strap = (nv_ro08(bios, data + 0x00) & 0xf0) >> 4; + info->group = (nv_ro08(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 nvbios_M0203T M0203T; + u8 cnt, len, idx = 0xff; + u32 data; + + if (!nvbios_M0203Tp(bios, ver, hdr, &cnt, &len, &M0203T)) { + nv_warn(bios, "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: + nv_warn(bios, "M0203T type %02x\n", M0203T.type); + return 0x00000000; + } + } + + return data; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/M0205.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/M0205.c new file mode 100644 index 000000000..e1a8ad5f3 --- /dev/null +++ b/kernel/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 = nv_ro32(bios, bit_M.offset + 0x05); + if (data) { + *ver = nv_ro08(bios, data + 0x00); + switch (*ver) { + case 0x10: + *hdr = nv_ro08(bios, data + 0x01); + *len = nv_ro08(bios, data + 0x02); + *ssz = nv_ro08(bios, data + 0x03); + *snr = nv_ro08(bios, data + 0x04); + *cnt = nv_ro08(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 = nv_ro16(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 = nv_ro08(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 = nv_ro08(bios, data + 0x00); + return data; + default: + break; + } + return 0x00000000; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/M0209.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/M0209.c new file mode 100644 index 000000000..3026920c3 --- /dev/null +++ b/kernel/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 = nv_ro32(bios, bit_M.offset + 0x09); + if (data) { + *ver = nv_ro08(bios, data + 0x00); + switch (*ver) { + case 0x10: + *hdr = nv_ro08(bios, data + 0x01); + *len = nv_ro08(bios, data + 0x02); + *ssz = nv_ro08(bios, data + 0x03); + *snr = 1; + *cnt = nv_ro08(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 = (nv_ro08(bios, data + 0x00) & 0x40) >> 6; + info->bits = nv_ro08(bios, data + 0x00) & 0x3f; + info->modulo = nv_ro08(bios, data + 0x01); + info->v02_40 = (nv_ro08(bios, data + 0x02) & 0x40) >> 6; + info->v02_07 = nv_ro08(bios, data + 0x02) & 0x07; + info->v03 = nv_ro08(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] = nv_ro32(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/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/P0260.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/P0260.c new file mode 100644 index 000000000..b72edcf84 --- /dev/null +++ b/kernel/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 = nv_ro32(bios, bit_P.offset + 0x60); + if (data) { + *ver = nv_ro08(bios, data + 0); + switch (*ver) { + case 0x10: + *hdr = nv_ro08(bios, data + 1); + *cnt = nv_ro08(bios, data + 2); + *len = 4; + *xnr = nv_ro08(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 = nv_ro32(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 = nv_ro32(bios, data); + return data; + default: + break; + } + return 0x00000000; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/base.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/base.c new file mode 100644 index 000000000..8db204f92 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/base.c @@ -0,0 +1,206 @@ +/* + * 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 + +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_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 u8 +nvkm_bios_rd08(struct nvkm_object *object, u64 addr) +{ + struct nvkm_bios *bios = (void *)object; + return bios->data[addr]; +} + +static u16 +nvkm_bios_rd16(struct nvkm_object *object, u64 addr) +{ + struct nvkm_bios *bios = (void *)object; + return get_unaligned_le16(&bios->data[addr]); +} + +static u32 +nvkm_bios_rd32(struct nvkm_object *object, u64 addr) +{ + struct nvkm_bios *bios = (void *)object; + return get_unaligned_le32(&bios->data[addr]); +} + +static void +nvkm_bios_wr08(struct nvkm_object *object, u64 addr, u8 data) +{ + struct nvkm_bios *bios = (void *)object; + bios->data[addr] = data; +} + +static void +nvkm_bios_wr16(struct nvkm_object *object, u64 addr, u16 data) +{ + struct nvkm_bios *bios = (void *)object; + put_unaligned_le16(data, &bios->data[addr]); +} + +static void +nvkm_bios_wr32(struct nvkm_object *object, u64 addr, u32 data) +{ + struct nvkm_bios *bios = (void *)object; + put_unaligned_le32(data, &bios->data[addr]); +} + +static int +nvkm_bios_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_bios *bios; + struct bit_entry bit_i; + int ret; + + ret = nvkm_subdev_create(parent, engine, oclass, 0, + "VBIOS", "bios", &bios); + *pobject = nv_object(bios); + if (ret) + return ret; + + ret = nvbios_shadow(bios); + if (ret) + return ret; + + /* 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) { + nv_info(bios, "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) + nv_info(bios, "BIT signature found\n"); + + /* determine the vbios version number */ + if (!bit_entry(bios, 'i', &bit_i) && bit_i.length >= 4) { + bios->version.major = nv_ro08(bios, bit_i.offset + 3); + bios->version.chip = nv_ro08(bios, bit_i.offset + 2); + bios->version.minor = nv_ro08(bios, bit_i.offset + 1); + bios->version.micro = nv_ro08(bios, bit_i.offset + 0); + bios->version.patch = nv_ro08(bios, bit_i.offset + 4); + } else + if (bmp_version(bios)) { + bios->version.major = nv_ro08(bios, bios->bmp_offset + 13); + bios->version.chip = nv_ro08(bios, bios->bmp_offset + 12); + bios->version.minor = nv_ro08(bios, bios->bmp_offset + 11); + bios->version.micro = nv_ro08(bios, bios->bmp_offset + 10); + } + + nv_info(bios, "version %02x.%02x.%02x.%02x.%02x\n", + bios->version.major, bios->version.chip, + bios->version.minor, bios->version.micro, bios->version.patch); + + return 0; +} + +static void +nvkm_bios_dtor(struct nvkm_object *object) +{ + struct nvkm_bios *bios = (void *)object; + kfree(bios->data); + nvkm_subdev_destroy(&bios->base); +} + +static int +nvkm_bios_init(struct nvkm_object *object) +{ + struct nvkm_bios *bios = (void *)object; + return nvkm_subdev_init(&bios->base); +} + +static int +nvkm_bios_fini(struct nvkm_object *object, bool suspend) +{ + struct nvkm_bios *bios = (void *)object; + return nvkm_subdev_fini(&bios->base, suspend); +} + +struct nvkm_oclass +nvkm_bios_oclass = { + .handle = NV_SUBDEV(VBIOS, 0x00), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nvkm_bios_ctor, + .dtor = nvkm_bios_dtor, + .init = nvkm_bios_init, + .fini = nvkm_bios_fini, + .rd08 = nvkm_bios_rd08, + .rd16 = nvkm_bios_rd16, + .rd32 = nvkm_bios_rd32, + .wr08 = nvkm_bios_wr08, + .wr16 = nvkm_bios_wr16, + .wr32 = nvkm_bios_wr32, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/bit.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/bit.c new file mode 100644 index 000000000..eab540496 --- /dev/null +++ b/kernel/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 = nv_ro08(bios, bios->bit_offset + 10); + u32 entry = bios->bit_offset + 12; + while (entries--) { + if (nv_ro08(bios, entry + 0) == id) { + bit->id = nv_ro08(bios, entry + 0); + bit->version = nv_ro08(bios, entry + 1); + bit->length = nv_ro16(bios, entry + 2); + bit->offset = nv_ro16(bios, entry + 4); + return 0; + } + + entry += nv_ro08(bios, bios->bit_offset + 9); + } + + return -ENOENT; + } + + return -EINVAL; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/boost.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/boost.c new file mode 100644 index 000000000..12e958533 --- /dev/null +++ b/kernel/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 + +u16 +nvbios_boostTe(struct nvkm_bios *bios, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz) +{ + struct bit_entry bit_P; + u16 boost = 0x0000; + + if (!bit_entry(bios, 'P', &bit_P)) { + if (bit_P.version == 2) + boost = nv_ro16(bios, bit_P.offset + 0x30); + + if (boost) { + *ver = nv_ro08(bios, boost + 0); + switch (*ver) { + case 0x11: + *hdr = nv_ro08(bios, boost + 1); + *cnt = nv_ro08(bios, boost + 5); + *len = nv_ro08(bios, boost + 2); + *snr = nv_ro08(bios, boost + 4); + *ssz = nv_ro08(bios, boost + 3); + return boost; + default: + break; + } + } + } + + return 0x0000; +} + +u16 +nvbios_boostEe(struct nvkm_bios *bios, int idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + u8 snr, ssz; + u16 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 0x0000; +} + +u16 +nvbios_boostEp(struct nvkm_bios *bios, int idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_boostE *info) +{ + u16 data = nvbios_boostEe(bios, idx, ver, hdr, cnt, len); + memset(info, 0x00, sizeof(*info)); + if (data) { + info->pstate = (nv_ro16(bios, data + 0x00) & 0x01e0) >> 5; + info->min = nv_ro16(bios, data + 0x02) * 1000; + info->max = nv_ro16(bios, data + 0x04) * 1000; + } + return data; +} + +u16 +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; +} + +u16 +nvbios_boostSe(struct nvkm_bios *bios, int idx, + u16 data, u8 *ver, u8 *hdr, u8 cnt, u8 len) +{ + if (data && idx < cnt) { + data = data + *hdr + (idx * len); + *hdr = len; + return data; + } + return 0x0000; +} + +u16 +nvbios_boostSp(struct nvkm_bios *bios, int idx, + u16 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 = nv_ro08(bios, data + 0x00); + info->percent = nv_ro08(bios, data + 0x01); + info->min = nv_ro16(bios, data + 0x02) * 1000; + info->max = nv_ro16(bios, data + 0x04) * 1000; + } + return data; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/conn.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/conn.c new file mode 100644 index 000000000..706a1650a --- /dev/null +++ b/kernel/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 = nv_ro16(bios, dcb + 0x14); + if (data) { + *ver = nv_ro08(bios, data + 0); + *hdr = nv_ro08(bios, data + 1); + *cnt = nv_ro08(bios, data + 2); + *len = nv_ro08(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 = nv_ro08(bios, data + 0x00); + info->location = nv_ro08(bios, data + 0x01) & 0x0f; + info->hpd = (nv_ro08(bios, data + 0x01) & 0x30) >> 4; + info->dp = (nv_ro08(bios, data + 0x01) & 0xc0) >> 6; + if (*len < 4) + return data; + info->hpd |= (nv_ro08(bios, data + 0x02) & 0x03) << 2; + info->dp |= nv_ro08(bios, data + 0x02) & 0x0c; + info->di = (nv_ro08(bios, data + 0x02) & 0xf0) >> 4; + info->hpd |= (nv_ro08(bios, data + 0x03) & 0x07) << 4; + info->sr = (nv_ro08(bios, data + 0x03) & 0x08) >> 3; + info->lcdid = (nv_ro08(bios, data + 0x03) & 0x70) >> 4; + return data; + default: + break; + } + return 0x00000000; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/cstep.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/cstep.c new file mode 100644 index 000000000..16f7ad8a4 --- /dev/null +++ b/kernel/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 + +u16 +nvbios_cstepTe(struct nvkm_bios *bios, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *xnr, u8 *xsz) +{ + struct bit_entry bit_P; + u16 cstep = 0x0000; + + if (!bit_entry(bios, 'P', &bit_P)) { + if (bit_P.version == 2) + cstep = nv_ro16(bios, bit_P.offset + 0x34); + + if (cstep) { + *ver = nv_ro08(bios, cstep + 0); + switch (*ver) { + case 0x10: + *hdr = nv_ro08(bios, cstep + 1); + *cnt = nv_ro08(bios, cstep + 3); + *len = nv_ro08(bios, cstep + 2); + *xnr = nv_ro08(bios, cstep + 5); + *xsz = nv_ro08(bios, cstep + 4); + return cstep; + default: + break; + } + } + } + + return 0x0000; +} + +u16 +nvbios_cstepEe(struct nvkm_bios *bios, int idx, u8 *ver, u8 *hdr) +{ + u8 cnt, len, xnr, xsz; + u16 data = nvbios_cstepTe(bios, ver, hdr, &cnt, &len, &xnr, &xsz); + if (data && idx < cnt) { + data = data + *hdr + (idx * len); + *hdr = len; + return data; + } + return 0x0000; +} + +u16 +nvbios_cstepEp(struct nvkm_bios *bios, int idx, u8 *ver, u8 *hdr, + struct nvbios_cstepE *info) +{ + u16 data = nvbios_cstepEe(bios, idx, ver, hdr); + memset(info, 0x00, sizeof(*info)); + if (data) { + info->pstate = (nv_ro16(bios, data + 0x00) & 0x01e0) >> 5; + info->index = nv_ro08(bios, data + 0x03); + } + return data; +} + +u16 +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; +} + +u16 +nvbios_cstepXe(struct nvkm_bios *bios, int idx, u8 *ver, u8 *hdr) +{ + u8 cnt, len, xnr, xsz; + u16 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 0x0000; +} + +u16 +nvbios_cstepXp(struct nvkm_bios *bios, int idx, u8 *ver, u8 *hdr, + struct nvbios_cstepX *info) +{ + u16 data = nvbios_cstepXe(bios, idx, ver, hdr); + memset(info, 0x00, sizeof(*info)); + if (data) { + info->freq = nv_ro16(bios, data + 0x00) * 1000; + info->unkn[0] = nv_ro08(bios, data + 0x02); + info->unkn[1] = nv_ro08(bios, data + 0x03); + info->voltage = nv_ro08(bios, data + 0x04); + } + return data; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/dcb.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/dcb.c new file mode 100644 index 000000000..8d78140f9 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/dcb.c @@ -0,0 +1,234 @@ +/* + * 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_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + struct nvkm_device *device = nv_device(bios); + u16 dcb = 0x0000; + + if (device->card_type > NV_04) + dcb = nv_ro16(bios, 0x36); + if (!dcb) { + nv_warn(bios, "DCB table not found\n"); + return dcb; + } + + *ver = nv_ro08(bios, dcb); + + if (*ver >= 0x42) { + nv_warn(bios, "DCB version 0x%02x unknown\n", *ver); + return 0x0000; + } else + if (*ver >= 0x30) { + if (nv_ro32(bios, dcb + 6) == 0x4edcbdcb) { + *hdr = nv_ro08(bios, dcb + 1); + *cnt = nv_ro08(bios, dcb + 2); + *len = nv_ro08(bios, dcb + 3); + return dcb; + } + } else + if (*ver >= 0x20) { + if (nv_ro32(bios, dcb + 4) == 0x4edcbdcb) { + u16 i2c = nv_ro16(bios, dcb + 2); + *hdr = 8; + *cnt = (i2c - dcb) / 8; + *len = 8; + return dcb; + } + } else + if (*ver >= 0x15) { + if (!nv_memcmp(bios, dcb - 7, "DEV_REC", 7)) { + u16 i2c = nv_ro16(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 + */ + nv_warn(bios, "DCB contains no useful data\n"); + return 0x0000; + } + + nv_warn(bios, "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 = nv_ro32(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 = nv_ro32(bios, dcb + 0x04); + switch (outp->type) { + case DCB_OUTPUT_DP: + switch (conf & 0x00e00000) { + case 0x00000000: + outp->dpconf.link_bw = 0x06; + break; + case 0x00200000: + outp->dpconf.link_bw = 0x0a; + break; + case 0x00400000: + default: + outp->dpconf.link_bw = 0x14; + break; + } + + outp->dpconf.link_nr = (conf & 0x0f000000) >> 24; + if (*ver < 0x41) { + switch (outp->dpconf.link_nr) { + case 0x0f: + outp->dpconf.link_nr = 4; + break; + case 0x03: + outp->dpconf.link_nr = 2; + break; + case 0x01: + default: + outp->dpconf.link_nr = 1; + break; + } + } + + /* fall-through... */ + 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 (nv_ro32(bios, outp) == 0x00000000) + break; /* seen on an NV11 with DCB v1.5 */ + if (nv_ro32(bios, outp) == 0xffffffff) + break; /* seen on an NV17 with DCB v2.0 */ + + if (nv_ro08(bios, outp) == DCB_OUTPUT_UNUSED) + continue; + if (nv_ro08(bios, outp) == DCB_OUTPUT_EOL) + break; + + ret = exec(bios, data, idx, outp); + if (ret) + return ret; + } + + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/disp.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/disp.c new file mode 100644 index 000000000..262c410b7 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/disp.c @@ -0,0 +1,172 @@ +/* + * 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 = nv_ro16(bios, U.offset); + if (data) { + *ver = nv_ro08(bios, data + 0x00); + switch (*ver) { + case 0x20: + case 0x21: + case 0x22: + *hdr = nv_ro08(bios, data + 0x01); + *len = nv_ro08(bios, data + 0x02); + *cnt = nv_ro08(bios, data + 0x03); + *sub = nv_ro08(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 = nv_ro16(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 = nv_ro08(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 = nv_ro16(bios, data + 0x00); + info->mask = nv_ro32(bios, data + 0x02); + if (*ver <= 0x20) /* match any link */ + info->mask |= 0x00c0; + info->script[0] = nv_ro16(bios, data + 0x06); + info->script[1] = nv_ro16(bios, data + 0x08); + info->script[2] = 0x0000; + if (*hdr >= 0x0c) + info->script[2] = nv_ro16(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->match = nv_ro16(bios, data + 0x00); + info->clkcmp[0] = nv_ro16(bios, data + 0x02); + info->clkcmp[1] = nv_ro16(bios, data + 0x04); + } + return data; +} + +u16 +nvbios_ocfg_match(struct nvkm_bios *bios, u16 outp, u16 type, + 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->match == type) + break; + } + return data; +} + +u16 +nvbios_oclk_match(struct nvkm_bios *bios, u16 cmp, u32 khz) +{ + while (cmp) { + if (khz / 10 >= nv_ro16(bios, cmp + 0x00)) + return nv_ro16(bios, cmp + 0x02); + cmp += 0x04; + } + return 0x0000; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/dp.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/dp.c new file mode 100644 index 000000000..95970faae --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/dp.c @@ -0,0 +1,215 @@ +/* + * 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 +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 = nv_ro16(bios, d.offset); + if (data) { + *ver = nv_ro08(bios, data + 0x00); + switch (*ver) { + case 0x21: + case 0x30: + case 0x40: + case 0x41: + *hdr = nv_ro08(bios, data + 0x01); + *len = nv_ro08(bios, data + 0x02); + *cnt = nv_ro08(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 = nv_ro16(bios, data + *hdr + idx * *len); + switch (*ver * !!outp) { + case 0x21: + case 0x30: + *hdr = nv_ro08(bios, data + 0x04); + *len = nv_ro08(bios, data + 0x05); + *cnt = nv_ro08(bios, outp + 0x04); + break; + case 0x40: + case 0x41: + *hdr = nv_ro08(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 = nv_ro16(bios, data + 0x00); + info->mask = nv_ro16(bios, data + 0x02); + switch (*ver) { + case 0x21: + case 0x30: + info->flags = nv_ro08(bios, data + 0x05); + info->script[0] = nv_ro16(bios, data + 0x06); + info->script[1] = nv_ro16(bios, data + 0x08); + info->lnkcmp = nv_ro16(bios, data + 0x0a); + if (*len >= 0x0f) { + info->script[2] = nv_ro16(bios, data + 0x0c); + info->script[3] = nv_ro16(bios, data + 0x0e); + } + if (*len >= 0x11) + info->script[4] = nv_ro16(bios, data + 0x10); + break; + case 0x40: + case 0x41: + info->flags = nv_ro08(bios, data + 0x04); + info->script[0] = nv_ro16(bios, data + 0x05); + info->script[1] = nv_ro16(bios, data + 0x07); + info->lnkcmp = nv_ro16(bios, data + 0x09); + info->script[2] = nv_ro16(bios, data + 0x0b); + info->script[3] = nv_ro16(bios, data + 0x0d); + info->script[4] = nv_ro16(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 = nv_ro08(bios, outp + 0x06); + *cnt = nv_ro08(bios, outp + 0x07); + } + + 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 0x21: + info->dc = nv_ro08(bios, data + 0x02); + info->pe = nv_ro08(bios, data + 0x03); + info->tx_pu = nv_ro08(bios, data + 0x04); + break; + case 0x30: + case 0x40: + case 0x41: + info->pc = nv_ro08(bios, data + 0x00); + info->dc = nv_ro08(bios, data + 0x01); + info->pe = nv_ro08(bios, data + 0x02); + info->tx_pu = nv_ro08(bios, data + 0x03) & 0x0f; + 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) { + /*XXX: there's a second set of these on at least 4.1, that + * i've witnessed nvidia using instead of the first + * on gm204. figure out what/why + */ + const u8 vsoff[] = { 0, 4, 7, 9 }; + idx = (pc * 10) + vsoff[vs] + pe; + } else { + while ((data = nvbios_dpcfg_entry(bios, outp, ++idx, + ver, hdr, cnt, len))) { + if (nv_ro08(bios, data + 0x00) == vs && + nv_ro08(bios, data + 0x01) == pe) + break; + } + } + + return nvbios_dpcfg_parse(bios, outp, idx, ver, hdr, cnt, len, info); +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/extdev.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/extdev.c new file mode 100644 index 000000000..a8503a185 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/extdev.c @@ -0,0 +1,97 @@ +/* + * 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)) + return 0x0000; + + extdev = nv_ro16(bios, dcb + 18); + if (!extdev) + return 0x0000; + + *ver = nv_ro08(bios, extdev + 0); + *hdr = nv_ro08(bios, extdev + 1); + *cnt = nv_ro08(bios, extdev + 2); + *len = nv_ro08(bios, extdev + 3); + return extdev + *hdr; +} + +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 = nv_ro08(bios, offset + 0); + entry->addr = nv_ro08(bios, offset + 1); + entry->bus = (nv_ro08(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/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/fan.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/fan.c new file mode 100644 index 000000000..8dba70d9d --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/fan.c @@ -0,0 +1,93 @@ +/* + * 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 + +u16 +nvbios_fan_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + struct bit_entry bit_P; + u16 fan = 0x0000; + + if (!bit_entry(bios, 'P', &bit_P)) { + if (bit_P.version == 2 && bit_P.length >= 0x5a) + fan = nv_ro16(bios, bit_P.offset + 0x58); + + if (fan) { + *ver = nv_ro08(bios, fan + 0); + switch (*ver) { + case 0x10: + *hdr = nv_ro08(bios, fan + 1); + *len = nv_ro08(bios, fan + 2); + *cnt = nv_ro08(bios, fan + 3); + return fan; + default: + break; + } + } + } + + return 0x0000; +} + +u16 +nvbios_fan_entry(struct nvkm_bios *bios, int idx, u8 *ver, u8 *hdr, + u8 *cnt, u8 *len) +{ + u16 data = nvbios_fan_table(bios, ver, hdr, cnt, len); + if (data && idx < *cnt) + return data + *hdr + (idx * (*len)); + return 0x0000; +} + +u16 +nvbios_fan_parse(struct nvkm_bios *bios, struct nvbios_therm_fan *fan) +{ + u8 ver, hdr, cnt, len; + + u16 data = nvbios_fan_entry(bios, 0, &ver, &hdr, &cnt, &len); + if (data) { + u8 type = nv_ro08(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->min_duty = nv_ro08(bios, data + 0x02); + fan->max_duty = nv_ro08(bios, data + 0x03); + + fan->pwm_freq = nv_ro32(bios, data + 0x0b) & 0xffffff; + } + + return data; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/gpio.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/gpio.c new file mode 100644 index 000000000..8ce154d88 --- /dev/null +++ b/kernel/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 = nv_ro16(bios, dcb + 0x0a); + else + if (*ver >= 0x22 && nv_ro08(bios, dcb - 1) >= 0x13) + data = nv_ro16(bios, dcb - 0x0f); + + if (data) { + *ver = nv_ro08(bios, data + 0x00); + if (*ver < 0x30) { + *hdr = 3; + *cnt = nv_ro08(bios, data + 0x02); + *len = nv_ro08(bios, data + 0x01); + } else + if (*ver <= 0x41) { + *hdr = nv_ro08(bios, data + 0x01); + *cnt = nv_ro08(bios, data + 0x02); + *len = nv_ro08(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 = nv_ro16(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 = nv_ro32(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 = nv_ro32(bios, data + 0); + u8 info1 = nv_ro32(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 = nv_ro08(bios, data - 5); + u8 addr = nv_ro08(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/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/i2c.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/i2c.c new file mode 100644 index 000000000..c4e1f085e --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/i2c.c @@ -0,0 +1,163 @@ +/* + * 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 = nv_ro16(bios, dcb + 2); + if (*ver >= 0x30) + i2c = nv_ro16(bios, dcb + 4); + } + + if (i2c && *ver >= 0x42) { + nv_warn(bios, "ccb %02x not supported\n", *ver); + return 0x0000; + } + + if (i2c && *ver >= 0x30) { + *ver = nv_ro08(bios, i2c + 0); + *hdr = nv_ro08(bios, i2c + 1); + *cnt = nv_ro08(bios, i2c + 2); + *len = nv_ro08(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) +{ + u8 ver, len; + u16 ent = dcb_i2c_entry(bios, idx, &ver, &len); + if (ent) { + if (ver >= 0x41) { + u32 ent_value = nv_ro32(bios, ent); + u8 i2c_port = (ent_value >> 27) & 0x1f; + u8 dpaux_port = (ent_value >> 22) & 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 = nv_ro08(bios, ent + 0x03); + } else { + info->type = nv_ro08(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 = nv_ro08(bios, ent + 0); + info->sense = nv_ro08(bios, ent + 1); + return 0; + case DCB_I2C_NV4E_BIT: + info->drive = nv_ro08(bios, ent + 1); + return 0; + case DCB_I2C_NVIO_BIT: + info->drive = nv_ro08(bios, ent + 0) & 0x0f; + if (nv_ro08(bios, ent + 1) & 0x01) + info->share = nv_ro08(bios, ent + 1) >> 1; + return 0; + case DCB_I2C_NVIO_AUX: + info->auxch = nv_ro08(bios, ent + 0) & 0x0f; + if (nv_ro08(bios, ent + 1) & 0x01) + info->share = info->auxch; + return 0; + case DCB_I2C_PMGR: + info->drive = (nv_ro16(bios, ent + 0) & 0x01f) >> 0; + if (info->drive == 0x1f) + info->drive = DCB_I2C_UNUSED; + info->auxch = (nv_ro16(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: + nv_warn(bios, "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 (nv_ro08(bios, bios->bmp_offset + 5) < 4) + ent = 0x0048; + else + ent = 0x0036 + bios->bmp_offset; + + if (idx == 0) { + info->drive = nv_ro08(bios, ent + 4); + if (!info->drive) info->drive = 0x3f; + info->sense = nv_ro08(bios, ent + 5); + if (!info->sense) info->sense = 0x3e; + } else + if (idx == 1) { + info->drive = nv_ro08(bios, ent + 6); + if (!info->drive) info->drive = 0x37; + info->sense = nv_ro08(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/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/image.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/image.c new file mode 100644 index 000000000..1815540a0 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/image.c @@ -0,0 +1,77 @@ +/* + * 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 nvbios_pcirT pcir; + struct nvbios_npdeT npde; + u8 ver; + u16 hdr; + u32 data; + + switch ((data = nv_ro16(bios, image->base + 0x00))) { + case 0xaa55: + case 0xbb77: + case 0x4e56: /* NV */ + break; + default: + nv_debug(bios, "%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) +{ + memset(image, 0x00, sizeof(*image)); + do { + image->base += image->size; + if (image->last || !nvbios_imagen(bios, image)) + return false; + } while(idx--); + return true; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c new file mode 100644 index 000000000..f67cdae1e --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c @@ -0,0 +1,2247 @@ +/* + * 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 { \ + nv_printk(init->bios, lvl, "0x%04x[%c]: "fmt, init->offset, \ + init_exec(init) ? '0' + (init->nested - 1) : ' ', ##args); \ +} while(0) +#define cont(fmt, args...) do { \ + if (nv_subdev(init->bios)->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->outp) + return ffs(init->outp->or) - 1; + error("script needs OR!!\n"); + } + return 0; +} + +static inline int +init_link(struct nvbios_init *init) +{ + if (init_exec(init)) { + if (init->outp) + return !(init->outp->sorconf.link & 1); + error("script needs OR link\n"); + } + return 0; +} + +static inline int +init_crtc(struct nvbios_init *init) +{ + if (init_exec(init)) { + if (init->crtc >= 0) + return init->crtc; + error("script needs crtc\n"); + } + return 0; +} + +static u8 +init_conn(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->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 = nvkm_devinit(init->bios); + + /* 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 (nv_device(init->bios)->card_type >= NV_50) { + if (reg & 0x80000000) { + reg += init_crtc(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); + + if (devinit->mmio) + reg = devinit->mmio(devinit, reg); + return reg; +} + +static u32 +init_rd32(struct nvbios_init *init, u32 reg) +{ + reg = init_nvreg(init, reg); + if (reg != ~0 && init_exec(init)) + return nv_rd32(init->subdev, reg); + return 0x00000000; +} + +static void +init_wr32(struct nvbios_init *init, u32 reg, u32 val) +{ + reg = init_nvreg(init, reg); + if (reg != ~0 && init_exec(init)) + nv_wr32(init->subdev, reg, val); +} + +static u32 +init_mask(struct nvbios_init *init, u32 reg, u32 mask, u32 val) +{ + reg = init_nvreg(init, reg); + if (reg != ~0 && init_exec(init)) { + u32 tmp = nv_rd32(init->subdev, reg); + nv_wr32(init->subdev, reg, (tmp & ~mask) | val); + return tmp; + } + return 0x00000000; +} + +static u8 +init_rdport(struct nvbios_init *init, u16 port) +{ + if (init_exec(init)) + return nv_rdport(init->subdev, init->crtc, port); + return 0x00; +} + +static void +init_wrport(struct nvbios_init *init, u16 port, u8 value) +{ + if (init_exec(init)) + nv_wrport(init->subdev, init->crtc, 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->crtc < 0 ? 0 : init->crtc; + return nv_rdvgai(subdev, head, port, index); + } + return 0x00; +} + +static void +init_wrvgai(struct nvbios_init *init, u16 port, u8 index, u8 value) +{ + /* force head 0 for updates to cr44, it only exists on first head */ + if (nv_device(init->subdev)->card_type < NV_50) { + if (port == 0x03d4 && index == 0x44) + init->crtc = 0; + } + + if (init_exec(init)) { + int head = init->crtc < 0 ? 0 : init->crtc; + nv_wrvgai(init->subdev, head, port, index, value); + } + + /* select head 1 if cr44 write selected it */ + if (nv_device(init->subdev)->card_type < NV_50) { + if (port == 0x03d4 && index == 0x44 && value == 3) + init->crtc = 1; + } +} + +static struct nvkm_i2c_port * +init_i2c(struct nvbios_init *init, int index) +{ + struct nvkm_i2c *i2c = nvkm_i2c(init->bios); + + if (index == 0xff) { + index = NV_I2C_DEFAULT(0); + if (init->outp && init->outp->i2c_upper_default) + index = NV_I2C_DEFAULT(1); + } else + if (index < 0) { + if (!init->outp) { + if (init_exec(init)) + error("script needs output for i2c\n"); + return NULL; + } + + if (index == -2 && init->outp->location) { + index = NV_I2C_TYPE_EXTAUX(init->outp->extdev); + return i2c->find_type(i2c, index); + } + + index = init->outp->i2c_index; + if (init->outp->type == DCB_OUTPUT_DP) + index += NV_I2C_AUX(0); + } + + return i2c->find(i2c, index); +} + +static int +init_rdi2cr(struct nvbios_init *init, u8 index, u8 addr, u8 reg) +{ + struct nvkm_i2c_port *port = init_i2c(init, index); + if (port && init_exec(init)) + return nv_rdi2cr(port, addr, reg); + return -ENODEV; +} + +static int +init_wri2cr(struct nvbios_init *init, u8 index, u8 addr, u8 reg, u8 val) +{ + struct nvkm_i2c_port *port = init_i2c(init, index); + if (port && init_exec(init)) + return nv_wri2cr(port, addr, reg, val); + return -ENODEV; +} + +static u8 +init_rdauxr(struct nvbios_init *init, u32 addr) +{ + struct nvkm_i2c_port *port = init_i2c(init, -2); + u8 data; + + if (port && init_exec(init)) { + int ret = nv_rdaux(port, 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_port *port = init_i2c(init, -2); + if (port && init_exec(init)) { + int ret = nv_wraux(port, 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 = nvkm_devinit(init->bios); + if (devinit->pll_set && init_exec(init)) { + int ret = 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->bios; + u16 len, data = init_table(bios, &len); + if (data) { + if (len >= offset + 2) { + data = nv_ro16(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 conditon 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 = { .bios = bios }; + 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 nv_ro16(bios, data + (index * 2)); + } + + data = init_script_table(&init); + if (data) + return nv_ro16(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 nv_ro16(bios, data + 14); + return 0x0000; +} + +static u8 +init_ram_restrict_group_count(struct nvbios_init *init) +{ + return nvbios_ramcfg_count(init->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->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->bios; + u16 table = init_xlat_table(init); + if (table) { + u16 data = nv_ro16(bios, table + (index * 2)); + if (data) + return nv_ro08(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->bios; + u16 table = init_condition_table(init); + if (table) { + u32 reg = nv_ro32(bios, table + (cond * 12) + 0); + u32 msk = nv_ro32(bios, table + (cond * 12) + 4); + u32 val = nv_ro32(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->bios; + u16 table = init_io_condition_table(init); + if (table) { + u16 port = nv_ro16(bios, table + (cond * 5) + 0); + u8 index = nv_ro08(bios, table + (cond * 5) + 2); + u8 mask = nv_ro08(bios, table + (cond * 5) + 3); + u8 value = nv_ro08(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->bios; + u16 table = init_io_flag_condition_table(init); + if (table) { + u16 port = nv_ro16(bios, table + (cond * 9) + 0); + u8 index = nv_ro08(bios, table + (cond * 9) + 2); + u8 mask = nv_ro08(bios, table + (cond * 9) + 3); + u8 shift = nv_ro08(bios, table + (cond * 9) + 4); + u16 data = nv_ro16(bios, table + (cond * 9) + 5); + u8 dmask = nv_ro08(bios, table + (cond * 9) + 7); + u8 value = nv_ro08(bios, table + (cond * 9) + 8); + u8 ioval = (init_rdvgai(init, port, index) & mask) >> shift; + return (nv_ro08(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) +{ + u8 opcode = nv_ro08(init->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", nv_ro08(init->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->bios; + u16 port = nv_ro16(bios, init->offset + 1); + u8 index = nv_ro08(bios, init->offset + 3); + u8 mask = nv_ro08(bios, init->offset + 4); + u8 shift = nv_ro08(bios, init->offset + 5); + u8 count = nv_ro08(bios, init->offset + 6); + u32 reg = nv_ro32(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 = nv_ro32(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->bios; + u8 count = nv_ro08(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->bios; + u16 port = nv_ro16(bios, init->offset + 1); + u8 index = nv_ro08(bios, init->offset + 3); + u8 mask = nv_ro08(bios, init->offset + 4); + u8 shift = nv_ro08(bios, init->offset + 5); + s8 iofc = nv_ro08(bios, init->offset + 6); + u8 count = nv_ro08(bios, init->offset + 7); + u32 reg = nv_ro32(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 = nv_ro16(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->bios; + u32 reg = nv_ro32(bios, init->offset + 1); + u8 shift = nv_ro08(bios, init->offset + 5); + u8 smask = nv_ro08(bios, init->offset + 6); + u16 port = nv_ro16(bios, init->offset + 7); + u8 index = nv_ro08(bios, init->offset + 9); + u8 mask = nv_ro08(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->bios; + u8 cond = nv_ro08(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_DP_CONDITION - opcode 0x3a + * + */ +static void +init_dp_condition(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->bios; + struct nvbios_dpout info; + u8 cond = nv_ro08(bios, init->offset + 1); + u8 unkn = nv_ro08(bios, init->offset + 2); + u8 ver, hdr, cnt, len; + u16 data; + + trace("DP_CONDITION\t0x%02x 0x%02x\n", cond, unkn); + init->offset += 3; + + switch (cond) { + case 0: + if (init_conn(init) != DCB_CONNECTOR_eDP) + init_exec_set(init, false); + break; + case 1: + case 2: + 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: + if (!(init_rdauxr(init, 0x0d) & 1)) + init_exec_set(init, false); + break; + default: + warn("unknown dp condition 0x%02x\n", cond); + break; + } +} + +/** + * INIT_IO_MASK_OR - opcode 0x3b + * + */ +static void +init_io_mask_or(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->bios; + u8 index = nv_ro08(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->bios; + u8 index = nv_ro08(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->bios; + u32 reg = nv_ro32(bios, init->offset + 1); + u32 mask = nv_ro32(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->bios; + u32 reg = nv_ro32(bios, init->offset + 1); + u32 mask = nv_ro32(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->bios; + u32 creg = nv_ro32(bios, init->offset + 1); + u32 dreg = nv_ro32(bios, init->offset + 5); + u32 mask = nv_ro32(bios, init->offset + 9); + u32 data = nv_ro32(bios, init->offset + 13); + u8 count = nv_ro08(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 = nv_ro08(bios, init->offset + 0); + u8 idata = nv_ro08(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->bios; + u16 port = nv_ro16(bios, init->offset + 1); + u8 index = nv_ro08(bios, init->offset + 3); + u8 mask = nv_ro08(bios, init->offset + 4); + u8 shift = nv_ro08(bios, init->offset + 5); + u8 count = nv_ro08(bios, init->offset + 6); + u32 reg = nv_ro32(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 = nv_ro32(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->bios; + u32 reg = nv_ro32(bios, init->offset + 1); + u32 freq = nv_ro32(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->bios; + u8 index = nv_ro08(bios, init->offset + 1); + u8 addr = nv_ro08(bios, init->offset + 2) >> 1; + u8 count = nv_ro08(bios, init->offset + 3); + + trace("I2C_BYTE\tI2C[0x%02x][0x%02x]\n", index, addr); + init->offset += 4; + + while (count--) { + u8 reg = nv_ro08(bios, init->offset + 0); + u8 mask = nv_ro08(bios, init->offset + 1); + u8 data = nv_ro08(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->bios; + u8 index = nv_ro08(bios, init->offset + 1); + u8 addr = nv_ro08(bios, init->offset + 2) >> 1; + u8 count = nv_ro08(bios, init->offset + 3); + + trace("ZM_I2C_BYTE\tI2C[0x%02x][0x%02x]\n", index, addr); + init->offset += 4; + + while (count--) { + u8 reg = nv_ro08(bios, init->offset + 0); + u8 data = nv_ro08(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->bios; + u8 index = nv_ro08(bios, init->offset + 1); + u8 addr = nv_ro08(bios, init->offset + 2) >> 1; + u8 count = nv_ro08(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] = nv_ro08(bios, init->offset); + trace("\t0x%02x\n", data[i]); + init->offset++; + } + + if (init_exec(init)) { + struct nvkm_i2c_port *port = init_i2c(init, index); + struct i2c_msg msg = { + .addr = addr, .flags = 0, .len = count, .buf = data, + }; + int ret; + + if (port && (ret = i2c_transfer(&port->adapter, &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->bios; + u8 tmds = nv_ro08(bios, init->offset + 1); + u8 addr = nv_ro08(bios, init->offset + 2); + u8 mask = nv_ro08(bios, init->offset + 3); + u8 data = nv_ro08(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->bios; + u8 tmds = nv_ro08(bios, init->offset + 1); + u8 count = nv_ro08(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 = nv_ro08(bios, init->offset + 0); + u8 data = nv_ro08(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->bios; + u8 addr0 = nv_ro08(bios, init->offset + 1); + u8 addr1 = nv_ro08(bios, init->offset + 2); + u8 base = nv_ro08(bios, init->offset + 3); + u8 count = nv_ro08(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 = nv_ro08(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->bios; + u8 addr = nv_ro08(bios, init->offset + 1); + u8 mask = nv_ro08(bios, init->offset + 2); + u8 data = nv_ro08(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->bios; + u8 addr = nv_ro08(bios, init->offset + 1); + u8 data = nv_ro08(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->bios; + u8 count = nv_ro08(bios, init->offset + 1); + + trace("ZM_CR_GROUP\n"); + init->offset += 2; + + while (count--) { + u8 addr = nv_ro08(bios, init->offset + 0); + u8 data = nv_ro08(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->bios; + u8 cond = nv_ro08(bios, init->offset + 1); + u8 retry = nv_ro08(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->bios; + u16 msec = nv_ro16(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->bios; + u32 base = nv_ro32(bios, init->offset + 1); + u8 count = nv_ro08(bios, init->offset + 5); + + trace("ZM_REG_SEQUENCE\t0x%02x\n", count); + init->offset += 6; + + while (count--) { + u32 data = nv_ro32(bios, init->offset); + + trace("\t\tR[0x%06x] = 0x%08x\n", base, data); + init->offset += 4; + + init_wr32(init, base, data); + base += 4; + } +} + +/** + * INIT_SUB_DIRECT - opcode 0x5b + * + */ +static void +init_sub_direct(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->bios; + u16 addr = nv_ro16(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->bios; + u16 offset = nv_ro16(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->bios; + u8 index = nv_ro08(bios, init->offset + 1); + u8 addr = nv_ro08(bios, init->offset + 2); + u8 reg = nv_ro08(bios, init->offset + 3); + u8 mask = nv_ro08(bios, init->offset + 4); + u8 data = nv_ro08(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->bios; + u32 sreg = nv_ro32(bios, init->offset + 1); + u8 shift = nv_ro08(bios, init->offset + 5); + u32 smask = nv_ro32(bios, init->offset + 6); + u32 sxor = nv_ro32(bios, init->offset + 10); + u32 dreg = nv_ro32(bios, init->offset + 14); + u32 dmask = nv_ro32(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->bios; + u16 port = nv_ro16(bios, init->offset + 1); + u8 index = nv_ro08(bios, init->offset + 3); + u8 data = nv_ro08(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 = nvkm_devinit(init->bios); + + trace("COMPUTE_MEM\n"); + init->offset += 1; + + init_exec_force(init, true); + if (init_exec(init) && devinit->meminit) + 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->bios; + u32 reg = nv_ro32(bios, init->offset + 1); + u32 data1 = nv_ro32(bios, init->offset + 5); + u32 data2 = nv_ro32(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->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->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 (nv_ro08(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 = nv_ro32(bios, sdata)) != 0xffffffff; sdata += 4) { + switch (addr) { + case 0x10021c: /* CKE_NORMAL */ + case 0x1002d0: /* CMD_REFRESH */ + case 0x1002d4: /* CMD_PRECHARGE */ + data = 0x00000001; + break; + default: + data = nv_ro32(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->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 = nv_ro16(bios, mdata + 4) * 10; + init_prog_pll(init, 0x680500, clock); + + /* MPLL */ + clock = nv_ro16(bios, mdata + 2) * 10; + if (nv_ro08(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->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->bios; + u16 port = nv_ro16(bios, init->offset + 1); + u8 mask = nv_ro16(bios, init->offset + 3); + u8 data = nv_ro16(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 (nv_device(init->bios)->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->bios; + u8 index = nv_ro08(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->bios; + u8 mask = nv_ro08(bios, init->offset + 1); + u8 value = nv_ro08(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->bios; + u32 reg = nv_ro32(bios, init->offset + 1); + u32 mask = nv_ro32(bios, init->offset + 5); + u32 data = nv_ro32(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->bios; + u8 macro = nv_ro08(bios, init->offset + 1); + u16 table; + + trace("MACRO\t0x%02x\n", macro); + + table = init_macro_table(init); + if (table) { + u32 addr = nv_ro32(bios, table + (macro * 8) + 0); + u32 data = nv_ro32(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_TIME - opcode 0x74 + * + */ +static void +init_time(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->bios; + u16 usec = nv_ro16(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->bios; + u8 cond = nv_ro08(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->bios; + u8 cond = nv_ro08(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_INDEX_IO - opcode 0x78 + * + */ +static void +init_index_io(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->bios; + u16 port = nv_ro16(bios, init->offset + 1); + u8 index = nv_ro16(bios, init->offset + 3); + u8 mask = nv_ro08(bios, init->offset + 4); + u8 data = nv_ro08(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->bios; + u32 reg = nv_ro32(bios, init->offset + 1); + u32 freq = nv_ro16(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->bios; + u32 addr = nv_ro32(bios, init->offset + 1); + u32 data = nv_ro32(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->bios; + u8 type = nv_ro08(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 = nv_ro32(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_GPIO - opcode 0x8e + * + */ +static void +init_gpio(struct nvbios_init *init) +{ + struct nvkm_gpio *gpio = nvkm_gpio(init->bios); + + trace("GPIO\n"); + init->offset += 1; + + if (init_exec(init) && gpio && gpio->reset) + 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->bios; + u32 addr = nv_ro32(bios, init->offset + 1); + u8 incr = nv_ro08(bios, init->offset + 5); + u8 num = nv_ro08(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 = nv_ro32(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->bios; + u32 sreg = nv_ro32(bios, init->offset + 1); + u32 dreg = nv_ro32(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->bios; + u32 addr = nv_ro32(bios, init->offset + 1); + u8 count = nv_ro08(bios, init->offset + 5); + + trace("ZM_REG_GROUP\tR[0x%06x] =\n", addr); + init->offset += 6; + + while (count--) { + u32 data = nv_ro32(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->bios; + u32 saddr = nv_ro32(bios, init->offset + 1); + u8 sshift = nv_ro08(bios, init->offset + 5); + u8 smask = nv_ro08(bios, init->offset + 6); + u8 index = nv_ro08(bios, init->offset + 7); + u32 daddr = nv_ro32(bios, init->offset + 8); + u32 dmask = nv_ro32(bios, init->offset + 12); + u8 shift = nv_ro08(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->bios; + u32 addr = nv_ro32(bios, init->offset + 1); + u32 mask = nv_ro32(bios, init->offset + 5); + u32 add = nv_ro32(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->bios; + u32 addr = nv_ro32(bios, init->offset + 1); + u8 count = nv_ro08(bios, init->offset + 5); + + trace("AUXCH\tAUX[0x%08x] 0x%02x\n", addr, count); + init->offset += 6; + + while (count--) { + u8 mask = nv_ro08(bios, init->offset + 0); + u8 data = nv_ro08(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->bios; + u32 addr = nv_ro32(bios, init->offset + 1); + u8 count = nv_ro08(bios, init->offset + 5); + + trace("ZM_AUXCH\tAUX[0x%08x] 0x%02x\n", addr, count); + init->offset += 6; + + while (count--) { + u8 data = nv_ro08(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->bios; + u8 index = nv_ro08(bios, init->offset + 1); + u8 addr = nv_ro08(bios, init->offset + 2) >> 1; + u8 reglo = nv_ro08(bios, init->offset + 3); + u8 reghi = nv_ro08(bios, init->offset + 4); + u8 mask = nv_ro08(bios, init->offset + 5); + u8 data = nv_ro08(bios, init->offset + 6); + struct nvkm_i2c_port *port; + + 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; + + port = init_i2c(init, index); + if (port) { + 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(&port->adapter, 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->bios; + struct nvkm_gpio *gpio = nvkm_gpio(bios); + struct dcb_gpio_func func; + u8 count = nv_ro08(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 ", nv_ro08(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 == nv_ro08(bios, i)) + break; + } + + trace("\tFUNC[0x%02x]", func.func); + if (i == (init->offset + count)) { + cont(" *"); + if (init_exec(init) && gpio && gpio->reset) + 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_dp_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 }, + [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 }, + [0x74] = { init_time }, + [0x75] = { init_condition }, + [0x76] = { init_io_condition }, + [0x78] = { init_index_io }, + [0x79] = { init_pll }, + [0x7a] = { init_zm_reg }, + [0x87] = { init_ram_restrict_pll }, + [0x8c] = { init_reserved }, + [0x8d] = { init_reserved }, + [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 }, +}; + +#define init_opcode_nr (sizeof(init_opcode) / sizeof(init_opcode[0])) + +int +nvbios_exec(struct nvbios_init *init) +{ + init->nested++; + while (init->offset) { + u8 opcode = nv_ro08(init->bios, init->offset); + if (opcode >= init_opcode_nr || !init_opcode[opcode].exec) { + error("unknown opcode 0x%02x\n", opcode); + return -EINVAL; + } + + init_opcode[opcode].exec(init); + } + init->nested--; + return 0; +} + +int +nvbios_init(struct nvkm_subdev *subdev, bool execute) +{ + struct nvkm_bios *bios = nvkm_bios(subdev); + int ret = 0; + int i = -1; + u16 data; + + if (execute) + nv_info(bios, "running init tables\n"); + while (!ret && (data = (init_script(bios, ++i)))) { + struct nvbios_init init = { + .subdev = subdev, + .bios = bios, + .offset = data, + .outp = NULL, + .crtc = -1, + .execute = execute ? 1 : 0, + }; + + ret = nvbios_exec(&init); + } + + /* 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))) { + struct nvbios_init init = { + .subdev = subdev, + .bios = bios, + .offset = data, + .outp = NULL, + .crtc = -1, + .execute = execute ? 1 : 0, + }; + + ret = nvbios_exec(&init); + } + + return ret; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/mxm.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/mxm.c new file mode 100644 index 000000000..c4087df4f --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/mxm.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 + +u16 +mxm_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr) +{ + struct bit_entry x; + + if (bit_entry(bios, 'x', &x)) { + nv_debug(bios, "BIT 'x' table not present\n"); + return 0x0000; + } + + *ver = x.version; + *hdr = x.length; + if (*ver != 1 || *hdr < 3) { + nv_warn(bios, "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) +{ + u8 ver, hdr; + u16 mxm = mxm_table(bios, &ver, &hdr); + if (mxm && hdr >= 6) { + u16 map = nv_ro16(bios, mxm + 4); + if (map) { + ver = nv_ro08(bios, map); + if (ver == 0x10) { + if (conn < nv_ro08(bios, map + 3)) { + map += nv_ro08(bios, map + 1); + map += conn; + return nv_ro08(bios, map); + } + + return 0x00; + } + + nv_warn(bios, "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]; + + nv_warn(bios, "missing sor map\n"); + return 0x00; +} + +u8 +mxm_ddc_map(struct nvkm_bios *bios, u8 port) +{ + u8 ver, hdr; + u16 mxm = mxm_table(bios, &ver, &hdr); + if (mxm && hdr >= 8) { + u16 map = nv_ro16(bios, mxm + 6); + if (map) { + ver = nv_ro08(bios, map); + if (ver == 0x10) { + if (port < nv_ro08(bios, map + 3)) { + map += nv_ro08(bios, map + 1); + map += port; + return nv_ro08(bios, map); + } + + return 0x00; + } + + nv_warn(bios, "unknown ddc map v%02x\n", ver); + } + } + + /* v2.x: directly write port as dcb i2cidx */ + return (port << 4) | port; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/npde.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/npde.c new file mode 100644 index 000000000..fd7dd718b --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/npde.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 +#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 (nv_ro32(bios, data + 0x00)) { + case 0x4544504e: /* NPDE */ + break; + default: + nv_debug(bios, "%08x: NPDE signature (%08x) unknown\n", + data, nv_ro32(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 = nv_ro16(bios, data + 0x08) * 512; + info->last = nv_ro08(bios, data + 0x0a) & 0x80; + } + return data; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/pcir.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/pcir.c new file mode 100644 index 000000000..df5978753 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/pcir.c @@ -0,0 +1,68 @@ +/* + * 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 = nv_ro16(bios, base + 0x18); + if (data) { + data += base; + switch (nv_ro32(bios, data + 0x00)) { + case 0x52494350: /* PCIR */ + case 0x53494752: /* RGIS */ + case 0x5344504e: /* NPDS */ + *hdr = nv_ro16(bios, data + 0x0a); + *ver = nv_ro08(bios, data + 0x0c); + break; + default: + nv_debug(bios, "%08x: PCIR signature (%08x) unknown\n", + data, nv_ro32(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 = nv_ro16(bios, data + 0x04); + info->device_id = nv_ro16(bios, data + 0x06); + info->class_code[0] = nv_ro08(bios, data + 0x0d); + info->class_code[1] = nv_ro08(bios, data + 0x0e); + info->class_code[2] = nv_ro08(bios, data + 0x0f); + info->image_size = nv_ro16(bios, data + 0x10) * 512; + info->image_rev = nv_ro16(bios, data + 0x12); + info->image_type = nv_ro08(bios, data + 0x14); + info->last = nv_ro08(bios, data + 0x15) & 0x80; + } + return data; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/perf.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/perf.c new file mode 100644 index 000000000..382ae9cdb --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/perf.c @@ -0,0 +1,201 @@ +/* + * 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 + +u16 +nvbios_perf_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, + u8 *cnt, u8 *len, u8 *snr, u8 *ssz) +{ + struct bit_entry bit_P; + u16 perf = 0x0000; + + if (!bit_entry(bios, 'P', &bit_P)) { + if (bit_P.version <= 2) { + perf = nv_ro16(bios, bit_P.offset + 0); + if (perf) { + *ver = nv_ro08(bios, perf + 0); + *hdr = nv_ro08(bios, perf + 1); + if (*ver >= 0x40 && *ver < 0x41) { + *cnt = nv_ro08(bios, perf + 5); + *len = nv_ro08(bios, perf + 2); + *snr = nv_ro08(bios, perf + 4); + *ssz = nv_ro08(bios, perf + 3); + return perf; + } else + if (*ver >= 0x20 && *ver < 0x40) { + *cnt = nv_ro08(bios, perf + 2); + *len = nv_ro08(bios, perf + 3); + *snr = nv_ro08(bios, perf + 4); + *ssz = nv_ro08(bios, perf + 5); + return perf; + } + } + } + } + + if (bios->bmp_offset) { + if (nv_ro08(bios, bios->bmp_offset + 6) >= 0x25) { + perf = nv_ro16(bios, bios->bmp_offset + 0x94); + if (perf) { + *hdr = nv_ro08(bios, perf + 0); + *ver = nv_ro08(bios, perf + 1); + *cnt = nv_ro08(bios, perf + 2); + *len = nv_ro08(bios, perf + 3); + *snr = 0; + *ssz = 0; + return perf; + } + } + } + + return 0x0000; +} + +u16 +nvbios_perf_entry(struct nvkm_bios *bios, int idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + u8 snr, ssz; + u16 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 0x0000; +} + +u16 +nvbios_perfEp(struct nvkm_bios *bios, int idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_perfE *info) +{ + u16 perf = nvbios_perf_entry(bios, idx, ver, hdr, cnt, len); + memset(info, 0x00, sizeof(*info)); + info->pstate = nv_ro08(bios, perf + 0x00); + switch (!!perf * *ver) { + case 0x12: + case 0x13: + case 0x14: + info->core = nv_ro32(bios, perf + 0x01) * 10; + info->memory = nv_ro32(bios, perf + 0x05) * 20; + info->fanspeed = nv_ro08(bios, perf + 0x37); + if (*hdr > 0x38) + info->voltage = nv_ro08(bios, perf + 0x38); + break; + case 0x21: + case 0x23: + case 0x24: + info->fanspeed = nv_ro08(bios, perf + 0x04); + info->voltage = nv_ro08(bios, perf + 0x05); + info->shader = nv_ro16(bios, perf + 0x06) * 1000; + info->core = info->shader + (signed char) + nv_ro08(bios, perf + 0x08) * 1000; + switch (nv_device(bios)->chipset) { + case 0x49: + case 0x4b: + info->memory = nv_ro16(bios, perf + 0x0b) * 1000; + break; + default: + info->memory = nv_ro16(bios, perf + 0x0b) * 2000; + break; + } + break; + case 0x25: + info->fanspeed = nv_ro08(bios, perf + 0x04); + info->voltage = nv_ro08(bios, perf + 0x05); + info->core = nv_ro16(bios, perf + 0x06) * 1000; + info->shader = nv_ro16(bios, perf + 0x0a) * 1000; + info->memory = nv_ro16(bios, perf + 0x0c) * 1000; + break; + case 0x30: + info->script = nv_ro16(bios, perf + 0x02); + case 0x35: + info->fanspeed = nv_ro08(bios, perf + 0x06); + info->voltage = nv_ro08(bios, perf + 0x07); + info->core = nv_ro16(bios, perf + 0x08) * 1000; + info->shader = nv_ro16(bios, perf + 0x0a) * 1000; + info->memory = nv_ro16(bios, perf + 0x0c) * 1000; + info->vdec = nv_ro16(bios, perf + 0x10) * 1000; + info->disp = nv_ro16(bios, perf + 0x14) * 1000; + break; + case 0x40: + info->voltage = nv_ro08(bios, perf + 0x02); + break; + default: + return 0x0000; + } + 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 = (nv_ro16(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; + u16 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 = nv_ro16(bios, perf + 6); + else + fan->pwm_divisor = 0; + + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/pll.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/pll.c new file mode 100644 index 000000000..ebd402e19 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/pll.c @@ -0,0 +1,417 @@ +/* + * 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 + +#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 u16 +pll_limits_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + struct bit_entry bit_C; + + if (!bit_entry(bios, 'C', &bit_C) && bit_C.length >= 10) { + u16 data = nv_ro16(bios, bit_C.offset + 8); + if (data) { + *ver = nv_ro08(bios, data + 0); + *hdr = nv_ro08(bios, data + 1); + *len = nv_ro08(bios, data + 2); + *cnt = nv_ro08(bios, data + 3); + return data; + } + } + + if (bmp_version(bios) >= 0x0524) { + u16 data = nv_ro16(bios, bios->bmp_offset + 142); + if (data) { + *ver = nv_ro08(bios, data + 0); + *hdr = 1; + *cnt = 1; + *len = 0x18; + return data; + } + } + + *ver = 0x00; + return 0x0000; +} + +static struct pll_mapping * +pll_map(struct nvkm_bios *bios) +{ + switch (nv_device(bios)->card_type) { + case NV_04: + case NV_10: + case NV_11: + case NV_20: + case NV_30: + return nv04_pll_mapping; + break; + case NV_40: + return nv40_pll_mapping; + case NV_50: + if (nv_device(bios)->chipset == 0x50) + return nv50_pll_mapping; + else + if (nv_device(bios)->chipset < 0xa3 || + nv_device(bios)->chipset == 0xaa || + nv_device(bios)->chipset == 0xac) + return g84_pll_mapping; + default: + return NULL; + } +} + +static u16 +pll_map_reg(struct nvkm_bios *bios, u32 reg, u32 *type, u8 *ver, u8 *len) +{ + struct pll_mapping *map; + u8 hdr, cnt; + u16 data; + + data = pll_limits_table(bios, ver, &hdr, &cnt, len); + if (data && *ver >= 0x30) { + data += hdr; + while (cnt--) { + if (nv_ro32(bios, data + 3) == reg) { + *type = nv_ro08(bios, data + 0); + return data; + } + data += *len; + } + return 0x0000; + } + + map = pll_map(bios); + while (map->reg) { + if (map->reg == reg && *ver >= 0x20) { + u16 addr = (data += hdr); + *type = map->type; + while (cnt--) { + if (nv_ro32(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 u16 +pll_map_type(struct nvkm_bios *bios, u8 type, u32 *reg, u8 *ver, u8 *len) +{ + struct pll_mapping *map; + u8 hdr, cnt; + u16 data; + + data = pll_limits_table(bios, ver, &hdr, &cnt, len); + if (data && *ver >= 0x30) { + data += hdr; + while (cnt--) { + if (nv_ro08(bios, data + 0) == type) { + *reg = nv_ro32(bios, data + 3); + return data; + } + data += *len; + } + return 0x0000; + } + + map = pll_map(bios); + while (map->reg) { + if (map->type == type && *ver >= 0x20) { + u16 addr = (data += hdr); + *reg = map->reg; + while (cnt--) { + if (nv_ro32(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) +{ + u8 ver, len; + u32 reg = type; + u16 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 = nv_ro32(bios, data + 0); + info->vco1.max_freq = nv_ro32(bios, data + 4); + info->vco2.min_freq = nv_ro32(bios, data + 8); + info->vco2.max_freq = nv_ro32(bios, data + 12); + info->vco1.min_inputfreq = nv_ro32(bios, data + 16); + info->vco2.min_inputfreq = nv_ro32(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 = nv_ro16(bios, data + 4) * 1000; + info->vco1.max_freq = nv_ro16(bios, data + 6) * 1000; + info->vco2.min_freq = nv_ro16(bios, data + 8) * 1000; + info->vco2.max_freq = nv_ro16(bios, data + 10) * 1000; + info->vco1.min_inputfreq = nv_ro16(bios, data + 12) * 1000; + info->vco2.min_inputfreq = nv_ro16(bios, data + 14) * 1000; + info->vco1.max_inputfreq = nv_ro16(bios, data + 16) * 1000; + info->vco2.max_inputfreq = nv_ro16(bios, data + 18) * 1000; + info->vco1.min_n = nv_ro08(bios, data + 20); + info->vco1.max_n = nv_ro08(bios, data + 21); + info->vco1.min_m = nv_ro08(bios, data + 22); + info->vco1.max_m = nv_ro08(bios, data + 23); + info->vco2.min_n = nv_ro08(bios, data + 24); + info->vco2.max_n = nv_ro08(bios, data + 25); + info->vco2.min_m = nv_ro08(bios, data + 26); + info->vco2.max_m = nv_ro08(bios, data + 27); + + info->max_p = nv_ro08(bios, data + 29); + info->max_p_usable = info->max_p; + if (bios->version.chip < 0x60) + info->max_p_usable = 0x6; + info->bias_p = nv_ro08(bios, data + 30); + + if (len > 0x22) + info->refclk = nv_ro32(bios, data + 31); + break; + case 0x30: + data = nv_ro16(bios, data + 1); + + info->vco1.min_freq = nv_ro16(bios, data + 0) * 1000; + info->vco1.max_freq = nv_ro16(bios, data + 2) * 1000; + info->vco2.min_freq = nv_ro16(bios, data + 4) * 1000; + info->vco2.max_freq = nv_ro16(bios, data + 6) * 1000; + info->vco1.min_inputfreq = nv_ro16(bios, data + 8) * 1000; + info->vco2.min_inputfreq = nv_ro16(bios, data + 10) * 1000; + info->vco1.max_inputfreq = nv_ro16(bios, data + 12) * 1000; + info->vco2.max_inputfreq = nv_ro16(bios, data + 14) * 1000; + info->vco1.min_n = nv_ro08(bios, data + 16); + info->vco1.max_n = nv_ro08(bios, data + 17); + info->vco1.min_m = nv_ro08(bios, data + 18); + info->vco1.max_m = nv_ro08(bios, data + 19); + info->vco2.min_n = nv_ro08(bios, data + 20); + info->vco2.max_n = nv_ro08(bios, data + 21); + info->vco2.min_m = nv_ro08(bios, data + 22); + info->vco2.max_m = nv_ro08(bios, data + 23); + info->max_p_usable = info->max_p = nv_ro08(bios, data + 25); + info->bias_p = nv_ro08(bios, data + 27); + info->refclk = nv_ro32(bios, data + 28); + break; + case 0x40: + info->refclk = nv_ro16(bios, data + 9) * 1000; + data = nv_ro16(bios, data + 1); + + info->vco1.min_freq = nv_ro16(bios, data + 0) * 1000; + info->vco1.max_freq = nv_ro16(bios, data + 2) * 1000; + info->vco1.min_inputfreq = nv_ro16(bios, data + 4) * 1000; + info->vco1.max_inputfreq = nv_ro16(bios, data + 6) * 1000; + info->vco1.min_m = nv_ro08(bios, data + 8); + info->vco1.max_m = nv_ro08(bios, data + 9); + info->vco1.min_n = nv_ro08(bios, data + 10); + info->vco1.max_n = nv_ro08(bios, data + 11); + info->min_p = nv_ro08(bios, data + 12); + info->max_p = nv_ro08(bios, data + 13); + break; + default: + nv_error(bios, "unknown pll limits version 0x%02x\n", ver); + return -EINVAL; + } + + if (!info->refclk) { + info->refclk = nv_device(bios)->crystal; + if (bios->version.chip == 0x51) { + u32 sel_clk = nv_rd32(bios, 0x680524); + if ((info->reg == 0x680508 && sel_clk & 0x20) || + (info->reg == 0x680520 && sel_clk & 0x80)) { + if (nv_rdvgac(bios, 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 = nv_ro32(bios, bios->bmp_offset + 67); + info->vco1.min_freq = nv_ro32(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 (nv_device(bios)->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/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/pmu.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/pmu.c new file mode 100644 index 000000000..20c5ce0cd --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/pmu.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 +#include +#include +#include + +static u32 +weirdo_pointer(struct nvkm_bios *bios, u32 data) +{ + struct nvbios_image image; + int idx = 0; + if (nvbios_image(bios, idx++, &image)) { + data -= image.size; + while (nvbios_image(bios, idx++, &image)) { + if (image.type == 0xe0) + return image.base + data; + } + } + return 0; +} + +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 = nv_ro32(bios, bit_p.offset + 0x00); + if ((data = weirdo_pointer(bios, data))) { + *ver = nv_ro08(bios, data + 0x00); /* maybe? */ + *hdr = nv_ro08(bios, data + 0x01); + *len = nv_ro08(bios, data + 0x02); + *cnt = nv_ro08(bios, data + 0x03); + } + } + + return data; +} + +u32 +nvbios_pmuTp(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_pmuT *info) +{ + u32 data = nvbios_pmuTe(bios, ver, hdr, cnt, len); + memset(info, 0x00, sizeof(*info)); + switch (!!data * *ver) { + default: + break; + } + 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); + memset(info, 0x00, sizeof(*info)); + switch (!!data * *ver) { + default: + info->type = nv_ro08(bios, data + 0x00); + info->data = nv_ro32(bios, data + 0x02); + break; + } + 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 = weirdo_pointer(bios, pmuE.data))) { + info->init_addr_pmu = nv_ro32(bios, data + 0x08); + info->args_addr_pmu = nv_ro32(bios, data + 0x0c); + info->boot_addr = data + 0x30; + info->boot_addr_pmu = nv_ro32(bios, data + 0x10) + + nv_ro32(bios, data + 0x18); + info->boot_size = nv_ro32(bios, data + 0x1c) - + nv_ro32(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 = nv_ro32(bios, data + 0x20); + info->data_addr = data + 0x30 + + nv_ro32(bios, data + 0x24); + info->data_addr_pmu = nv_ro32(bios, data + 0x28); + info->data_size = nv_ro32(bios, data + 0x2c); + return true; + } + } + return false; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h new file mode 100644 index 000000000..95e4fa153 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h @@ -0,0 +1,23 @@ +#ifndef __NVKM_BIOS_PRIV_H__ +#define __NVKM_BIOS_PRIV_H__ +#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 *); + bool rw; +}; + +int nvbios_extend(struct nvkm_bios *, u32 length); +int nvbios_shadow(struct nvkm_bios *); + +extern const struct nvbios_source nvbios_rom; +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/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/ramcfg.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/ramcfg.c new file mode 100644 index 000000000..a17b22111 --- /dev/null +++ b/kernel/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 (nv_rd32(subdev, 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 nv_ro08(bios, bit_M.offset + 2); + if (bit_M.version == 2 && bit_M.length >= 3) + return nv_ro08(bios, bit_M.offset + 0); + } + + return 0x00; +} + +u8 +nvbios_ramcfg_index(struct nvkm_subdev *subdev) +{ + struct nvkm_bios *bios = nvkm_bios(subdev); + 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 = nv_ro16(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 = nv_ro16(bios, bit_M.offset + 1); + } + } + + if (xlat) + strap = nv_ro08(bios, xlat + strap); + return strap; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/rammap.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/rammap.c new file mode 100644 index 000000000..8b17bb4b2 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/rammap.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 +#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; + u16 rammap = 0x0000; + + if (!bit_entry(bios, 'P', &bit_P)) { + if (bit_P.version == 2) + rammap = nv_ro16(bios, bit_P.offset + 4); + + if (rammap) { + *ver = nv_ro08(bios, rammap + 0); + switch (*ver) { + case 0x10: + case 0x11: + *hdr = nv_ro08(bios, rammap + 1); + *cnt = nv_ro08(bios, rammap + 5); + *len = nv_ro08(bios, rammap + 2); + *snr = nv_ro08(bios, rammap + 4); + *ssz = nv_ro08(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; + u16 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; +} + +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 = nv_ro16(bios, data + 0x00); + p->rammap_max = nv_ro16(bios, data + 0x02); + p->rammap_10_04_02 = (nv_ro08(bios, data + 0x04) & 0x02) >> 1; + p->rammap_10_04_08 = (nv_ro08(bios, data + 0x04) & 0x08) >> 3; + break; + case 0x11: + p->rammap_min = nv_ro16(bios, data + 0x00); + p->rammap_max = nv_ro16(bios, data + 0x02); + p->rammap_11_08_01 = (nv_ro08(bios, data + 0x08) & 0x01) >> 0; + p->rammap_11_08_0c = (nv_ro08(bios, data + 0x08) & 0x0c) >> 2; + p->rammap_11_08_10 = (nv_ro08(bios, data + 0x08) & 0x10) >> 4; + temp = nv_ro32(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 = nv_ro08(bios, data + 0x0d); + p->rammap_11_0e = nv_ro08(bios, data + 0x0e); + p->rammap_11_0f = nv_ro08(bios, data + 0x0f); + p->rammap_11_11_0c = (nv_ro08(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(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 = nv_ro08(bios, data + 0x01); + p->ramcfg_10_02_01 = (nv_ro08(bios, data + 0x02) & 0x01) >> 0; + p->ramcfg_10_02_02 = (nv_ro08(bios, data + 0x02) & 0x02) >> 1; + p->ramcfg_10_02_04 = (nv_ro08(bios, data + 0x02) & 0x04) >> 2; + p->ramcfg_10_02_08 = (nv_ro08(bios, data + 0x02) & 0x08) >> 3; + p->ramcfg_10_02_10 = (nv_ro08(bios, data + 0x02) & 0x10) >> 4; + p->ramcfg_10_02_20 = (nv_ro08(bios, data + 0x02) & 0x20) >> 5; + p->ramcfg_10_DLLoff = (nv_ro08(bios, data + 0x02) & 0x40) >> 6; + p->ramcfg_10_03_0f = (nv_ro08(bios, data + 0x03) & 0x0f) >> 0; + p->ramcfg_10_04_01 = (nv_ro08(bios, data + 0x04) & 0x01) >> 0; + p->ramcfg_10_05 = (nv_ro08(bios, data + 0x05) & 0xff) >> 0; + p->ramcfg_10_06 = (nv_ro08(bios, data + 0x06) & 0xff) >> 0; + p->ramcfg_10_07 = (nv_ro08(bios, data + 0x07) & 0xff) >> 0; + p->ramcfg_10_08 = (nv_ro08(bios, data + 0x08) & 0xff) >> 0; + p->ramcfg_10_09_0f = (nv_ro08(bios, data + 0x09) & 0x0f) >> 0; + p->ramcfg_10_09_f0 = (nv_ro08(bios, data + 0x09) & 0xf0) >> 4; + break; + case 0x11: + p->ramcfg_timing = nv_ro08(bios, data + 0x00); + p->ramcfg_11_01_01 = (nv_ro08(bios, data + 0x01) & 0x01) >> 0; + p->ramcfg_11_01_02 = (nv_ro08(bios, data + 0x01) & 0x02) >> 1; + p->ramcfg_11_01_04 = (nv_ro08(bios, data + 0x01) & 0x04) >> 2; + p->ramcfg_11_01_08 = (nv_ro08(bios, data + 0x01) & 0x08) >> 3; + p->ramcfg_11_01_10 = (nv_ro08(bios, data + 0x01) & 0x10) >> 4; + p->ramcfg_11_01_20 = (nv_ro08(bios, data + 0x01) & 0x20) >> 5; + p->ramcfg_11_01_40 = (nv_ro08(bios, data + 0x01) & 0x40) >> 6; + p->ramcfg_11_01_80 = (nv_ro08(bios, data + 0x01) & 0x80) >> 7; + p->ramcfg_11_02_03 = (nv_ro08(bios, data + 0x02) & 0x03) >> 0; + p->ramcfg_11_02_04 = (nv_ro08(bios, data + 0x02) & 0x04) >> 2; + p->ramcfg_11_02_08 = (nv_ro08(bios, data + 0x02) & 0x08) >> 3; + p->ramcfg_11_02_10 = (nv_ro08(bios, data + 0x02) & 0x10) >> 4; + p->ramcfg_11_02_40 = (nv_ro08(bios, data + 0x02) & 0x40) >> 6; + p->ramcfg_11_02_80 = (nv_ro08(bios, data + 0x02) & 0x80) >> 7; + p->ramcfg_11_03_0f = (nv_ro08(bios, data + 0x03) & 0x0f) >> 0; + p->ramcfg_11_03_30 = (nv_ro08(bios, data + 0x03) & 0x30) >> 4; + p->ramcfg_11_03_c0 = (nv_ro08(bios, data + 0x03) & 0xc0) >> 6; + p->ramcfg_11_03_f0 = (nv_ro08(bios, data + 0x03) & 0xf0) >> 4; + p->ramcfg_11_04 = (nv_ro08(bios, data + 0x04) & 0xff) >> 0; + p->ramcfg_11_06 = (nv_ro08(bios, data + 0x06) & 0xff) >> 0; + p->ramcfg_11_07_02 = (nv_ro08(bios, data + 0x07) & 0x02) >> 1; + p->ramcfg_11_07_04 = (nv_ro08(bios, data + 0x07) & 0x04) >> 2; + p->ramcfg_11_07_08 = (nv_ro08(bios, data + 0x07) & 0x08) >> 3; + p->ramcfg_11_07_10 = (nv_ro08(bios, data + 0x07) & 0x10) >> 4; + p->ramcfg_11_07_40 = (nv_ro08(bios, data + 0x07) & 0x40) >> 6; + p->ramcfg_11_07_80 = (nv_ro08(bios, data + 0x07) & 0x80) >> 7; + p->ramcfg_11_08_01 = (nv_ro08(bios, data + 0x08) & 0x01) >> 0; + p->ramcfg_11_08_02 = (nv_ro08(bios, data + 0x08) & 0x02) >> 1; + p->ramcfg_11_08_04 = (nv_ro08(bios, data + 0x08) & 0x04) >> 2; + p->ramcfg_11_08_08 = (nv_ro08(bios, data + 0x08) & 0x08) >> 3; + p->ramcfg_11_08_10 = (nv_ro08(bios, data + 0x08) & 0x10) >> 4; + p->ramcfg_11_08_20 = (nv_ro08(bios, data + 0x08) & 0x20) >> 5; + p->ramcfg_11_09 = (nv_ro08(bios, data + 0x09) & 0xff) >> 0; + break; + default: + data = 0; + break; + } + return data; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c new file mode 100644 index 000000000..8c2b7cba5 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c @@ -0,0 +1,272 @@ +/* + * 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 +#include + +struct shadow { + struct nvkm_oclass base; + u32 skip; + const struct nvbios_source *func; + void *data; + u32 size; + int score; +}; + +static bool +shadow_fetch(struct nvkm_bios *bios, u32 upto) +{ + struct shadow *mthd = (void *)nv_object(bios)->oclass; + 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 >= limit; +} + +static u8 +shadow_rd08(struct nvkm_object *object, u64 addr) +{ + struct nvkm_bios *bios = (void *)object; + if (shadow_fetch(bios, addr + 1)) + return bios->data[addr]; + return 0x00; +} + +static u16 +shadow_rd16(struct nvkm_object *object, u64 addr) +{ + struct nvkm_bios *bios = (void *)object; + if (shadow_fetch(bios, addr + 2)) + return get_unaligned_le16(&bios->data[addr]); + return 0x0000; +} + +static u32 +shadow_rd32(struct nvkm_object *object, u64 addr) +{ + struct nvkm_bios *bios = (void *)object; + if (shadow_fetch(bios, addr + 4)) + return get_unaligned_le32(&bios->data[addr]); + return 0x00000000; +} + +static struct nvkm_oclass +shadow_class = { + .handle = NV_SUBDEV(VBIOS, 0x00), + .ofuncs = &(struct nvkm_ofuncs) { + .rd08 = shadow_rd08, + .rd16 = shadow_rd16, + .rd32 = shadow_rd32, + }, +}; + +static int +shadow_image(struct nvkm_bios *bios, int idx, struct shadow *mthd) +{ + struct nvbios_image image; + int score = 1; + + if (!nvbios_image(bios, idx, &image)) { + nv_debug(bios, "image %d invalid\n", idx); + return 0; + } + nv_debug(bios, "%08x: type %02x, %d bytes\n", + image.base, image.type, image.size); + + if (!shadow_fetch(bios, image.size)) { + nv_debug(bios, "%08x: fetch failed\n", image.base); + return 0; + } + + switch (image.type) { + case 0x00: + if (nvbios_checksum(&bios->data[image.base], image.size)) { + nv_debug(bios, "%08x: checksum failed\n", image.base); + if (mthd->func->rw) + score += 1; + score += 1; + } else { + score += 3; + } + break; + default: + score += 3; + break; + } + + if (!image.last) + score += shadow_image(bios, idx + 1, mthd); + return score; +} + +static int +shadow_score(struct nvkm_bios *bios, struct shadow *mthd) +{ + struct nvkm_oclass *oclass = nv_object(bios)->oclass; + int score; + nv_object(bios)->oclass = &mthd->base; + score = shadow_image(bios, 0, mthd); + nv_object(bios)->oclass = oclass; + return score; + +} + +static int +shadow_method(struct nvkm_bios *bios, struct shadow *mthd, const char *name) +{ + const struct nvbios_source *func = mthd->func; + if (func->name) { + nv_debug(bios, "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_score(bios, mthd); + if (func->fini) + func->fini(mthd->data); + nv_debug(bios, "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 = &nv_device(bios)->pdev->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 shadow mthds[] = { + { shadow_class, 0, &nvbios_of }, + { shadow_class, 0, &nvbios_ramin }, + { shadow_class, 0, &nvbios_rom }, + { shadow_class, 0, &nvbios_acpi_fast }, + { shadow_class, 4, &nvbios_acpi_slow }, + { shadow_class, 1, &nvbios_pcirom }, + { shadow_class, 1, &nvbios_platform }, + { shadow_class } + }, *mthd = mthds, *best = NULL; + const char *optarg; + char *source; + int optlen; + + /* handle user-specified bios source */ + optarg = nvkm_stropt(nv_device(bios)->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) { + nv_error(bios, "%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) { + nv_fatal(bios, "unable to locate usable image\n"); + return -EINVAL; + } + + nv_info(bios, "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/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowacpi.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowacpi.c new file mode 100644 index 000000000..f9d0eb564 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowacpi.c @@ -0,0 +1,112 @@ +/* + * 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(CONFIG_ACPI) && defined(CONFIG_X86) +int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len); +bool nouveau_acpi_rom_supported(struct pci_dev *pdev); +#else +static inline bool +nouveau_acpi_rom_supported(struct pci_dev *pdev) +{ + return false; +} + +static inline int +nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len) +{ + 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 = nouveau_acpi_get_bios_chunk(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 = nouveau_acpi_get_bios_chunk(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 (!nouveau_acpi_rom_supported(nv_device(bios)->pdev)) + return ERR_PTR(-ENODEV); + return NULL; +} + +const struct nvbios_source +nvbios_acpi_fast = { + .name = "ACPI", + .init = acpi_init, + .read = acpi_read_fast, + .rw = false, +}; + +const struct nvbios_source +nvbios_acpi_slow = { + .name = "ACPI", + .init = acpi_init, + .read = acpi_read_slow, + .rw = false, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowof.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowof.c new file mode 100644 index 000000000..4c19a7dba --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowof.c @@ -0,0 +1,72 @@ +/* + * 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 + length <= priv->size) { + memcpy_fromio(bios->data + offset, priv->data + offset, length); + return length; + } + return 0; +} + +static void * +of_init(struct nvkm_bios *bios, const char *name) +{ + struct pci_dev *pdev = nv_device(bios)->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, + .rw = false, +}; +#else +const struct nvbios_source +nvbios_of = { +}; +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowpci.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowpci.c new file mode 100644 index 000000000..1b045483d --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowpci.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. + * + */ +#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 pci_dev *pdev = nv_device(bios)->pdev; + struct priv *priv = NULL; + int ret; + + 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 pci_dev *pdev = nv_device(bios)->pdev; + struct priv *priv; + int ret = -ENOMEM; + + if ((priv = kmalloc(sizeof(*priv), GFP_KERNEL))) { + if (ret = -ENODEV, + (priv->rom = pci_platform_rom(pdev, &priv->size))) + return priv; + kfree(priv); + } + + return ERR_PTR(ret); +} + +const struct nvbios_source +nvbios_platform = { + .name = "PLATFORM", + .init = platform_init, + .fini = (void(*)(void *))kfree, + .read = pcirom_read, + .rw = true, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c new file mode 100644 index 000000000..abe8ae4d3 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c @@ -0,0 +1,115 @@ +/* + * 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 nvkm_bios *bios; + u32 bar0; +}; + +static u32 +pramin_read(void *data, u32 offset, u32 length, struct nvkm_bios *bios) +{ + u32 i; + if (offset + length <= 0x00100000) { + for (i = offset; i < offset + length; i += 4) + *(u32 *)&bios->data[i] = nv_rd32(bios, 0x700000 + i); + return length; + } + return 0; +} + +static void +pramin_fini(void *data) +{ + struct priv *priv = data; + if (priv) { + nv_wr32(priv->bios, 0x001700, priv->bar0); + kfree(priv); + } +} + +static void * +pramin_init(struct nvkm_bios *bios, const char *name) +{ + struct priv *priv = NULL; + u64 addr = 0; + + /* PRAMIN always potentially available prior to nv50 */ + if (nv_device(bios)->card_type < NV_50) + return NULL; + + /* we can't get the bios image pointer without PDISP */ + if (nv_device(bios)->card_type >= GM100) + addr = nv_rd32(bios, 0x021c04); + else + if (nv_device(bios)->card_type >= NV_C0) + addr = nv_rd32(bios, 0x022500); + if (addr & 0x00000001) { + nv_debug(bios, "... 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 + */ + addr = nv_rd32(bios, 0x619f04); + if (!(addr & 0x00000008)) { + nv_debug(bios, "... not enabled\n"); + return ERR_PTR(-ENODEV); + } + if ( (addr & 0x00000003) != 1) { + nv_debug(bios, "... not in vram\n"); + return ERR_PTR(-ENODEV); + } + + /* some alternate method inherited from xf86-video-nv... */ + addr = (addr & 0xffffff00) << 8; + if (!addr) { + addr = (u64)nv_rd32(bios, 0x001700) << 16; + addr += 0xf0000; + } + + /* modify bar0 PRAMIN window to cover the bios image */ + if (!(priv = kmalloc(sizeof(*priv), GFP_KERNEL))) { + nv_error(bios, "... out of memory\n"); + return ERR_PTR(-ENOMEM); + } + + priv->bios = bios; + priv->bar0 = nv_rd32(bios, 0x001700); + nv_wr32(bios, 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/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowrom.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowrom.c new file mode 100644 index 000000000..6ec3b2379 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowrom.c @@ -0,0 +1,70 @@ +/* + * 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 +prom_read(void *data, u32 offset, u32 length, struct nvkm_bios *bios) +{ + u32 i; + if (offset + length <= 0x00100000) { + for (i = offset; i < offset + length; i += 4) + *(u32 *)&bios->data[i] = nv_rd32(bios, 0x300000 + i); + return length; + } + return 0; +} + +static void +prom_fini(void *data) +{ + struct nvkm_bios *bios = data; + if (nv_device(bios)->card_type < NV_50) + nv_mask(bios, 0x001850, 0x00000001, 0x00000001); + else + nv_mask(bios, 0x088050, 0x00000001, 0x00000001); +} + +static void * +prom_init(struct nvkm_bios *bios, const char *name) +{ + if (nv_device(bios)->card_type < NV_50) { + if (nv_device(bios)->card_type == NV_40 && + nv_device(bios)->chipset >= 0x4c) + return ERR_PTR(-ENODEV); + nv_mask(bios, 0x001850, 0x00000001, 0x00000000); + } else { + nv_mask(bios, 0x088050, 0x00000001, 0x00000000); + } + return bios; +} + +const struct nvbios_source +nvbios_rom = { + .name = "PROM", + .init = prom_init, + .fini = prom_fini, + .read = prom_read, + .rw = false, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/therm.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/therm.c new file mode 100644 index 000000000..249ff6d58 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/therm.c @@ -0,0 +1,214 @@ +/* + * 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 + +static u16 +therm_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *len, u8 *cnt) +{ + struct bit_entry bit_P; + u16 therm = 0; + + if (!bit_entry(bios, 'P', &bit_P)) { + if (bit_P.version == 1) + therm = nv_ro16(bios, bit_P.offset + 12); + else if (bit_P.version == 2) + therm = nv_ro16(bios, bit_P.offset + 16); + else + nv_error(bios, + "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 0x0000; + + *ver = nv_ro08(bios, therm + 0); + *hdr = nv_ro08(bios, therm + 1); + *len = nv_ro08(bios, therm + 2); + *cnt = nv_ro08(bios, therm + 3); + return therm + nv_ro08(bios, therm + 1); +} + +static u16 +nvbios_therm_entry(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len) +{ + u8 hdr, cnt; + u16 therm = therm_table(bios, ver, &hdr, len, &cnt); + if (therm && idx < cnt) + return therm + idx * *len; + return 0x0000; +} + +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; + u16 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 = nv_ro16(bios, entry + 1); + + switch (nv_ro08(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) nv_ro08(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; + u16 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 = nv_ro16(bios, entry + 1); + + switch (nv_ro08(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 = nv_ro08(bios, entry + 1); + fan->linear_max_temp = nv_ro08(bios, entry + 2); + break; + } + } + + /* starting from fermi, fan management is always linear */ + if (nv_device(bios)->card_type >= NV_C0 && + fan->fan_mode == NVBIOS_THERM_FAN_OTHER) { + fan->fan_mode = NVBIOS_THERM_FAN_LINEAR; + } + + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/timing.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/timing.c new file mode 100644 index 000000000..763fd29a5 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/timing.c @@ -0,0 +1,166 @@ +/* + * 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 + +u16 +nvbios_timingTe(struct nvkm_bios *bios, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz) +{ + struct bit_entry bit_P; + u16 timing = 0x0000; + + if (!bit_entry(bios, 'P', &bit_P)) { + if (bit_P.version == 1) + timing = nv_ro16(bios, bit_P.offset + 4); + else + if (bit_P.version == 2) + timing = nv_ro16(bios, bit_P.offset + 8); + + if (timing) { + *ver = nv_ro08(bios, timing + 0); + switch (*ver) { + case 0x10: + *hdr = nv_ro08(bios, timing + 1); + *cnt = nv_ro08(bios, timing + 2); + *len = nv_ro08(bios, timing + 3); + *snr = 0; + *ssz = 0; + return timing; + case 0x20: + *hdr = nv_ro08(bios, timing + 1); + *cnt = nv_ro08(bios, timing + 5); + *len = nv_ro08(bios, timing + 2); + *snr = nv_ro08(bios, timing + 4); + *ssz = nv_ro08(bios, timing + 3); + return timing; + default: + break; + } + } + } + + return 0x0000; +} + +u16 +nvbios_timingEe(struct nvkm_bios *bios, int idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + u8 snr, ssz; + u16 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 0x0000; +} + +u16 +nvbios_timingEp(struct nvkm_bios *bios, int idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_ramcfg *p) +{ + u16 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 = nv_ro08(bios, data + 0x00); + p->timing_10_WTR = nv_ro08(bios, data + 0x01); + p->timing_10_CL = nv_ro08(bios, data + 0x02); + p->timing_10_RC = nv_ro08(bios, data + 0x03); + p->timing_10_RFC = nv_ro08(bios, data + 0x05); + p->timing_10_RAS = nv_ro08(bios, data + 0x07); + p->timing_10_RP = nv_ro08(bios, data + 0x09); + p->timing_10_RCDRD = nv_ro08(bios, data + 0x0a); + p->timing_10_RCDWR = nv_ro08(bios, data + 0x0b); + p->timing_10_RRD = nv_ro08(bios, data + 0x0c); + p->timing_10_13 = nv_ro08(bios, data + 0x0d); + p->timing_10_ODT = nv_ro08(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 = nv_ro08(bios, data + 0x18); + case 24: + case 23: + case 22: + p->timing_10_21 = nv_ro08(bios, data + 0x15); + case 21: + p->timing_10_20 = nv_ro08(bios, data + 0x14); + case 20: + p->timing_10_CWL = nv_ro08(bios, data + 0x13); + case 19: + p->timing_10_18 = nv_ro08(bios, data + 0x12); + case 18: + case 17: + p->timing_10_16 = nv_ro08(bios, data + 0x10); + } + + break; + case 0x20: + p->timing[0] = nv_ro32(bios, data + 0x00); + p->timing[1] = nv_ro32(bios, data + 0x04); + p->timing[2] = nv_ro32(bios, data + 0x08); + p->timing[3] = nv_ro32(bios, data + 0x0c); + p->timing[4] = nv_ro32(bios, data + 0x10); + p->timing[5] = nv_ro32(bios, data + 0x14); + p->timing[6] = nv_ro32(bios, data + 0x18); + p->timing[7] = nv_ro32(bios, data + 0x1c); + p->timing[8] = nv_ro32(bios, data + 0x20); + p->timing[9] = nv_ro32(bios, data + 0x24); + p->timing[10] = nv_ro32(bios, data + 0x28); + p->timing_20_2e_03 = (nv_ro08(bios, data + 0x2e) & 0x03) >> 0; + p->timing_20_2e_30 = (nv_ro08(bios, data + 0x2e) & 0x30) >> 4; + p->timing_20_2e_c0 = (nv_ro08(bios, data + 0x2e) & 0xc0) >> 6; + p->timing_20_2f_03 = (nv_ro08(bios, data + 0x2f) & 0x03) >> 0; + temp = nv_ro16(bios, data + 0x2c); + p->timing_20_2c_003f = (temp & 0x003f) >> 0; + p->timing_20_2c_1fc0 = (temp & 0x1fc0) >> 6; + p->timing_20_30_07 = (nv_ro08(bios, data + 0x30) & 0x07) >> 0; + p->timing_20_30_f8 = (nv_ro08(bios, data + 0x30) & 0xf8) >> 3; + temp = nv_ro16(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/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/vmap.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/vmap.c new file mode 100644 index 000000000..e95b69faa --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/vmap.c @@ -0,0 +1,111 @@ +/* + * 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 + +u16 +nvbios_vmap_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + struct bit_entry bit_P; + u16 vmap = 0x0000; + + if (!bit_entry(bios, 'P', &bit_P)) { + if (bit_P.version == 2) { + vmap = nv_ro16(bios, bit_P.offset + 0x20); + if (vmap) { + *ver = nv_ro08(bios, vmap + 0); + switch (*ver) { + case 0x10: + case 0x20: + *hdr = nv_ro08(bios, vmap + 1); + *cnt = nv_ro08(bios, vmap + 3); + *len = nv_ro08(bios, vmap + 2); + return vmap; + default: + break; + } + } + } + } + + return 0x0000; +} + +u16 +nvbios_vmap_parse(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_vmap *info) +{ + u16 vmap = nvbios_vmap_table(bios, ver, hdr, cnt, len); + memset(info, 0x00, sizeof(*info)); + switch (!!vmap * *ver) { + case 0x10: + case 0x20: + break; + } + return vmap; +} + +u16 +nvbios_vmap_entry(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len) +{ + u8 hdr, cnt; + u16 vmap = nvbios_vmap_table(bios, ver, &hdr, &cnt, len); + if (vmap && idx < cnt) { + vmap = vmap + hdr + (idx * *len); + return vmap; + } + return 0x0000; +} + +u16 +nvbios_vmap_entry_parse(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len, + struct nvbios_vmap_entry *info) +{ + u16 vmap = nvbios_vmap_entry(bios, idx, ver, len); + memset(info, 0x00, sizeof(*info)); + switch (!!vmap * *ver) { + case 0x10: + info->link = 0xff; + info->min = nv_ro32(bios, vmap + 0x00); + info->max = nv_ro32(bios, vmap + 0x04); + info->arg[0] = nv_ro32(bios, vmap + 0x08); + info->arg[1] = nv_ro32(bios, vmap + 0x0c); + info->arg[2] = nv_ro32(bios, vmap + 0x10); + break; + case 0x20: + info->unk0 = nv_ro08(bios, vmap + 0x00); + info->link = nv_ro08(bios, vmap + 0x01); + info->min = nv_ro32(bios, vmap + 0x02); + info->max = nv_ro32(bios, vmap + 0x06); + info->arg[0] = nv_ro32(bios, vmap + 0x0a); + info->arg[1] = nv_ro32(bios, vmap + 0x0e); + info->arg[2] = nv_ro32(bios, vmap + 0x12); + info->arg[3] = nv_ro32(bios, vmap + 0x16); + info->arg[4] = nv_ro32(bios, vmap + 0x1a); + info->arg[5] = nv_ro32(bios, vmap + 0x1e); + break; + } + return vmap; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/volt.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/volt.c new file mode 100644 index 000000000..8454ab7c4 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/volt.c @@ -0,0 +1,136 @@ +/* + * 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 + +u16 +nvbios_volt_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + struct bit_entry bit_P; + u16 volt = 0x0000; + + if (!bit_entry(bios, 'P', &bit_P)) { + if (bit_P.version == 2) + volt = nv_ro16(bios, bit_P.offset + 0x0c); + else + if (bit_P.version == 1) + volt = nv_ro16(bios, bit_P.offset + 0x10); + + if (volt) { + *ver = nv_ro08(bios, volt + 0); + switch (*ver) { + case 0x12: + *hdr = 5; + *cnt = nv_ro08(bios, volt + 2); + *len = nv_ro08(bios, volt + 1); + return volt; + case 0x20: + *hdr = nv_ro08(bios, volt + 1); + *cnt = nv_ro08(bios, volt + 2); + *len = nv_ro08(bios, volt + 3); + return volt; + case 0x30: + case 0x40: + case 0x50: + *hdr = nv_ro08(bios, volt + 1); + *cnt = nv_ro08(bios, volt + 3); + *len = nv_ro08(bios, volt + 2); + return volt; + } + } + } + + return 0x0000; +} + +u16 +nvbios_volt_parse(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_volt *info) +{ + u16 volt = nvbios_volt_table(bios, ver, hdr, cnt, len); + memset(info, 0x00, sizeof(*info)); + switch (!!volt * *ver) { + case 0x12: + info->vidmask = nv_ro08(bios, volt + 0x04); + break; + case 0x20: + info->vidmask = nv_ro08(bios, volt + 0x05); + break; + case 0x30: + info->vidmask = nv_ro08(bios, volt + 0x04); + break; + case 0x40: + info->base = nv_ro32(bios, volt + 0x04); + info->step = nv_ro16(bios, volt + 0x08); + info->vidmask = nv_ro08(bios, volt + 0x0b); + /*XXX*/ + info->min = 0; + info->max = info->base; + break; + case 0x50: + info->vidmask = nv_ro08(bios, volt + 0x06); + info->min = nv_ro32(bios, volt + 0x0a); + info->max = nv_ro32(bios, volt + 0x0e); + info->base = nv_ro32(bios, volt + 0x12) & 0x00ffffff; + info->step = nv_ro16(bios, volt + 0x16); + break; + } + return volt; +} + +u16 +nvbios_volt_entry(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len) +{ + u8 hdr, cnt; + u16 volt = nvbios_volt_table(bios, ver, &hdr, &cnt, len); + if (volt && idx < cnt) { + volt = volt + hdr + (idx * *len); + return volt; + } + return 0x0000; +} + +u16 +nvbios_volt_entry_parse(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len, + struct nvbios_volt_entry *info) +{ + u16 volt = nvbios_volt_entry(bios, idx, ver, len); + memset(info, 0x00, sizeof(*info)); + switch (!!volt * *ver) { + case 0x12: + case 0x20: + info->voltage = nv_ro08(bios, volt + 0x00) * 10000; + info->vid = nv_ro08(bios, volt + 0x01); + break; + case 0x30: + info->voltage = nv_ro08(bios, volt + 0x00) * 10000; + info->vid = nv_ro08(bios, volt + 0x01) >> 2; + break; + case 0x40: + case 0x50: + break; + } + return volt; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/xpio.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bios/xpio.c new file mode 100644 index 000000000..63a5e1b5c --- /dev/null +++ b/kernel/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 = nv_ro16(bios, data + 0x04); + if (xpio) { + *ver = nv_ro08(bios, data + 0x00); + *hdr = nv_ro08(bios, data + 0x01); + *cnt = nv_ro08(bios, data + 0x02); + *len = nv_ro08(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 = nv_ro16(bios, data + *hdr + (idx * *len)); + if (xpio) { + *ver = nv_ro08(bios, data + 0x00); + *hdr = nv_ro08(bios, data + 0x01); + *cnt = nv_ro08(bios, data + 0x02); + *len = nv_ro08(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 = nv_ro08(bios, data + 0x04); + info->addr = nv_ro08(bios, data + 0x05); + info->flags = nv_ro08(bios, data + 0x06); + } + return 0x0000; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bus/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bus/Kbuild new file mode 100644 index 000000000..83d80b13f --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bus/Kbuild @@ -0,0 +1,6 @@ +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/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bus/g94.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bus/g94.c new file mode 100644 index 000000000..cbe699e82 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bus/g94.c @@ -0,0 +1,58 @@ +/* + * 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 "nv04.h" + +#include + +static int +g94_bus_hwsq_exec(struct nvkm_bus *pbus, u32 *data, u32 size) +{ + struct nv50_bus_priv *priv = (void *)pbus; + int i; + + nv_mask(pbus, 0x001098, 0x00000008, 0x00000000); + nv_wr32(pbus, 0x001304, 0x00000000); + nv_wr32(pbus, 0x001318, 0x00000000); + for (i = 0; i < size; i++) + nv_wr32(priv, 0x080000 + (i * 4), data[i]); + nv_mask(pbus, 0x001098, 0x00000018, 0x00000018); + nv_wr32(pbus, 0x00130c, 0x00000001); + + return nv_wait(pbus, 0x001308, 0x00000100, 0x00000000) ? 0 : -ETIMEDOUT; +} + +struct nvkm_oclass * +g94_bus_oclass = &(struct nv04_bus_impl) { + .base.handle = NV_SUBDEV(BUS, 0x94), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_bus_ctor, + .dtor = _nvkm_bus_dtor, + .init = nv50_bus_init, + .fini = _nvkm_bus_fini, + }, + .intr = nv50_bus_intr, + .hwsq_exec = g94_bus_hwsq_exec, + .hwsq_size = 128, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bus/gf100.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bus/gf100.c new file mode 100644 index 000000000..ebc63ba96 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bus/gf100.c @@ -0,0 +1,80 @@ +/* + * 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 "nv04.h" + +static void +gf100_bus_intr(struct nvkm_subdev *subdev) +{ + struct nvkm_bus *pbus = nvkm_bus(subdev); + u32 stat = nv_rd32(pbus, 0x001100) & nv_rd32(pbus, 0x001140); + + if (stat & 0x0000000e) { + u32 addr = nv_rd32(pbus, 0x009084); + u32 data = nv_rd32(pbus, 0x009088); + + nv_error(pbus, "MMIO %s of 0x%08x FAULT at 0x%06x [ %s%s%s]\n", + (addr & 0x00000002) ? "write" : "read", data, + (addr & 0x00fffffc), + (stat & 0x00000002) ? "!ENGINE " : "", + (stat & 0x00000004) ? "IBUS " : "", + (stat & 0x00000008) ? "TIMEOUT " : ""); + + nv_wr32(pbus, 0x009084, 0x00000000); + nv_wr32(pbus, 0x001100, (stat & 0x0000000e)); + stat &= ~0x0000000e; + } + + if (stat) { + nv_error(pbus, "unknown intr 0x%08x\n", stat); + nv_mask(pbus, 0x001140, stat, 0x00000000); + } +} + +static int +gf100_bus_init(struct nvkm_object *object) +{ + struct nv04_bus_priv *priv = (void *)object; + int ret; + + ret = nvkm_bus_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, 0x001100, 0xffffffff); + nv_wr32(priv, 0x001140, 0x0000000e); + return 0; +} + +struct nvkm_oclass * +gf100_bus_oclass = &(struct nv04_bus_impl) { + .base.handle = NV_SUBDEV(BUS, 0xc0), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_bus_ctor, + .dtor = _nvkm_bus_dtor, + .init = gf100_bus_init, + .fini = _nvkm_bus_fini, + }, + .intr = gf100_bus_intr, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bus/hwsq.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bus/hwsq.c new file mode 100644 index 000000000..7622b4161 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bus/hwsq.c @@ -0,0 +1,143 @@ +/* + * 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 + +struct nvkm_hwsq { + struct nvkm_bus *pbus; + 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_bus *pbus, struct nvkm_hwsq **phwsq) +{ + struct nvkm_hwsq *hwsq; + + hwsq = *phwsq = kmalloc(sizeof(*hwsq), GFP_KERNEL); + if (hwsq) { + hwsq->pbus = pbus; + 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_bus *pbus = hwsq->pbus; + hwsq->c.size = (hwsq->c.size + 4) / 4; + if (hwsq->c.size <= pbus->hwsq_size) { + if (exec) + ret = pbus->hwsq_exec(pbus, (u32 *)hwsq->c.data, + hwsq->c.size); + if (ret) + nv_error(pbus, "hwsq exec failed: %d\n", ret); + } else { + nv_error(pbus, "hwsq ucode too large\n"); + ret = -ENOSPC; + } + + for (i = 0; ret && i < hwsq->c.size; i++) + nv_error(pbus, "\t0x%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) +{ + nv_debug(hwsq->pbus, "R[%06x] = 0x%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) +{ + nv_debug(hwsq->pbus, " 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) +{ + nv_debug(hwsq->pbus, " WAIT[%02x] = %d\n", flag, data); + hwsq_cmd(hwsq, 3, (u8[]){ 0x5f, flag, data }); +} + +void +nvkm_hwsq_nsec(struct nvkm_hwsq *hwsq, u32 nsec) +{ + u8 shift = 0, usec = nsec / 1000; + while (usec & ~3) { + usec >>= 2; + shift++; + } + + nv_debug(hwsq->pbus, " DELAY = %d ns\n", nsec); + hwsq_cmd(hwsq, 1, (u8[]){ 0x00 | (shift << 2) | usec }); +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bus/hwsq.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bus/hwsq.h new file mode 100644 index 000000000..ebf709c27 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bus/hwsq.h @@ -0,0 +1,141 @@ +#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) +{ + struct nvkm_bus *pbus = nvkm_bus(subdev); + int ret; + + ret = nvkm_hwsq_init(pbus, &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) +{ + if (reg->sequence != ram->sequence) + reg->data = nv_rd32(ram->subdev, 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_nsec(struct hwsq *ram, u32 nsec) +{ + nvkm_hwsq_nsec(ram->hwsq, nsec); +} +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv04.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv04.c new file mode 100644 index 000000000..19c8e50ee --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv04.c @@ -0,0 +1,94 @@ +/* + * 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 "nv04.h" + +static void +nv04_bus_intr(struct nvkm_subdev *subdev) +{ + struct nvkm_bus *pbus = nvkm_bus(subdev); + u32 stat = nv_rd32(pbus, 0x001100) & nv_rd32(pbus, 0x001140); + + if (stat & 0x00000001) { + nv_error(pbus, "BUS ERROR\n"); + stat &= ~0x00000001; + nv_wr32(pbus, 0x001100, 0x00000001); + } + + if (stat & 0x00000110) { + subdev = nvkm_subdev(subdev, NVDEV_SUBDEV_GPIO); + if (subdev && subdev->intr) + subdev->intr(subdev); + stat &= ~0x00000110; + nv_wr32(pbus, 0x001100, 0x00000110); + } + + if (stat) { + nv_error(pbus, "unknown intr 0x%08x\n", stat); + nv_mask(pbus, 0x001140, stat, 0x00000000); + } +} + +static int +nv04_bus_init(struct nvkm_object *object) +{ + struct nv04_bus_priv *priv = (void *)object; + + nv_wr32(priv, 0x001100, 0xffffffff); + nv_wr32(priv, 0x001140, 0x00000111); + + return nvkm_bus_init(&priv->base); +} + +int +nv04_bus_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv04_bus_impl *impl = (void *)oclass; + struct nv04_bus_priv *priv; + int ret; + + ret = nvkm_bus_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->intr = impl->intr; + priv->base.hwsq_exec = impl->hwsq_exec; + priv->base.hwsq_size = impl->hwsq_size; + return 0; +} + +struct nvkm_oclass * +nv04_bus_oclass = &(struct nv04_bus_impl) { + .base.handle = NV_SUBDEV(BUS, 0x04), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_bus_ctor, + .dtor = _nvkm_bus_dtor, + .init = nv04_bus_init, + .fini = _nvkm_bus_fini, + }, + .intr = nv04_bus_intr, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv04.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv04.h new file mode 100644 index 000000000..3ddc8f91b --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv04.h @@ -0,0 +1,21 @@ +#ifndef __NVKM_BUS_NV04_H__ +#define __NVKM_BUS_NV04_H__ +#include + +struct nv04_bus_priv { + struct nvkm_bus base; +}; + +int nv04_bus_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); +int nv50_bus_init(struct nvkm_object *); +void nv50_bus_intr(struct nvkm_subdev *); + +struct nv04_bus_impl { + struct nvkm_oclass base; + void (*intr)(struct nvkm_subdev *); + int (*hwsq_exec)(struct nvkm_bus *, u32 *, u32); + u32 hwsq_size; +}; +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv31.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv31.c new file mode 100644 index 000000000..c5739bce8 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv31.c @@ -0,0 +1,91 @@ +/* + * 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 "nv04.h" + +static void +nv31_bus_intr(struct nvkm_subdev *subdev) +{ + struct nvkm_bus *pbus = nvkm_bus(subdev); + u32 stat = nv_rd32(pbus, 0x001100) & nv_rd32(pbus, 0x001140); + u32 gpio = nv_rd32(pbus, 0x001104) & nv_rd32(pbus, 0x001144); + + if (gpio) { + subdev = nvkm_subdev(pbus, NVDEV_SUBDEV_GPIO); + if (subdev && subdev->intr) + subdev->intr(subdev); + } + + if (stat & 0x00000008) { /* NV41- */ + u32 addr = nv_rd32(pbus, 0x009084); + u32 data = nv_rd32(pbus, 0x009088); + + nv_error(pbus, "MMIO %s of 0x%08x FAULT at 0x%06x\n", + (addr & 0x00000002) ? "write" : "read", data, + (addr & 0x00fffffc)); + + stat &= ~0x00000008; + nv_wr32(pbus, 0x001100, 0x00000008); + } + + if (stat & 0x00070000) { + subdev = nvkm_subdev(pbus, NVDEV_SUBDEV_THERM); + if (subdev && subdev->intr) + subdev->intr(subdev); + stat &= ~0x00070000; + nv_wr32(pbus, 0x001100, 0x00070000); + } + + if (stat) { + nv_error(pbus, "unknown intr 0x%08x\n", stat); + nv_mask(pbus, 0x001140, stat, 0x00000000); + } +} + +static int +nv31_bus_init(struct nvkm_object *object) +{ + struct nv04_bus_priv *priv = (void *)object; + int ret; + + ret = nvkm_bus_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, 0x001100, 0xffffffff); + nv_wr32(priv, 0x001140, 0x00070008); + return 0; +} + +struct nvkm_oclass * +nv31_bus_oclass = &(struct nv04_bus_impl) { + .base.handle = NV_SUBDEV(BUS, 0x31), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_bus_ctor, + .dtor = _nvkm_bus_dtor, + .init = nv31_bus_init, + .fini = _nvkm_bus_fini, + }, + .intr = nv31_bus_intr, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv50.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv50.c new file mode 100644 index 000000000..1987863d7 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv50.c @@ -0,0 +1,104 @@ +/* + * 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 "nv04.h" + +#include + +static int +nv50_bus_hwsq_exec(struct nvkm_bus *pbus, u32 *data, u32 size) +{ + struct nv50_bus_priv *priv = (void *)pbus; + int i; + + nv_mask(pbus, 0x001098, 0x00000008, 0x00000000); + nv_wr32(pbus, 0x001304, 0x00000000); + for (i = 0; i < size; i++) + nv_wr32(priv, 0x001400 + (i * 4), data[i]); + nv_mask(pbus, 0x001098, 0x00000018, 0x00000018); + nv_wr32(pbus, 0x00130c, 0x00000003); + + return nv_wait(pbus, 0x001308, 0x00000100, 0x00000000) ? 0 : -ETIMEDOUT; +} + +void +nv50_bus_intr(struct nvkm_subdev *subdev) +{ + struct nvkm_bus *pbus = nvkm_bus(subdev); + u32 stat = nv_rd32(pbus, 0x001100) & nv_rd32(pbus, 0x001140); + + if (stat & 0x00000008) { + u32 addr = nv_rd32(pbus, 0x009084); + u32 data = nv_rd32(pbus, 0x009088); + + nv_error(pbus, "MMIO %s of 0x%08x FAULT at 0x%06x\n", + (addr & 0x00000002) ? "write" : "read", data, + (addr & 0x00fffffc)); + + stat &= ~0x00000008; + nv_wr32(pbus, 0x001100, 0x00000008); + } + + if (stat & 0x00010000) { + subdev = nvkm_subdev(pbus, NVDEV_SUBDEV_THERM); + if (subdev && subdev->intr) + subdev->intr(subdev); + stat &= ~0x00010000; + nv_wr32(pbus, 0x001100, 0x00010000); + } + + if (stat) { + nv_error(pbus, "unknown intr 0x%08x\n", stat); + nv_mask(pbus, 0x001140, stat, 0); + } +} + +int +nv50_bus_init(struct nvkm_object *object) +{ + struct nv04_bus_priv *priv = (void *)object; + int ret; + + ret = nvkm_bus_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, 0x001100, 0xffffffff); + nv_wr32(priv, 0x001140, 0x00010008); + return 0; +} + +struct nvkm_oclass * +nv50_bus_oclass = &(struct nv04_bus_impl) { + .base.handle = NV_SUBDEV(BUS, 0x50), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_bus_ctor, + .dtor = _nvkm_bus_dtor, + .init = nv50_bus_init, + .fini = _nvkm_bus_fini, + }, + .intr = nv50_bus_intr, + .hwsq_exec = nv50_bus_hwsq_exec, + .hwsq_size = 64, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/Kbuild new file mode 100644 index 000000000..9c2f688c9 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/Kbuild @@ -0,0 +1,12 @@ +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/pllnv04.o +nvkm-y += nvkm/subdev/clk/pllgt215.o diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c new file mode 100644 index 000000000..39a83d82e --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c @@ -0,0 +1,591 @@ +/* + * 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 +#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 = nvkm_bios(clk); + struct nvbios_boostE boostE; + u8 ver, hdr, cnt, len; + u16 data; + + data = nvbios_boostEm(bios, pstate, &ver, &hdr, &cnt, &len, &boostE); + if (data) { + struct nvbios_boostS boostS; + u8 idx = 0, sver, shdr; + u16 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 int +nvkm_cstate_prog(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei) +{ + struct nvkm_therm *ptherm = nvkm_therm(clk); + struct nvkm_volt *volt = nvkm_volt(clk); + struct nvkm_cstate *cstate; + int ret; + + if (!list_empty(&pstate->list)) { + cstate = list_entry(pstate->list.prev, typeof(*cstate), head); + } else { + cstate = &pstate->base; + } + + if (ptherm) { + ret = nvkm_therm_cstate(ptherm, pstate->fanspeed, +1); + if (ret && ret != -ENODEV) { + nv_error(clk, "failed to raise fan speed: %d\n", ret); + return ret; + } + } + + if (volt) { + ret = volt->set_id(volt, cstate->voltage, +1); + if (ret && ret != -ENODEV) { + nv_error(clk, "failed to raise voltage: %d\n", ret); + return ret; + } + } + + ret = clk->calc(clk, cstate); + if (ret == 0) { + ret = clk->prog(clk); + clk->tidy(clk); + } + + if (volt) { + ret = volt->set_id(volt, cstate->voltage, -1); + if (ret && ret != -ENODEV) + nv_error(clk, "failed to lower voltage: %d\n", ret); + } + + if (ptherm) { + ret = nvkm_therm_cstate(ptherm, pstate->fanspeed, -1); + if (ret && ret != -ENODEV) + nv_error(clk, "failed to lower fan speed: %d\n", ret); + } + + return 0; +} + +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 = nvkm_bios(clk); + struct nvkm_domain *domain = clk->domains; + struct nvkm_cstate *cstate = NULL; + struct nvbios_cstepX cstepX; + u8 ver, hdr; + u16 data; + + data = nvbios_cstepXp(bios, idx, &ver, &hdr, &cstepX); + if (!data) + return -ENOENT; + + cstate = kzalloc(sizeof(*cstate), GFP_KERNEL); + if (!cstate) + return -ENOMEM; + + *cstate = pstate->base; + cstate->voltage = cstepX.voltage; + + 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_fb *pfb = nvkm_fb(clk); + struct nvkm_pstate *pstate; + int ret, idx = 0; + + list_for_each_entry(pstate, &clk->states, head) { + if (idx++ == pstatei) + break; + } + + nv_debug(clk, "setting performance state %d\n", pstatei); + clk->pstate = pstatei; + + if (pfb->ram && pfb->ram->calc) { + int khz = pstate->base.domain[nv_clk_src_mem]; + do { + ret = pfb->ram->calc(pfb, khz); + if (ret == 0) + ret = pfb->ram->prog(pfb); + } while (ret > 0); + pfb->ram->tidy(pfb); + } + + return nvkm_cstate_prog(clk, pstate, 0); +} + +static void +nvkm_pstate_work(struct work_struct *work) +{ + struct nvkm_clk *clk = container_of(work, typeof(*clk), work); + int pstate; + + if (!atomic_xchg(&clk->waiting, 0)) + return; + clk->pwrsrc = power_supply_is_system_supplied(); + + nv_trace(clk, "P %d PWR %d U(AC) %d U(DC) %d A %d T %d D %d\n", + clk->pstate, clk->pwrsrc, clk->ustate_ac, clk->ustate_dc, + clk->astate, clk->tstate, 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 - clk->tstate); + pstate = max(pstate, clk->dstate); + } else { + pstate = clk->pstate = -1; + } + + nv_trace(clk, "-> %d\n", pstate); + if (pstate != clk->pstate) { + int ret = nvkm_pstate_prog(clk, pstate); + if (ret) { + nv_error(clk, "error setting pstate %d: %d\n", + pstate, ret); + } + } + + wake_up_all(&clk->wait); + nvkm_notify_get(&clk->pwrsrc_ntfy); +} + +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) +{ + struct nvkm_domain *clock = clk->domains - 1; + struct nvkm_cstate *cstate; + 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; + + nv_debug(clk, "%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); + nv_debug(clk, "%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); + } + } + } + + nv_info(clk, "%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 = nvkm_bios(clk); + 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; + u16 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; + 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, int req, int rel) +{ + if (!rel) clk->tstate = req; + if ( rel) clk->tstate += rel; + clk->tstate = min(clk->tstate, 0); + clk->tstate = max(clk->tstate, -(clk->state_nr - 1)); + return nvkm_pstate_calc(clk, true); +} + +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); +} + +static int +nvkm_clk_pwrsrc(struct nvkm_notify *notify) +{ + struct nvkm_clk *clk = + container_of(notify, typeof(*clk), pwrsrc_ntfy); + nvkm_pstate_calc(clk, false); + return NVKM_NOTIFY_DROP; +} + +/****************************************************************************** + * subdev base class implementation + *****************************************************************************/ + +int +_nvkm_clk_fini(struct nvkm_object *object, bool suspend) +{ + struct nvkm_clk *clk = (void *)object; + nvkm_notify_put(&clk->pwrsrc_ntfy); + return nvkm_subdev_fini(&clk->base, suspend); +} + +int +_nvkm_clk_init(struct nvkm_object *object) +{ + struct nvkm_clk *clk = (void *)object; + struct nvkm_domain *clock = clk->domains; + int ret; + + ret = nvkm_subdev_init(&clk->base); + if (ret) + return 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 = clk->read(clk, clock->name); + if (ret < 0) { + nv_error(clk, "%02x freq unknown\n", clock->name); + return ret; + } + clk->bstate.base.domain[clock->name] = ret; + clock++; + } + + nvkm_pstate_info(clk, &clk->bstate); + + clk->astate = clk->state_nr - 1; + clk->tstate = 0; + clk->dstate = 0; + clk->pstate = -1; + nvkm_pstate_calc(clk, true); + return 0; +} + +void +_nvkm_clk_dtor(struct nvkm_object *object) +{ + struct nvkm_clk *clk = (void *)object; + struct nvkm_pstate *pstate, *temp; + + nvkm_notify_fini(&clk->pwrsrc_ntfy); + + list_for_each_entry_safe(pstate, temp, &clk->states, head) { + nvkm_pstate_del(pstate); + } + + nvkm_subdev_destroy(&clk->base); +} + +int +nvkm_clk_create_(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, struct nvkm_domain *clocks, + struct nvkm_pstate *pstates, int nb_pstates, + bool allow_reclock, int length, void **object) +{ + struct nvkm_device *device = nv_device(parent); + struct nvkm_clk *clk; + int ret, idx, arglen; + const char *mode; + + ret = nvkm_subdev_create_(parent, engine, oclass, 0, "CLK", + "clock", length, object); + clk = *object; + if (ret) + return ret; + + INIT_LIST_HEAD(&clk->states); + clk->domains = clocks; + clk->ustate_ac = -1; + clk->ustate_dc = -1; + + 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 (!pstates) { + idx = 0; + do { + ret = nvkm_pstate_new(clk, idx++); + } while (ret == 0); + } else { + for (idx = 0; idx < nb_pstates; idx++) + list_add_tail(&pstates[idx].head, &clk->states); + clk->state_nr = nb_pstates; + } + + clk->allow_reclock = allow_reclock; + + ret = nvkm_notify_init(NULL, &device->event, nvkm_clk_pwrsrc, true, + NULL, 0, 0, &clk->pwrsrc_ntfy); + if (ret) + return ret; + + 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); + + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/g84.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/g84.c new file mode 100644 index 000000000..4c90b9769 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/g84.c @@ -0,0 +1,47 @@ +/* + * 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 struct nvkm_domain +g84_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 } +}; + +struct nvkm_oclass * +g84_clk_oclass = &(struct nv50_clk_oclass) { + .base.handle = NV_SUBDEV(CLK, 0x84), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_clk_ctor, + .dtor = _nvkm_clk_dtor, + .init = _nvkm_clk_init, + .fini = _nvkm_clk_fini, + }, + .domains = g84_domains, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gf100.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gf100.c new file mode 100644 index 000000000..3d7330d54 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gf100.c @@ -0,0 +1,462 @@ +/* + * 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 "pll.h" + +#include +#include +#include +#include + +struct gf100_clk_info { + u32 freq; + u32 ssel; + u32 mdiv; + u32 dsrc; + u32 ddiv; + u32 coef; +}; + +struct gf100_clk_priv { + struct nvkm_clk base; + struct gf100_clk_info eng[16]; +}; + +static u32 read_div(struct gf100_clk_priv *, int, u32, u32); + +static u32 +read_vco(struct gf100_clk_priv *priv, u32 dsrc) +{ + struct nvkm_clk *clk = &priv->base; + u32 ssrc = nv_rd32(priv, dsrc); + if (!(ssrc & 0x00000100)) + return clk->read(clk, nv_clk_src_sppll0); + return clk->read(clk, nv_clk_src_sppll1); +} + +static u32 +read_pll(struct gf100_clk_priv *priv, u32 pll) +{ + struct nvkm_clk *clk = &priv->base; + u32 ctrl = nv_rd32(priv, pll + 0x00); + u32 coef = nv_rd32(priv, 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 = nv_device(priv)->crystal; + P = 1; + break; + case 0x132000: + sclk = clk->read(clk, nv_clk_src_mpllsrc); + break; + case 0x132020: + sclk = clk->read(clk, nv_clk_src_mpllsrcref); + break; + case 0x137000: + case 0x137020: + case 0x137040: + case 0x1370e0: + sclk = read_div(priv, (pll & 0xff) / 0x20, 0x137120, 0x137140); + break; + default: + return 0; + } + + return sclk * N / M / P; +} + +static u32 +read_div(struct gf100_clk_priv *priv, int doff, u32 dsrc, u32 dctl) +{ + u32 ssrc = nv_rd32(priv, dsrc + (doff * 4)); + u32 sctl = nv_rd32(priv, dctl + (doff * 4)); + + switch (ssrc & 0x00000003) { + case 0: + if ((ssrc & 0x00030000) != 0x00030000) + return nv_device(priv)->crystal; + return 108000; + case 2: + return 100000; + case 3: + if (sctl & 0x80000000) { + u32 sclk = read_vco(priv, dsrc + (doff * 4)); + u32 sdiv = (sctl & 0x0000003f) + 2; + return (sclk * 2) / sdiv; + } + + return read_vco(priv, dsrc + (doff * 4)); + default: + return 0; + } +} + +static u32 +read_clk(struct gf100_clk_priv *priv, int clk) +{ + u32 sctl = nv_rd32(priv, 0x137250 + (clk * 4)); + u32 ssel = nv_rd32(priv, 0x137100); + u32 sclk, sdiv; + + if (ssel & (1 << clk)) { + if (clk < 7) + sclk = read_pll(priv, 0x137000 + (clk * 0x20)); + else + sclk = read_pll(priv, 0x1370e0); + sdiv = ((sctl & 0x00003f00) >> 8) + 2; + } else { + sclk = read_div(priv, clk, 0x137160, 0x1371d0); + sdiv = ((sctl & 0x0000003f) >> 0) + 2; + } + + if (sctl & 0x80000000) + return (sclk * 2) / sdiv; + + return sclk; +} + +static int +gf100_clk_read(struct nvkm_clk *clk, enum nv_clk_src src) +{ + struct nvkm_device *device = nv_device(clk); + struct gf100_clk_priv *priv = (void *)clk; + + 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(priv, 0x00e800); + case nv_clk_src_sppll1: + return read_pll(priv, 0x00e820); + + case nv_clk_src_mpllsrcref: + return read_div(priv, 0, 0x137320, 0x137330); + case nv_clk_src_mpllsrc: + return read_pll(priv, 0x132020); + case nv_clk_src_mpll: + return read_pll(priv, 0x132000); + case nv_clk_src_mdiv: + return read_div(priv, 0, 0x137300, 0x137310); + case nv_clk_src_mem: + if (nv_rd32(priv, 0x1373f0) & 0x00000002) + return clk->read(clk, nv_clk_src_mpll); + return clk->read(clk, nv_clk_src_mdiv); + + case nv_clk_src_gpc: + return read_clk(priv, 0x00); + case nv_clk_src_rop: + return read_clk(priv, 0x01); + case nv_clk_src_hubk07: + return read_clk(priv, 0x02); + case nv_clk_src_hubk06: + return read_clk(priv, 0x07); + case nv_clk_src_hubk01: + return read_clk(priv, 0x08); + case nv_clk_src_copy: + return read_clk(priv, 0x09); + case nv_clk_src_daemon: + return read_clk(priv, 0x0c); + case nv_clk_src_vdec: + return read_clk(priv, 0x0e); + default: + nv_error(clk, "invalid clock source %d\n", src); + return -EINVAL; + } +} + +static u32 +calc_div(struct gf100_clk_priv *priv, int clk, u32 ref, u32 freq, u32 *ddiv) +{ + u32 div = min((ref * 2) / freq, (u32)65); + if (div < 2) + div = 2; + + *ddiv = div - 2; + return (ref * 2) / div; +} + +static u32 +calc_src(struct gf100_clk_priv *priv, int clk, u32 freq, u32 *dsrc, u32 *ddiv) +{ + u32 sclk; + + /* use one of the fixed frequencies if possible */ + *ddiv = 0x00000000; + switch (freq) { + case 27000: + case 108000: + *dsrc = 0x00000000; + if (freq == 108000) + *dsrc |= 0x00030000; + return freq; + case 100000: + *dsrc = 0x00000002; + return freq; + default: + *dsrc = 0x00000003; + break; + } + + /* otherwise, calculate the closest divider */ + sclk = read_vco(priv, 0x137160 + (clk * 4)); + if (clk < 7) + sclk = calc_div(priv, clk, sclk, freq, ddiv); + return sclk; +} + +static u32 +calc_pll(struct gf100_clk_priv *priv, int clk, u32 freq, u32 *coef) +{ + struct nvkm_bios *bios = nvkm_bios(priv); + struct nvbios_pll limits; + int N, M, P, ret; + + ret = nvbios_pll_parse(bios, 0x137000 + (clk * 0x20), &limits); + if (ret) + return 0; + + limits.refclk = read_div(priv, clk, 0x137120, 0x137140); + if (!limits.refclk) + return 0; + + ret = gt215_pll_calc(nv_subdev(priv), &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_priv *priv, + struct nvkm_cstate *cstate, int clk, int dom) +{ + struct gf100_clk_info *info = &priv->eng[clk]; + 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(priv, clk, freq, &src0, &div0); + clk0 = calc_div(priv, clk, clk0, freq, &div1D); + + /* see if we can get any closer using PLLs */ + if (clk0 != freq && (0x00004387 & (1 << clk))) { + if (clk <= 7) + clk1 = calc_pll(priv, clk, freq, &info->coef); + else + clk1 = cstate->domain[nv_clk_src_hubk06]; + clk1 = calc_div(priv, clk, clk1, freq, &div1P); + } + + /* select the method which gets closest to target freq */ + if (abs((int)freq - clk0) <= abs((int)freq - clk1)) { + info->dsrc = src0; + if (div0) { + info->ddiv |= 0x80000000; + info->ddiv |= div0 << 8; + info->ddiv |= div0; + } + if (div1D) { + info->mdiv |= 0x80000000; + info->mdiv |= div1D; + } + info->ssel = info->coef = 0; + info->freq = clk0; + } else { + if (div1P) { + info->mdiv |= 0x80000000; + info->mdiv |= div1P << 8; + } + info->ssel = (1 << clk); + info->freq = clk1; + } + + return 0; +} + +static int +gf100_clk_calc(struct nvkm_clk *clk, struct nvkm_cstate *cstate) +{ + struct gf100_clk_priv *priv = (void *)clk; + int ret; + + if ((ret = calc_clk(priv, cstate, 0x00, nv_clk_src_gpc)) || + (ret = calc_clk(priv, cstate, 0x01, nv_clk_src_rop)) || + (ret = calc_clk(priv, cstate, 0x02, nv_clk_src_hubk07)) || + (ret = calc_clk(priv, cstate, 0x07, nv_clk_src_hubk06)) || + (ret = calc_clk(priv, cstate, 0x08, nv_clk_src_hubk01)) || + (ret = calc_clk(priv, cstate, 0x09, nv_clk_src_copy)) || + (ret = calc_clk(priv, cstate, 0x0c, nv_clk_src_daemon)) || + (ret = calc_clk(priv, cstate, 0x0e, nv_clk_src_vdec))) + return ret; + + return 0; +} + +static void +gf100_clk_prog_0(struct gf100_clk_priv *priv, int clk) +{ + struct gf100_clk_info *info = &priv->eng[clk]; + if (clk < 7 && !info->ssel) { + nv_mask(priv, 0x1371d0 + (clk * 0x04), 0x80003f3f, info->ddiv); + nv_wr32(priv, 0x137160 + (clk * 0x04), info->dsrc); + } +} + +static void +gf100_clk_prog_1(struct gf100_clk_priv *priv, int clk) +{ + nv_mask(priv, 0x137100, (1 << clk), 0x00000000); + nv_wait(priv, 0x137100, (1 << clk), 0x00000000); +} + +static void +gf100_clk_prog_2(struct gf100_clk_priv *priv, int clk) +{ + struct gf100_clk_info *info = &priv->eng[clk]; + const u32 addr = 0x137000 + (clk * 0x20); + if (clk <= 7) { + nv_mask(priv, addr + 0x00, 0x00000004, 0x00000000); + nv_mask(priv, addr + 0x00, 0x00000001, 0x00000000); + if (info->coef) { + nv_wr32(priv, addr + 0x04, info->coef); + nv_mask(priv, addr + 0x00, 0x00000001, 0x00000001); + nv_wait(priv, addr + 0x00, 0x00020000, 0x00020000); + nv_mask(priv, addr + 0x00, 0x00020004, 0x00000004); + } + } +} + +static void +gf100_clk_prog_3(struct gf100_clk_priv *priv, int clk) +{ + struct gf100_clk_info *info = &priv->eng[clk]; + if (info->ssel) { + nv_mask(priv, 0x137100, (1 << clk), info->ssel); + nv_wait(priv, 0x137100, (1 << clk), info->ssel); + } +} + +static void +gf100_clk_prog_4(struct gf100_clk_priv *priv, int clk) +{ + struct gf100_clk_info *info = &priv->eng[clk]; + nv_mask(priv, 0x137250 + (clk * 0x04), 0x00003f3f, info->mdiv); +} + +static int +gf100_clk_prog(struct nvkm_clk *clk) +{ + struct gf100_clk_priv *priv = (void *)clk; + struct { + void (*exec)(struct gf100_clk_priv *, 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(priv->eng); j++) { + if (!priv->eng[j].freq) + continue; + stage[i].exec(priv, j); + } + } + + return 0; +} + +static void +gf100_clk_tidy(struct nvkm_clk *clk) +{ + struct gf100_clk_priv *priv = (void *)clk; + memset(priv->eng, 0x00, sizeof(priv->eng)); +} + +static struct nvkm_domain +gf100_domain[] = { + { 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, 0, "core", 2000 }, + { nv_clk_src_rop , 0x04 }, + { nv_clk_src_mem , 0x05, 0, "memory", 1000 }, + { nv_clk_src_vdec , 0x06 }, + { nv_clk_src_daemon , 0x0a }, + { nv_clk_src_hubk07 , 0x0b }, + { nv_clk_src_max } +}; + +static int +gf100_clk_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gf100_clk_priv *priv; + int ret; + + ret = nvkm_clk_create(parent, engine, oclass, gf100_domain, + NULL, 0, false, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.read = gf100_clk_read; + priv->base.calc = gf100_clk_calc; + priv->base.prog = gf100_clk_prog; + priv->base.tidy = gf100_clk_tidy; + return 0; +} + +struct nvkm_oclass +gf100_clk_oclass = { + .handle = NV_SUBDEV(CLK, 0xc0), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_clk_ctor, + .dtor = _nvkm_clk_dtor, + .init = _nvkm_clk_init, + .fini = _nvkm_clk_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk104.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk104.c new file mode 100644 index 000000000..e9b2310bd --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk104.c @@ -0,0 +1,500 @@ +/* + * 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 "pll.h" + +#include +#include +#include +#include + +struct gk104_clk_info { + u32 freq; + u32 ssel; + u32 mdiv; + u32 dsrc; + u32 ddiv; + u32 coef; +}; + +struct gk104_clk_priv { + struct nvkm_clk base; + struct gk104_clk_info eng[16]; +}; + +static u32 read_div(struct gk104_clk_priv *, int, u32, u32); +static u32 read_pll(struct gk104_clk_priv *, u32); + +static u32 +read_vco(struct gk104_clk_priv *priv, u32 dsrc) +{ + u32 ssrc = nv_rd32(priv, dsrc); + if (!(ssrc & 0x00000100)) + return read_pll(priv, 0x00e800); + return read_pll(priv, 0x00e820); +} + +static u32 +read_pll(struct gk104_clk_priv *priv, u32 pll) +{ + u32 ctrl = nv_rd32(priv, pll + 0x00); + u32 coef = nv_rd32(priv, 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 = nv_device(priv)->crystal; + P = 1; + break; + case 0x132000: + sclk = read_pll(priv, 0x132020); + P = (coef & 0x10000000) ? 2 : 1; + break; + case 0x132020: + sclk = read_div(priv, 0, 0x137320, 0x137330); + fN = nv_rd32(priv, pll + 0x10) >> 16; + break; + case 0x137000: + case 0x137020: + case 0x137040: + case 0x1370e0: + sclk = read_div(priv, (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_priv *priv, int doff, u32 dsrc, u32 dctl) +{ + u32 ssrc = nv_rd32(priv, dsrc + (doff * 4)); + u32 sctl = nv_rd32(priv, dctl + (doff * 4)); + + switch (ssrc & 0x00000003) { + case 0: + if ((ssrc & 0x00030000) != 0x00030000) + return nv_device(priv)->crystal; + return 108000; + case 2: + return 100000; + case 3: + if (sctl & 0x80000000) { + u32 sclk = read_vco(priv, dsrc + (doff * 4)); + u32 sdiv = (sctl & 0x0000003f) + 2; + return (sclk * 2) / sdiv; + } + + return read_vco(priv, dsrc + (doff * 4)); + default: + return 0; + } +} + +static u32 +read_mem(struct gk104_clk_priv *priv) +{ + switch (nv_rd32(priv, 0x1373f4) & 0x0000000f) { + case 1: return read_pll(priv, 0x132020); + case 2: return read_pll(priv, 0x132000); + default: + return 0; + } +} + +static u32 +read_clk(struct gk104_clk_priv *priv, int clk) +{ + u32 sctl = nv_rd32(priv, 0x137250 + (clk * 4)); + u32 sclk, sdiv; + + if (clk < 7) { + u32 ssel = nv_rd32(priv, 0x137100); + if (ssel & (1 << clk)) { + sclk = read_pll(priv, 0x137000 + (clk * 0x20)); + sdiv = 1; + } else { + sclk = read_div(priv, clk, 0x137160, 0x1371d0); + sdiv = 0; + } + } else { + u32 ssrc = nv_rd32(priv, 0x137160 + (clk * 0x04)); + if ((ssrc & 0x00000003) == 0x00000003) { + sclk = read_div(priv, clk, 0x137160, 0x1371d0); + if (ssrc & 0x00000100) { + if (ssrc & 0x40000000) + sclk = read_pll(priv, 0x1370e0); + sdiv = 1; + } else { + sdiv = 0; + } + } else { + sclk = read_div(priv, clk, 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 *clk, enum nv_clk_src src) +{ + struct nvkm_device *device = nv_device(clk); + struct gk104_clk_priv *priv = (void *)clk; + + 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(priv); + case nv_clk_src_gpc: + return read_clk(priv, 0x00); + case nv_clk_src_rop: + return read_clk(priv, 0x01); + case nv_clk_src_hubk07: + return read_clk(priv, 0x02); + case nv_clk_src_hubk06: + return read_clk(priv, 0x07); + case nv_clk_src_hubk01: + return read_clk(priv, 0x08); + case nv_clk_src_daemon: + return read_clk(priv, 0x0c); + case nv_clk_src_vdec: + return read_clk(priv, 0x0e); + default: + nv_error(clk, "invalid clock source %d\n", src); + return -EINVAL; + } +} + +static u32 +calc_div(struct gk104_clk_priv *priv, int clk, u32 ref, u32 freq, u32 *ddiv) +{ + u32 div = min((ref * 2) / freq, (u32)65); + if (div < 2) + div = 2; + + *ddiv = div - 2; + return (ref * 2) / div; +} + +static u32 +calc_src(struct gk104_clk_priv *priv, int clk, u32 freq, u32 *dsrc, u32 *ddiv) +{ + u32 sclk; + + /* use one of the fixed frequencies if possible */ + *ddiv = 0x00000000; + switch (freq) { + case 27000: + case 108000: + *dsrc = 0x00000000; + if (freq == 108000) + *dsrc |= 0x00030000; + return freq; + case 100000: + *dsrc = 0x00000002; + return freq; + default: + *dsrc = 0x00000003; + break; + } + + /* otherwise, calculate the closest divider */ + sclk = read_vco(priv, 0x137160 + (clk * 4)); + if (clk < 7) + sclk = calc_div(priv, clk, sclk, freq, ddiv); + return sclk; +} + +static u32 +calc_pll(struct gk104_clk_priv *priv, int clk, u32 freq, u32 *coef) +{ + struct nvkm_bios *bios = nvkm_bios(priv); + struct nvbios_pll limits; + int N, M, P, ret; + + ret = nvbios_pll_parse(bios, 0x137000 + (clk * 0x20), &limits); + if (ret) + return 0; + + limits.refclk = read_div(priv, clk, 0x137120, 0x137140); + if (!limits.refclk) + return 0; + + ret = gt215_pll_calc(nv_subdev(priv), &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_priv *priv, + struct nvkm_cstate *cstate, int clk, int dom) +{ + struct gk104_clk_info *info = &priv->eng[clk]; + 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(priv, clk, freq, &src0, &div0); + clk0 = calc_div(priv, clk, clk0, freq, &div1D); + + /* see if we can get any closer using PLLs */ + if (clk0 != freq && (0x0000ff87 & (1 << clk))) { + if (clk <= 7) + clk1 = calc_pll(priv, clk, freq, &info->coef); + else + clk1 = cstate->domain[nv_clk_src_hubk06]; + clk1 = calc_div(priv, clk, clk1, freq, &div1P); + } + + /* select the method which gets closest to target freq */ + if (abs((int)freq - clk0) <= abs((int)freq - clk1)) { + info->dsrc = src0; + if (div0) { + info->ddiv |= 0x80000000; + info->ddiv |= div0; + } + if (div1D) { + info->mdiv |= 0x80000000; + info->mdiv |= div1D; + } + info->ssel = 0; + info->freq = clk0; + } else { + if (div1P) { + info->mdiv |= 0x80000000; + info->mdiv |= div1P << 8; + } + info->ssel = (1 << clk); + info->dsrc = 0x40000100; + info->freq = clk1; + } + + return 0; +} + +static int +gk104_clk_calc(struct nvkm_clk *clk, struct nvkm_cstate *cstate) +{ + struct gk104_clk_priv *priv = (void *)clk; + int ret; + + if ((ret = calc_clk(priv, cstate, 0x00, nv_clk_src_gpc)) || + (ret = calc_clk(priv, cstate, 0x01, nv_clk_src_rop)) || + (ret = calc_clk(priv, cstate, 0x02, nv_clk_src_hubk07)) || + (ret = calc_clk(priv, cstate, 0x07, nv_clk_src_hubk06)) || + (ret = calc_clk(priv, cstate, 0x08, nv_clk_src_hubk01)) || + (ret = calc_clk(priv, cstate, 0x0c, nv_clk_src_daemon)) || + (ret = calc_clk(priv, cstate, 0x0e, nv_clk_src_vdec))) + return ret; + + return 0; +} + +static void +gk104_clk_prog_0(struct gk104_clk_priv *priv, int clk) +{ + struct gk104_clk_info *info = &priv->eng[clk]; + if (!info->ssel) { + nv_mask(priv, 0x1371d0 + (clk * 0x04), 0x8000003f, info->ddiv); + nv_wr32(priv, 0x137160 + (clk * 0x04), info->dsrc); + } +} + +static void +gk104_clk_prog_1_0(struct gk104_clk_priv *priv, int clk) +{ + nv_mask(priv, 0x137100, (1 << clk), 0x00000000); + nv_wait(priv, 0x137100, (1 << clk), 0x00000000); +} + +static void +gk104_clk_prog_1_1(struct gk104_clk_priv *priv, int clk) +{ + nv_mask(priv, 0x137160 + (clk * 0x04), 0x00000100, 0x00000000); +} + +static void +gk104_clk_prog_2(struct gk104_clk_priv *priv, int clk) +{ + struct gk104_clk_info *info = &priv->eng[clk]; + const u32 addr = 0x137000 + (clk * 0x20); + nv_mask(priv, addr + 0x00, 0x00000004, 0x00000000); + nv_mask(priv, addr + 0x00, 0x00000001, 0x00000000); + if (info->coef) { + nv_wr32(priv, addr + 0x04, info->coef); + nv_mask(priv, addr + 0x00, 0x00000001, 0x00000001); + nv_wait(priv, addr + 0x00, 0x00020000, 0x00020000); + nv_mask(priv, addr + 0x00, 0x00020004, 0x00000004); + } +} + +static void +gk104_clk_prog_3(struct gk104_clk_priv *priv, int clk) +{ + struct gk104_clk_info *info = &priv->eng[clk]; + if (info->ssel) + nv_mask(priv, 0x137250 + (clk * 0x04), 0x00003f00, info->mdiv); + else + nv_mask(priv, 0x137250 + (clk * 0x04), 0x0000003f, info->mdiv); +} + +static void +gk104_clk_prog_4_0(struct gk104_clk_priv *priv, int clk) +{ + struct gk104_clk_info *info = &priv->eng[clk]; + if (info->ssel) { + nv_mask(priv, 0x137100, (1 << clk), info->ssel); + nv_wait(priv, 0x137100, (1 << clk), info->ssel); + } +} + +static void +gk104_clk_prog_4_1(struct gk104_clk_priv *priv, int clk) +{ + struct gk104_clk_info *info = &priv->eng[clk]; + if (info->ssel) { + nv_mask(priv, 0x137160 + (clk * 0x04), 0x40000000, 0x40000000); + nv_mask(priv, 0x137160 + (clk * 0x04), 0x00000100, 0x00000100); + } +} + +static int +gk104_clk_prog(struct nvkm_clk *clk) +{ + struct gk104_clk_priv *priv = (void *)clk; + struct { + u32 mask; + void (*exec)(struct gk104_clk_priv *, 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(priv->eng); j++) { + if (!(stage[i].mask & (1 << j))) + continue; + if (!priv->eng[j].freq) + continue; + stage[i].exec(priv, j); + } + } + + return 0; +} + +static void +gk104_clk_tidy(struct nvkm_clk *clk) +{ + struct gk104_clk_priv *priv = (void *)clk; + memset(priv->eng, 0x00, sizeof(priv->eng)); +} + +static struct nvkm_domain +gk104_domain[] = { + { nv_clk_src_crystal, 0xff }, + { nv_clk_src_href , 0xff }, + { nv_clk_src_gpc , 0x00, NVKM_CLK_DOM_FLAG_CORE, "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_daemon , 0x07 }, + { nv_clk_src_max } +}; + +static int +gk104_clk_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gk104_clk_priv *priv; + int ret; + + ret = nvkm_clk_create(parent, engine, oclass, gk104_domain, + NULL, 0, true, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.read = gk104_clk_read; + priv->base.calc = gk104_clk_calc; + priv->base.prog = gk104_clk_prog; + priv->base.tidy = gk104_clk_tidy; + return 0; +} + +struct nvkm_oclass +gk104_clk_oclass = { + .handle = NV_SUBDEV(CLK, 0xe0), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gk104_clk_ctor, + .dtor = _nvkm_clk_dtor, + .init = _nvkm_clk_init, + .fini = _nvkm_clk_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.c new file mode 100644 index 000000000..65c532742 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.c @@ -0,0 +1,680 @@ +/* + * 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. + * + * Shamelessly ripped off from ChromeOS's gk20a/clk_pllg.c + * + */ +#include +#include + +#include + +#ifdef __KERNEL__ +#include +#endif + +#define MHZ (1000 * 1000) + +#define MASK(w) ((1 << w) - 1) + +#define SYS_GPCPLL_CFG_BASE 0x00137000 +#define GPC_BCASE_GPCPLL_CFG_BASE 0x00132800 + +#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_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_P_SHIFT 16 +#define GPCPLL_COEFF_P_WIDTH 6 + +#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_PLL_STEPB_SHIFT 16 + +#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 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_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)) + +#define GPC_BCAST_NDIV_SLOWDOWN_DEBUG (GPC_BCASE_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) + +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, +}; + +/* All frequencies in Mhz */ +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; +}; + +static const struct gk20a_clk_pllg_params gk20a_pllg_params = { + .min_vco = 1000, .max_vco = 2064, + .min_u = 12, .max_u = 38, + .min_m = 1, .max_m = 255, + .min_n = 8, .max_n = 255, + .min_pl = 1, .max_pl = 32, +}; + +struct gk20a_clk_priv { + struct nvkm_clk base; + const struct gk20a_clk_pllg_params *params; + u32 m, n, pl; + u32 parent_rate; +}; +#define to_gk20a_clk(base) container_of(base, struct gk20a_clk_priv, base) + +static void +gk20a_pllg_read_mnp(struct gk20a_clk_priv *priv) +{ + u32 val; + + val = nv_rd32(priv, GPCPLL_COEFF); + priv->m = (val >> GPCPLL_COEFF_M_SHIFT) & MASK(GPCPLL_COEFF_M_WIDTH); + priv->n = (val >> GPCPLL_COEFF_N_SHIFT) & MASK(GPCPLL_COEFF_N_WIDTH); + priv->pl = (val >> GPCPLL_COEFF_P_SHIFT) & MASK(GPCPLL_COEFF_P_WIDTH); +} + +static u32 +gk20a_pllg_calc_rate(struct gk20a_clk_priv *priv) +{ + u32 rate; + u32 divider; + + rate = priv->parent_rate * priv->n; + divider = priv->m * pl_to_div[priv->pl]; + do_div(rate, divider); + + return rate / 2; +} + +static int +gk20a_pllg_calc_mnp(struct gk20a_clk_priv *priv, unsigned long rate) +{ + 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, vco_f; + u32 best_m, best_n; + u32 u_f; + u32 m, n, n2; + u32 delta, lwv, best_delta = ~0; + u32 pl; + + target_clk_f = rate * 2 / MHZ; + ref_clk_f = priv->parent_rate / MHZ; + + max_vco_f = priv->params->max_vco; + min_vco_f = priv->params->min_vco; + best_m = priv->params->max_m; + best_n = priv->params->min_n; + best_pl = priv->params->min_pl; + + target_vco_f = target_clk_f + target_clk_f / 50; + if (max_vco_f < target_vco_f) + max_vco_f = target_vco_f; + + /* min_pl <= high_pl <= max_pl */ + high_pl = (max_vco_f + target_vco_f - 1) / target_vco_f; + high_pl = min(high_pl, priv->params->max_pl); + high_pl = max(high_pl, priv->params->min_pl); + + /* min_pl <= low_pl <= max_pl */ + low_pl = min_vco_f / target_vco_f; + low_pl = min(low_pl, priv->params->max_pl); + low_pl = max(low_pl, priv->params->min_pl); + + /* Find Indices of high_pl and low_pl */ + for (pl = 0; pl < ARRAY_SIZE(pl_to_div) - 1; pl++) { + if (pl_to_div[pl] >= low_pl) { + low_pl = pl; + break; + } + } + for (pl = 0; pl < ARRAY_SIZE(pl_to_div) - 1; pl++) { + if (pl_to_div[pl] >= high_pl) { + high_pl = pl; + break; + } + } + + nv_debug(priv, "low_PL %d(div%d), high_PL %d(div%d)", low_pl, + pl_to_div[low_pl], high_pl, pl_to_div[high_pl]); + + /* Select lowest possible VCO */ + for (pl = low_pl; pl <= high_pl; pl++) { + target_vco_f = target_clk_f * pl_to_div[pl]; + for (m = priv->params->min_m; m <= priv->params->max_m; m++) { + u_f = ref_clk_f / m; + + if (u_f < priv->params->min_u) + break; + if (u_f > priv->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 > priv->params->max_n) + break; + + for (; n <= n2; n++) { + if (n < priv->params->min_n) + continue; + if (n > priv->params->max_n) + break; + + vco_f = ref_clk_f * n / m; + + if (vco_f >= min_vco_f && vco_f <= max_vco_f) { + lwv = (vco_f + (pl_to_div[pl] / 2)) + / 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) + nv_debug(priv, "no best match for target @ %dMHz on gpc_pll", + target_clk_f); + + priv->m = best_m; + priv->n = best_n; + priv->pl = best_pl; + + target_freq = gk20a_pllg_calc_rate(priv) / MHZ; + + nv_debug(priv, "actual target freq %d MHz, M %d, N %d, PL %d(div%d)\n", + target_freq, priv->m, priv->n, priv->pl, pl_to_div[priv->pl]); + return 0; +} + +static int +gk20a_pllg_slide(struct gk20a_clk_priv *priv, u32 n) +{ + u32 val; + int ramp_timeout; + + /* get old coefficients */ + val = nv_rd32(priv, GPCPLL_COEFF); + /* do nothing if NDIV is the same */ + if (n == ((val >> GPCPLL_COEFF_N_SHIFT) & MASK(GPCPLL_COEFF_N_WIDTH))) + return 0; + + /* setup */ + nv_mask(priv, GPCPLL_CFG2, 0xff << GPCPLL_CFG2_PLL_STEPA_SHIFT, + 0x2b << GPCPLL_CFG2_PLL_STEPA_SHIFT); + nv_mask(priv, GPCPLL_CFG3, 0xff << GPCPLL_CFG3_PLL_STEPB_SHIFT, + 0xb << GPCPLL_CFG3_PLL_STEPB_SHIFT); + + /* pll slowdown mode */ + nv_mask(priv, GPCPLL_NDIV_SLOWDOWN, + BIT(GPCPLL_NDIV_SLOWDOWN_SLOWDOWN_USING_PLL_SHIFT), + BIT(GPCPLL_NDIV_SLOWDOWN_SLOWDOWN_USING_PLL_SHIFT)); + + /* new ndiv ready for ramp */ + val = nv_rd32(priv, GPCPLL_COEFF); + val &= ~(MASK(GPCPLL_COEFF_N_WIDTH) << GPCPLL_COEFF_N_SHIFT); + val |= (n & MASK(GPCPLL_COEFF_N_WIDTH)) << GPCPLL_COEFF_N_SHIFT; + udelay(1); + nv_wr32(priv, GPCPLL_COEFF, val); + + /* dynamic ramp to new ndiv */ + val = nv_rd32(priv, GPCPLL_NDIV_SLOWDOWN); + val |= 0x1 << GPCPLL_NDIV_SLOWDOWN_EN_DYNRAMP_SHIFT; + udelay(1); + nv_wr32(priv, GPCPLL_NDIV_SLOWDOWN, val); + + for (ramp_timeout = 500; ramp_timeout > 0; ramp_timeout--) { + udelay(1); + val = nv_rd32(priv, GPC_BCAST_NDIV_SLOWDOWN_DEBUG); + if (val & GPC_BCAST_NDIV_SLOWDOWN_DEBUG_PLL_DYNRAMP_DONE_SYNCED_MASK) + break; + } + + /* exit slowdown mode */ + nv_mask(priv, GPCPLL_NDIV_SLOWDOWN, + BIT(GPCPLL_NDIV_SLOWDOWN_SLOWDOWN_USING_PLL_SHIFT) | + BIT(GPCPLL_NDIV_SLOWDOWN_EN_DYNRAMP_SHIFT), 0); + nv_rd32(priv, GPCPLL_NDIV_SLOWDOWN); + + if (ramp_timeout <= 0) { + nv_error(priv, "gpcpll dynamic ramp timeout\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static void +_gk20a_pllg_enable(struct gk20a_clk_priv *priv) +{ + nv_mask(priv, GPCPLL_CFG, GPCPLL_CFG_ENABLE, GPCPLL_CFG_ENABLE); + nv_rd32(priv, GPCPLL_CFG); +} + +static void +_gk20a_pllg_disable(struct gk20a_clk_priv *priv) +{ + nv_mask(priv, GPCPLL_CFG, GPCPLL_CFG_ENABLE, 0); + nv_rd32(priv, GPCPLL_CFG); +} + +static int +_gk20a_pllg_program_mnp(struct gk20a_clk_priv *priv, bool allow_slide) +{ + u32 val, cfg; + u32 m_old, pl_old, n_lo; + + /* get old coefficients */ + val = nv_rd32(priv, GPCPLL_COEFF); + m_old = (val >> GPCPLL_COEFF_M_SHIFT) & MASK(GPCPLL_COEFF_M_WIDTH); + pl_old = (val >> GPCPLL_COEFF_P_SHIFT) & MASK(GPCPLL_COEFF_P_WIDTH); + + /* do NDIV slide if there is no change in M and PL */ + cfg = nv_rd32(priv, GPCPLL_CFG); + if (allow_slide && priv->m == m_old && priv->pl == pl_old && + (cfg & GPCPLL_CFG_ENABLE)) { + return gk20a_pllg_slide(priv, priv->n); + } + + /* slide down to NDIV_LO */ + n_lo = DIV_ROUND_UP(m_old * priv->params->min_vco, + priv->parent_rate / MHZ); + if (allow_slide && (cfg & GPCPLL_CFG_ENABLE)) { + int ret = gk20a_pllg_slide(priv, n_lo); + + if (ret) + return ret; + } + + /* split FO-to-bypass jump in halfs by setting out divider 1:2 */ + nv_mask(priv, GPC2CLK_OUT, GPC2CLK_OUT_VCODIV_MASK, + 0x2 << GPC2CLK_OUT_VCODIV_SHIFT); + + /* put PLL in bypass before programming it */ + val = nv_rd32(priv, SEL_VCO); + val &= ~(BIT(SEL_VCO_GPC2CLK_OUT_SHIFT)); + udelay(2); + nv_wr32(priv, SEL_VCO, val); + + /* get out from IDDQ */ + val = nv_rd32(priv, GPCPLL_CFG); + if (val & GPCPLL_CFG_IDDQ) { + val &= ~GPCPLL_CFG_IDDQ; + nv_wr32(priv, GPCPLL_CFG, val); + nv_rd32(priv, GPCPLL_CFG); + udelay(2); + } + + _gk20a_pllg_disable(priv); + + nv_debug(priv, "%s: m=%d n=%d pl=%d\n", __func__, priv->m, priv->n, + priv->pl); + + n_lo = DIV_ROUND_UP(priv->m * priv->params->min_vco, + priv->parent_rate / MHZ); + val = priv->m << GPCPLL_COEFF_M_SHIFT; + val |= (allow_slide ? n_lo : priv->n) << GPCPLL_COEFF_N_SHIFT; + val |= priv->pl << GPCPLL_COEFF_P_SHIFT; + nv_wr32(priv, GPCPLL_COEFF, val); + + _gk20a_pllg_enable(priv); + + val = nv_rd32(priv, GPCPLL_CFG); + if (val & GPCPLL_CFG_LOCK_DET_OFF) { + val &= ~GPCPLL_CFG_LOCK_DET_OFF; + nv_wr32(priv, GPCPLL_CFG, val); + } + + if (!nvkm_timer_wait_eq(priv, 300000, GPCPLL_CFG, GPCPLL_CFG_LOCK, + GPCPLL_CFG_LOCK)) { + nv_error(priv, "%s: timeout waiting for pllg lock\n", __func__); + return -ETIMEDOUT; + } + + /* switch to VCO mode */ + nv_mask(priv, SEL_VCO, 0, BIT(SEL_VCO_GPC2CLK_OUT_SHIFT)); + + /* restore out divider 1:1 */ + val = nv_rd32(priv, GPC2CLK_OUT); + val &= ~GPC2CLK_OUT_VCODIV_MASK; + udelay(2); + nv_wr32(priv, GPC2CLK_OUT, val); + + /* slide up to new NDIV */ + return allow_slide ? gk20a_pllg_slide(priv, priv->n) : 0; +} + +static int +gk20a_pllg_program_mnp(struct gk20a_clk_priv *priv) +{ + int err; + + err = _gk20a_pllg_program_mnp(priv, true); + if (err) + err = _gk20a_pllg_program_mnp(priv, false); + + return err; +} + +static void +gk20a_pllg_disable(struct gk20a_clk_priv *priv) +{ + u32 val; + + /* slide to VCO min */ + val = nv_rd32(priv, GPCPLL_CFG); + if (val & GPCPLL_CFG_ENABLE) { + u32 coeff, m, n_lo; + + coeff = nv_rd32(priv, GPCPLL_COEFF); + m = (coeff >> GPCPLL_COEFF_M_SHIFT) & MASK(GPCPLL_COEFF_M_WIDTH); + n_lo = DIV_ROUND_UP(m * priv->params->min_vco, + priv->parent_rate / MHZ); + gk20a_pllg_slide(priv, n_lo); + } + + /* put PLL in bypass before disabling it */ + nv_mask(priv, SEL_VCO, BIT(SEL_VCO_GPC2CLK_OUT_SHIFT), 0); + + _gk20a_pllg_disable(priv); +} + +#define GK20A_CLK_GPC_MDIV 1000 + +static struct nvkm_domain +gk20a_domains[] = { + { nv_clk_src_crystal, 0xff }, + { nv_clk_src_gpc, 0xff, 0, "core", GK20A_CLK_GPC_MDIV }, + { nv_clk_src_max } +}; + +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, + }, + }, +}; + +static int +gk20a_clk_read(struct nvkm_clk *clk, enum nv_clk_src src) +{ + struct gk20a_clk_priv *priv = (void *)clk; + + switch (src) { + case nv_clk_src_crystal: + return nv_device(clk)->crystal; + case nv_clk_src_gpc: + gk20a_pllg_read_mnp(priv); + return gk20a_pllg_calc_rate(priv) / GK20A_CLK_GPC_MDIV; + default: + nv_error(clk, "invalid clock source %d\n", src); + return -EINVAL; + } +} + +static int +gk20a_clk_calc(struct nvkm_clk *clk, struct nvkm_cstate *cstate) +{ + struct gk20a_clk_priv *priv = (void *)clk; + + return gk20a_pllg_calc_mnp(priv, cstate->domain[nv_clk_src_gpc] * + GK20A_CLK_GPC_MDIV); +} + +static int +gk20a_clk_prog(struct nvkm_clk *clk) +{ + struct gk20a_clk_priv *priv = (void *)clk; + + return gk20a_pllg_program_mnp(priv); +} + +static void +gk20a_clk_tidy(struct nvkm_clk *clk) +{ +} + +static int +gk20a_clk_fini(struct nvkm_object *object, bool suspend) +{ + struct gk20a_clk_priv *priv = (void *)object; + int ret; + + ret = nvkm_clk_fini(&priv->base, false); + + gk20a_pllg_disable(priv); + + return ret; +} + +static int +gk20a_clk_init(struct nvkm_object *object) +{ + struct gk20a_clk_priv *priv = (void *)object; + int ret; + + nv_mask(priv, GPC2CLK_OUT, GPC2CLK_OUT_INIT_MASK, GPC2CLK_OUT_INIT_VAL); + + ret = nvkm_clk_init(&priv->base); + if (ret) + return ret; + + ret = gk20a_clk_prog(&priv->base); + if (ret) { + nv_error(priv, "cannot initialize clock\n"); + return ret; + } + + return 0; +} + +static int +gk20a_clk_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gk20a_clk_priv *priv; + struct nouveau_platform_device *plat; + int ret; + int i; + + /* Finish initializing the pstates */ + for (i = 0; i < ARRAY_SIZE(gk20a_pstates); i++) { + INIT_LIST_HEAD(&gk20a_pstates[i].list); + gk20a_pstates[i].pstate = i + 1; + } + + ret = nvkm_clk_create(parent, engine, oclass, gk20a_domains, + gk20a_pstates, ARRAY_SIZE(gk20a_pstates), + true, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->params = &gk20a_pllg_params; + + plat = nv_device_to_platform(nv_device(parent)); + priv->parent_rate = clk_get_rate(plat->gpu->clk); + nv_info(priv, "parent clock rate: %d Mhz\n", priv->parent_rate / MHZ); + + priv->base.read = gk20a_clk_read; + priv->base.calc = gk20a_clk_calc; + priv->base.prog = gk20a_clk_prog; + priv->base.tidy = gk20a_clk_tidy; + return 0; +} + +struct nvkm_oclass +gk20a_clk_oclass = { + .handle = NV_SUBDEV(CLK, 0xea), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gk20a_clk_ctor, + .dtor = _nvkm_subdev_dtor, + .init = gk20a_clk_init, + .fini = gk20a_clk_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gt215.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gt215.c new file mode 100644 index 000000000..822d32a28 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gt215.c @@ -0,0 +1,533 @@ +/* + * 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 + */ +#include "gt215.h" +#include "pll.h" + +#include +#include +#include +#include +#include + +struct gt215_clk_priv { + struct nvkm_clk base; + struct gt215_clk_info eng[nv_clk_src_max]; +}; + +static u32 read_clk(struct gt215_clk_priv *, int, bool); +static u32 read_pll(struct gt215_clk_priv *, int, u32); + +static u32 +read_vco(struct gt215_clk_priv *priv, int clk) +{ + u32 sctl = nv_rd32(priv, 0x4120 + (clk * 4)); + + switch (sctl & 0x00000030) { + case 0x00000000: + return nv_device(priv)->crystal; + case 0x00000020: + return read_pll(priv, 0x41, 0x00e820); + case 0x00000030: + return read_pll(priv, 0x42, 0x00e8a0); + default: + return 0; + } +} + +static u32 +read_clk(struct gt215_clk_priv *priv, int clk, bool ignore_en) +{ + u32 sctl, sdiv, sclk; + + /* refclk for the 0xe8xx plls is a fixed frequency */ + if (clk >= 0x40) { + if (nv_device(priv)->chipset == 0xaf) { + /* no joke.. seriously.. sigh.. */ + return nv_rd32(priv, 0x00471c) * 1000; + } + + return nv_device(priv)->crystal; + } + + sctl = nv_rd32(priv, 0x4120 + (clk * 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 nv_device(priv)->crystal; + return 0; + case 0x00002000: + if (sctl & 0x00000040) + return 108000; + return 100000; + case 0x00003000: + /* vco_enable */ + if (!(sctl & 0x00000001)) + return 0; + + sclk = read_vco(priv, clk); + sdiv = ((sctl & 0x003f0000) >> 16) + 2; + return (sclk * 2) / sdiv; + default: + return 0; + } +} + +static u32 +read_pll(struct gt215_clk_priv *priv, int clk, u32 pll) +{ + u32 ctrl = nv_rd32(priv, pll + 0); + u32 sclk = 0, P = 1, N = 1, M = 1; + + if (!(ctrl & 0x00000008)) { + if (ctrl & 0x00000001) { + u32 coef = nv_rd32(priv, 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(priv, 0x00 + clk, false); + } + } else { + sclk = read_clk(priv, 0x10 + clk, false); + } + + if (M * P) + return sclk * N / (M * P); + + return 0; +} + +static int +gt215_clk_read(struct nvkm_clk *clk, enum nv_clk_src src) +{ + struct gt215_clk_priv *priv = (void *)clk; + u32 hsrc; + + switch (src) { + case nv_clk_src_crystal: + return nv_device(priv)->crystal; + case nv_clk_src_core: + case nv_clk_src_core_intm: + return read_pll(priv, 0x00, 0x4200); + case nv_clk_src_shader: + return read_pll(priv, 0x01, 0x4220); + case nv_clk_src_mem: + return read_pll(priv, 0x02, 0x4000); + case nv_clk_src_disp: + return read_clk(priv, 0x20, false); + case nv_clk_src_vdec: + return read_clk(priv, 0x21, false); + case nv_clk_src_daemon: + return read_clk(priv, 0x25, false); + case nv_clk_src_host: + hsrc = (nv_rd32(priv, 0xc040) & 0x30000000) >> 28; + switch (hsrc) { + case 0: + return read_clk(priv, 0x1d, false); + case 2: + case 3: + return 277000; + default: + nv_error(clk, "unknown HOST clock source %d\n", hsrc); + return -EINVAL; + } + default: + nv_error(clk, "invalid clock source %d\n", src); + return -EINVAL; + } + + return 0; +} + +int +gt215_clk_info(struct nvkm_clk *clock, int clk, u32 khz, + struct gt215_clk_info *info) +{ + struct gt215_clk_priv *priv = (void *)clock; + u32 oclk, sclk, sdiv, 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(priv, clk); + 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 *clock, int clk, u32 pll, u32 khz, + struct gt215_clk_info *info) +{ + struct nvkm_bios *bios = nvkm_bios(clock); + struct gt215_clk_priv *priv = (void *)clock; + 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(clock, clk, khz, info); + diff = khz - ret; + if (!pll || (diff >= -2000 && diff < 3000)) { + goto out; + } + + /* Try with PLL */ + ret = nvbios_pll_parse(bios, pll, &limits); + if (ret) + return ret; + + ret = gt215_clk_info(clock, clk - 0x10, limits.refclk, info); + if (ret != limits.refclk) + return -EINVAL; + + ret = gt215_pll_calc(nv_subdev(priv), &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_priv *priv, struct nvkm_cstate *cstate, + int clk, u32 pll, int idx) +{ + int ret = gt215_pll_info(&priv->base, clk, pll, cstate->domain[idx], + &priv->eng[idx]); + if (ret >= 0) + return 0; + return ret; +} + +static int +calc_host(struct gt215_clk_priv *priv, struct nvkm_cstate *cstate) +{ + int ret = 0; + u32 kHz = cstate->domain[nv_clk_src_host]; + struct gt215_clk_info *info = &priv->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(&priv->base, 0x1d, kHz, info); + if (ret >= 0) + return 0; + + return ret; +} + +int +gt215_clk_pre(struct nvkm_clk *clk, unsigned long *flags) +{ + struct nvkm_fifo *pfifo = nvkm_fifo(clk); + + /* halt and idle execution engines */ + nv_mask(clk, 0x020060, 0x00070000, 0x00000000); + nv_mask(clk, 0x002504, 0x00000001, 0x00000001); + /* Wait until the interrupt handler is finished */ + if (!nv_wait(clk, 0x000100, 0xffffffff, 0x00000000)) + return -EBUSY; + + if (pfifo) + pfifo->pause(pfifo, flags); + + if (!nv_wait(clk, 0x002504, 0x00000010, 0x00000010)) + return -EIO; + if (!nv_wait(clk, 0x00251c, 0x0000003f, 0x0000003f)) + return -EIO; + + return 0; +} + +void +gt215_clk_post(struct nvkm_clk *clk, unsigned long *flags) +{ + struct nvkm_fifo *pfifo = nvkm_fifo(clk); + + if (pfifo && flags) + pfifo->start(pfifo, flags); + + nv_mask(clk, 0x002504, 0x00000001, 0x00000000); + nv_mask(clk, 0x020060, 0x00070000, 0x00040000); +} + +static void +disable_clk_src(struct gt215_clk_priv *priv, u32 src) +{ + nv_mask(priv, src, 0x00000100, 0x00000000); + nv_mask(priv, src, 0x00000001, 0x00000000); +} + +static void +prog_pll(struct gt215_clk_priv *priv, int clk, u32 pll, int idx) +{ + struct gt215_clk_info *info = &priv->eng[idx]; + const u32 src0 = 0x004120 + (clk * 4); + const u32 src1 = 0x004160 + (clk * 4); + const u32 ctrl = pll + 0; + const u32 coef = pll + 4; + u32 bypass; + + if (info->pll) { + /* Always start from a non-PLL clock */ + bypass = nv_rd32(priv, ctrl) & 0x00000008; + if (!bypass) { + nv_mask(priv, src1, 0x00000101, 0x00000101); + nv_mask(priv, ctrl, 0x00000008, 0x00000008); + udelay(20); + } + + nv_mask(priv, src0, 0x003f3141, 0x00000101 | info->clk); + nv_wr32(priv, coef, info->pll); + nv_mask(priv, ctrl, 0x00000015, 0x00000015); + nv_mask(priv, ctrl, 0x00000010, 0x00000000); + if (!nv_wait(priv, ctrl, 0x00020000, 0x00020000)) { + nv_mask(priv, ctrl, 0x00000010, 0x00000010); + nv_mask(priv, src0, 0x00000101, 0x00000000); + return; + } + nv_mask(priv, ctrl, 0x00000010, 0x00000010); + nv_mask(priv, ctrl, 0x00000008, 0x00000000); + disable_clk_src(priv, src1); + } else { + nv_mask(priv, src1, 0x003f3141, 0x00000101 | info->clk); + nv_mask(priv, ctrl, 0x00000018, 0x00000018); + udelay(20); + nv_mask(priv, ctrl, 0x00000001, 0x00000000); + disable_clk_src(priv, src0); + } +} + +static void +prog_clk(struct gt215_clk_priv *priv, int clk, int idx) +{ + struct gt215_clk_info *info = &priv->eng[idx]; + nv_mask(priv, 0x004120 + (clk * 4), 0x003f3141, 0x00000101 | info->clk); +} + +static void +prog_host(struct gt215_clk_priv *priv) +{ + struct gt215_clk_info *info = &priv->eng[nv_clk_src_host]; + u32 hsrc = (nv_rd32(priv, 0xc040)); + + switch (info->host_out) { + case NVA3_HOST_277: + if ((hsrc & 0x30000000) == 0) { + nv_wr32(priv, 0xc040, hsrc | 0x20000000); + disable_clk_src(priv, 0x4194); + } + break; + case NVA3_HOST_CLK: + prog_clk(priv, 0x1d, nv_clk_src_host); + if ((hsrc & 0x30000000) >= 0x20000000) { + nv_wr32(priv, 0xc040, hsrc & ~0x30000000); + } + break; + default: + break; + } + + /* This seems to be a clock gating factor on idle, always set to 64 */ + nv_wr32(priv, 0xc044, 0x3e); +} + +static void +prog_core(struct gt215_clk_priv *priv, int idx) +{ + struct gt215_clk_info *info = &priv->eng[idx]; + u32 fb_delay = nv_rd32(priv, 0x10002c); + + if (fb_delay < info->fb_delay) + nv_wr32(priv, 0x10002c, info->fb_delay); + + prog_pll(priv, 0x00, 0x004200, idx); + + if (fb_delay > info->fb_delay) + nv_wr32(priv, 0x10002c, info->fb_delay); +} + +static int +gt215_clk_calc(struct nvkm_clk *clk, struct nvkm_cstate *cstate) +{ + struct gt215_clk_priv *priv = (void *)clk; + struct gt215_clk_info *core = &priv->eng[nv_clk_src_core]; + int ret; + + if ((ret = calc_clk(priv, cstate, 0x10, 0x4200, nv_clk_src_core)) || + (ret = calc_clk(priv, cstate, 0x11, 0x4220, nv_clk_src_shader)) || + (ret = calc_clk(priv, cstate, 0x20, 0x0000, nv_clk_src_disp)) || + (ret = calc_clk(priv, cstate, 0x21, 0x0000, nv_clk_src_vdec)) || + (ret = calc_host(priv, 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, 0x10, + cstate->domain[nv_clk_src_core_intm], + &priv->eng[nv_clk_src_core_intm]); + if (ret < 0) + return ret; + } + + return 0; +} + +static int +gt215_clk_prog(struct nvkm_clk *clk) +{ + struct gt215_clk_priv *priv = (void *)clk; + struct gt215_clk_info *core = &priv->eng[nv_clk_src_core]; + int ret = 0; + unsigned long flags; + unsigned long *f = &flags; + + ret = gt215_clk_pre(clk, f); + if (ret) + goto out; + + if (core->pll) + prog_core(priv, nv_clk_src_core_intm); + + prog_core(priv, nv_clk_src_core); + prog_pll(priv, 0x01, 0x004220, nv_clk_src_shader); + prog_clk(priv, 0x20, nv_clk_src_disp); + prog_clk(priv, 0x21, nv_clk_src_vdec); + prog_host(priv); + +out: + if (ret == -EBUSY) + f = NULL; + + gt215_clk_post(clk, f); + return ret; +} + +static void +gt215_clk_tidy(struct nvkm_clk *clk) +{ +} + +static struct nvkm_domain +gt215_domain[] = { + { 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 } +}; + +static int +gt215_clk_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gt215_clk_priv *priv; + int ret; + + ret = nvkm_clk_create(parent, engine, oclass, gt215_domain, + NULL, 0, true, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.read = gt215_clk_read; + priv->base.calc = gt215_clk_calc; + priv->base.prog = gt215_clk_prog; + priv->base.tidy = gt215_clk_tidy; + return 0; +} + +struct nvkm_oclass +gt215_clk_oclass = { + .handle = NV_SUBDEV(CLK, 0xa3), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gt215_clk_ctor, + .dtor = _nvkm_clk_dtor, + .init = _nvkm_clk_init, + .fini = _nvkm_clk_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gt215.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gt215.h new file mode 100644 index 000000000..b447d9cd4 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gt215.h @@ -0,0 +1,18 @@ +#ifndef __NVKM_CLK_NVA3_H__ +#define __NVKM_CLK_NVA3_H__ +#include + +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 *clk, unsigned long *flags); +void gt215_clk_post(struct nvkm_clk *clk, unsigned long *flags); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/mcp77.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/mcp77.c new file mode 100644 index 000000000..c54417b14 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/mcp77.c @@ -0,0 +1,429 @@ +/* + * 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 "gt215.h" +#include "pll.h" + +#include +#include +#include +#include + +struct mcp77_clk_priv { + 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 nvkm_clk *clk) +{ + return nv_rd32(clk, 0x004600); +} + +static u32 +read_pll(struct nvkm_clk *clk, u32 base) +{ + u32 ctrl = nv_rd32(clk, base + 0); + u32 coef = nv_rd32(clk, base + 4); + u32 ref = clk->read(clk, nv_clk_src_href); + u32 post_div = 0; + u32 clock = 0; + int N1, M1; + + switch (base){ + case 0x4020: + post_div = 1 << ((nv_rd32(clk, 0x4070) & 0x000f0000) >> 16); + break; + case 0x4028: + post_div = (nv_rd32(clk, 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 *clk, enum nv_clk_src src) +{ + struct mcp77_clk_priv *priv = (void *)clk; + u32 mast = nv_rd32(clk, 0x00c054); + u32 P = 0; + + switch (src) { + case nv_clk_src_crystal: + return nv_device(priv)->crystal; + case nv_clk_src_href: + return 100000; /* PCIE reference clock */ + case nv_clk_src_hclkm4: + return clk->read(clk, nv_clk_src_href) * 4; + case nv_clk_src_hclkm2d3: + return clk->read(clk, nv_clk_src_href) * 2 / 3; + case nv_clk_src_host: + switch (mast & 0x000c0000) { + case 0x00000000: return clk->read(clk, nv_clk_src_hclkm2d3); + case 0x00040000: break; + case 0x00080000: return clk->read(clk, nv_clk_src_hclkm4); + case 0x000c0000: return clk->read(clk, nv_clk_src_cclk); + } + break; + case nv_clk_src_core: + P = (nv_rd32(clk, 0x004028) & 0x00070000) >> 16; + + switch (mast & 0x00000003) { + case 0x00000000: return clk->read(clk, nv_clk_src_crystal) >> P; + case 0x00000001: return 0; + case 0x00000002: return clk->read(clk, nv_clk_src_hclkm4) >> P; + case 0x00000003: return read_pll(clk, 0x004028) >> P; + } + break; + case nv_clk_src_cclk: + if ((mast & 0x03000000) != 0x03000000) + return clk->read(clk, nv_clk_src_core); + + if ((mast & 0x00000200) == 0x00000000) + return clk->read(clk, nv_clk_src_core); + + switch (mast & 0x00000c00) { + case 0x00000000: return clk->read(clk, nv_clk_src_href); + case 0x00000400: return clk->read(clk, nv_clk_src_hclkm4); + case 0x00000800: return clk->read(clk, nv_clk_src_hclkm2d3); + default: return 0; + } + case nv_clk_src_shader: + P = (nv_rd32(clk, 0x004020) & 0x00070000) >> 16; + switch (mast & 0x00000030) { + case 0x00000000: + if (mast & 0x00000040) + return clk->read(clk, nv_clk_src_href) >> P; + return clk->read(clk, 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; + break; + case nv_clk_src_vdec: + P = (read_div(clk) & 0x00000700) >> 8; + + switch (mast & 0x00400000) { + case 0x00400000: + return clk->read(clk, nv_clk_src_core) >> P; + break; + default: + return 500000 >> P; + break; + } + break; + default: + break; + } + + nv_debug(priv, "unknown clock source %d 0x%08x\n", src, mast); + return 0; +} + +static u32 +calc_pll(struct mcp77_clk_priv *priv, u32 reg, + u32 clock, int *N, int *M, int *P) +{ + struct nvkm_bios *bios = nvkm_bios(priv); + struct nvbios_pll pll; + struct nvkm_clk *clk = &priv->base; + int ret; + + ret = nvbios_pll_parse(bios, reg, &pll); + if (ret) + return 0; + + pll.vco2.max_freq = 0; + pll.refclk = clk->read(clk, nv_clk_src_href); + if (!pll.refclk) + return 0; + + return nv04_pll_calc(nv_subdev(priv), &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 *clk, struct nvkm_cstate *cstate) +{ + struct mcp77_clk_priv *priv = (void *)clk; + 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]; + 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 < clk->read(clk, nv_clk_src_hclkm4)) + out = calc_P(clk->read(clk, nv_clk_src_hclkm4), core, &divs); + + /* Calculate clock * 2, so shader clock can use it too */ + clock = calc_pll(priv, 0x4028, (core << 1), &N, &M, &P1); + + if (abs(core - out) <= abs(core - (clock >> 1))) { + priv->csrc = nv_clk_src_hclkm4; + priv->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; + } + + priv->csrc = nv_clk_src_core; + priv->ccoef = (N << 8) | M; + + priv->cctrl = (P2 + 1) << 16; + priv->cpost = (1 << P1) << 16; + } + + /* sclk: nvpll + divisor, href or spll */ + out = 0; + if (shader == clk->read(clk, nv_clk_src_href)) { + priv->ssrc = nv_clk_src_href; + } else { + clock = calc_pll(priv, 0x4020, shader, &N, &M, &P1); + if (priv->csrc == nv_clk_src_core) + out = calc_P((core << 1), shader, &divs); + + if (abs(shader - out) <= + abs(shader - clock) && + (divs + P2) <= 7) { + priv->ssrc = nv_clk_src_core; + priv->sctrl = (divs + P2) << 16; + } else { + priv->ssrc = nv_clk_src_shader; + priv->scoef = (N << 8) | M; + priv->sctrl = P1 << 16; + } + } + + /* vclk */ + out = calc_P(core, vdec, &divs); + clock = calc_P(500000, vdec, &P1); + if(abs(vdec - out) <= abs(vdec - clock)) { + priv->vsrc = nv_clk_src_cclk; + priv->vdiv = divs << 16; + } else { + priv->vsrc = nv_clk_src_vdec; + priv->vdiv = P1 << 16; + } + + /* Print strategy! */ + nv_debug(priv, "nvpll: %08x %08x %08x\n", + priv->ccoef, priv->cpost, priv->cctrl); + nv_debug(priv, " spll: %08x %08x %08x\n", + priv->scoef, priv->spost, priv->sctrl); + nv_debug(priv, " vdiv: %08x\n", priv->vdiv); + if (priv->csrc == nv_clk_src_hclkm4) + nv_debug(priv, "core: hrefm4\n"); + else + nv_debug(priv, "core: nvpll\n"); + + if (priv->ssrc == nv_clk_src_hclkm4) + nv_debug(priv, "shader: hrefm4\n"); + else if (priv->ssrc == nv_clk_src_core) + nv_debug(priv, "shader: nvpll\n"); + else + nv_debug(priv, "shader: spll\n"); + + if (priv->vsrc == nv_clk_src_hclkm4) + nv_debug(priv, "vdec: 500MHz\n"); + else + nv_debug(priv, "vdec: core\n"); + + return 0; +} + +static int +mcp77_clk_prog(struct nvkm_clk *clk) +{ + struct mcp77_clk_priv *priv = (void *)clk; + u32 pllmask = 0, mast; + unsigned long flags; + unsigned long *f = &flags; + int ret = 0; + + ret = gt215_clk_pre(clk, f); + if (ret) + goto out; + + /* First switch to safe clocks: href */ + mast = nv_mask(clk, 0xc054, 0x03400e70, 0x03400640); + mast &= ~0x00400e73; + mast |= 0x03000000; + + switch (priv->csrc) { + case nv_clk_src_hclkm4: + nv_mask(clk, 0x4028, 0x00070000, priv->cctrl); + mast |= 0x00000002; + break; + case nv_clk_src_core: + nv_wr32(clk, 0x402c, priv->ccoef); + nv_wr32(clk, 0x4028, 0x80000000 | priv->cctrl); + nv_wr32(clk, 0x4040, priv->cpost); + pllmask |= (0x3 << 8); + mast |= 0x00000003; + break; + default: + nv_warn(priv,"Reclocking failed: unknown core clock\n"); + goto resume; + } + + switch (priv->ssrc) { + case nv_clk_src_href: + nv_mask(clk, 0x4020, 0x00070000, 0x00000000); + /* mast |= 0x00000000; */ + break; + case nv_clk_src_core: + nv_mask(clk, 0x4020, 0x00070000, priv->sctrl); + mast |= 0x00000020; + break; + case nv_clk_src_shader: + nv_wr32(clk, 0x4024, priv->scoef); + nv_wr32(clk, 0x4020, 0x80000000 | priv->sctrl); + nv_wr32(clk, 0x4070, priv->spost); + pllmask |= (0x3 << 12); + mast |= 0x00000030; + break; + default: + nv_warn(priv,"Reclocking failed: unknown sclk clock\n"); + goto resume; + } + + if (!nv_wait(clk, 0x004080, pllmask, pllmask)) { + nv_warn(priv,"Reclocking failed: unstable PLLs\n"); + goto resume; + } + + switch (priv->vsrc) { + case nv_clk_src_cclk: + mast |= 0x00400000; + default: + nv_wr32(clk, 0x4600, priv->vdiv); + } + + nv_wr32(clk, 0xc054, mast); + +resume: + /* Disable some PLLs and dividers when unused */ + if (priv->csrc != nv_clk_src_core) { + nv_wr32(clk, 0x4040, 0x00000000); + nv_mask(clk, 0x4028, 0x80000000, 0x00000000); + } + + if (priv->ssrc != nv_clk_src_shader) { + nv_wr32(clk, 0x4070, 0x00000000); + nv_mask(clk, 0x4020, 0x80000000, 0x00000000); + } + +out: + if (ret == -EBUSY) + f = NULL; + + gt215_clk_post(clk, f); + return ret; +} + +static void +mcp77_clk_tidy(struct nvkm_clk *clk) +{ +} + +static struct nvkm_domain +mcp77_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 } +}; + +static int +mcp77_clk_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct mcp77_clk_priv *priv; + int ret; + + ret = nvkm_clk_create(parent, engine, oclass, mcp77_domains, + NULL, 0, true, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.read = mcp77_clk_read; + priv->base.calc = mcp77_clk_calc; + priv->base.prog = mcp77_clk_prog; + priv->base.tidy = mcp77_clk_tidy; + return 0; +} + +struct nvkm_oclass * +mcp77_clk_oclass = &(struct nvkm_oclass) { + .handle = NV_SUBDEV(CLK, 0xaa), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = mcp77_clk_ctor, + .dtor = _nvkm_clk_dtor, + .init = _nvkm_clk_init, + .fini = _nvkm_clk_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv04.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv04.c new file mode 100644 index 000000000..63dbbb575 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv04.c @@ -0,0 +1,103 @@ +/* + * 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 "pll.h" + +#include +#include +#include + +struct nv04_clk_priv { + struct nvkm_clk base; +}; + +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(nv_subdev(clock), 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_devinit *devinit = nvkm_devinit(clk); + int cv = nvkm_bios(clk)->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 struct nvkm_domain +nv04_domain[] = { + { nv_clk_src_max } +}; + +static int +nv04_clk_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv04_clk_priv *priv; + int ret; + + ret = nvkm_clk_create(parent, engine, oclass, nv04_domain, + NULL, 0, false, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.pll_calc = nv04_clk_pll_calc; + priv->base.pll_prog = nv04_clk_pll_prog; + return 0; +} + +struct nvkm_oclass +nv04_clk_oclass = { + .handle = NV_SUBDEV(CLK, 0x04), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_clk_ctor, + .dtor = _nvkm_clk_dtor, + .init = _nvkm_clk_init, + .fini = _nvkm_clk_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv40.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv40.c new file mode 100644 index 000000000..ed838130c --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv40.c @@ -0,0 +1,241 @@ +/* + * 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 "pll.h" + +#include +#include +#include + +struct nv40_clk_priv { + struct nvkm_clk base; + u32 ctrl; + u32 npll_ctrl; + u32 npll_coef; + u32 spll; +}; + +static struct nvkm_domain +nv40_domain[] = { + { 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 } +}; + +static u32 +read_pll_1(struct nv40_clk_priv *priv, u32 reg) +{ + u32 ctrl = nv_rd32(priv, reg + 0x00); + int P = (ctrl & 0x00070000) >> 16; + int N = (ctrl & 0x0000ff00) >> 8; + int M = (ctrl & 0x000000ff) >> 0; + u32 ref = 27000, clk = 0; + + if (ctrl & 0x80000000) + clk = ref * N / M; + + return clk >> P; +} + +static u32 +read_pll_2(struct nv40_clk_priv *priv, u32 reg) +{ + u32 ctrl = nv_rd32(priv, reg + 0x00); + u32 coef = nv_rd32(priv, 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, clk = 0; + + if ((ctrl & 0x80000000) && M1) { + clk = ref * N1 / M1; + if ((ctrl & 0x40000100) == 0x40000000) { + if (M2) + clk = clk * N2 / M2; + else + clk = 0; + } + } + + return clk >> P; +} + +static u32 +read_clk(struct nv40_clk_priv *priv, u32 src) +{ + switch (src) { + case 3: + return read_pll_2(priv, 0x004000); + case 2: + return read_pll_1(priv, 0x004008); + default: + break; + } + + return 0; +} + +static int +nv40_clk_read(struct nvkm_clk *clk, enum nv_clk_src src) +{ + struct nv40_clk_priv *priv = (void *)clk; + u32 mast = nv_rd32(priv, 0x00c040); + + switch (src) { + case nv_clk_src_crystal: + return nv_device(priv)->crystal; + case nv_clk_src_href: + return 100000; /*XXX: PCIE/AGP differ*/ + case nv_clk_src_core: + return read_clk(priv, (mast & 0x00000003) >> 0); + case nv_clk_src_shader: + return read_clk(priv, (mast & 0x00000030) >> 4); + case nv_clk_src_mem: + return read_pll_2(priv, 0x4020); + default: + break; + } + + nv_debug(priv, "unknown clock source %d 0x%08x\n", src, mast); + return -EINVAL; +} + +static int +nv40_clk_calc_pll(struct nv40_clk_priv *priv, u32 reg, u32 clk, + int *N1, int *M1, int *N2, int *M2, int *log2P) +{ + struct nvkm_bios *bios = nvkm_bios(priv); + struct nvbios_pll pll; + int ret; + + ret = nvbios_pll_parse(bios, reg, &pll); + if (ret) + return ret; + + if (clk < pll.vco1.max_freq) + pll.vco2.max_freq = 0; + + ret = nv04_pll_calc(nv_subdev(priv), &pll, clk, N1, M1, N2, M2, log2P); + if (ret == 0) + return -ERANGE; + + return ret; +} + +static int +nv40_clk_calc(struct nvkm_clk *clk, struct nvkm_cstate *cstate) +{ + struct nv40_clk_priv *priv = (void *)clk; + 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(priv, 0x004000, gclk, + &N1, &M1, &N2, &M2, &log2P); + if (ret < 0) + return ret; + + if (N2 == M2) { + priv->npll_ctrl = 0x80000100 | (log2P << 16); + priv->npll_coef = (N1 << 8) | M1; + } else { + priv->npll_ctrl = 0xc0000000 | (log2P << 16); + priv->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(priv, 0x004008, sclk, + &N1, &M1, NULL, NULL, &log2P); + if (ret < 0) + return ret; + + priv->spll = 0xc0000000 | (log2P << 16) | (N1 << 8) | M1; + priv->ctrl = 0x00000223; + } else { + priv->spll = 0x00000000; + priv->ctrl = 0x00000333; + } + + return 0; +} + +static int +nv40_clk_prog(struct nvkm_clk *clk) +{ + struct nv40_clk_priv *priv = (void *)clk; + nv_mask(priv, 0x00c040, 0x00000333, 0x00000000); + nv_wr32(priv, 0x004004, priv->npll_coef); + nv_mask(priv, 0x004000, 0xc0070100, priv->npll_ctrl); + nv_mask(priv, 0x004008, 0xc007ffff, priv->spll); + mdelay(5); + nv_mask(priv, 0x00c040, 0x00000333, priv->ctrl); + return 0; +} + +static void +nv40_clk_tidy(struct nvkm_clk *clk) +{ +} + +static int +nv40_clk_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv40_clk_priv *priv; + int ret; + + ret = nvkm_clk_create(parent, engine, oclass, nv40_domain, + NULL, 0, true, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.pll_calc = nv04_clk_pll_calc; + priv->base.pll_prog = nv04_clk_pll_prog; + priv->base.read = nv40_clk_read; + priv->base.calc = nv40_clk_calc; + priv->base.prog = nv40_clk_prog; + priv->base.tidy = nv40_clk_tidy; + return 0; +} + +struct nvkm_oclass +nv40_clk_oclass = { + .handle = NV_SUBDEV(CLK, 0x40), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv40_clk_ctor, + .dtor = _nvkm_clk_dtor, + .init = _nvkm_clk_init, + .fini = _nvkm_clk_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv50.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv50.c new file mode 100644 index 000000000..9b4ffd634 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv50.c @@ -0,0 +1,561 @@ +/* + * 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 +#include + +static u32 +read_div(struct nv50_clk_priv *priv) +{ + switch (nv_device(priv)->chipset) { + case 0x50: /* it exists, but only has bit 31, not the dividers.. */ + case 0x84: + case 0x86: + case 0x98: + case 0xa0: + return nv_rd32(priv, 0x004700); + case 0x92: + case 0x94: + case 0x96: + return nv_rd32(priv, 0x004800); + default: + return 0x00000000; + } +} + +static u32 +read_pll_src(struct nv50_clk_priv *priv, u32 base) +{ + struct nvkm_clk *clk = &priv->base; + u32 coef, ref = clk->read(clk, nv_clk_src_crystal); + u32 rsel = nv_rd32(priv, 0x00e18c); + int P, N, M, id; + + switch (nv_device(priv)->chipset) { + case 0x50: + case 0xa0: + switch (base) { + case 0x4020: + case 0x4028: id = !!(rsel & 0x00000004); break; + case 0x4008: id = !!(rsel & 0x00000008); break; + case 0x4030: id = 0; break; + default: + nv_error(priv, "ref: bad pll 0x%06x\n", base); + return 0; + } + + coef = nv_rd32(priv, 0x00e81c + (id * 0x0c)); + ref *= (coef & 0x01000000) ? 2 : 4; + P = (coef & 0x00070000) >> 16; + N = ((coef & 0x0000ff00) >> 8) + 1; + M = ((coef & 0x000000ff) >> 0) + 1; + break; + case 0x84: + case 0x86: + case 0x92: + coef = nv_rd32(priv, 0x00e81c); + P = (coef & 0x00070000) >> 16; + N = (coef & 0x0000ff00) >> 8; + M = (coef & 0x000000ff) >> 0; + break; + case 0x94: + case 0x96: + case 0x98: + rsel = nv_rd32(priv, 0x00c050); + switch (base) { + case 0x4020: rsel = (rsel & 0x00000003) >> 0; break; + case 0x4008: rsel = (rsel & 0x0000000c) >> 2; break; + case 0x4028: rsel = (rsel & 0x00001800) >> 11; break; + case 0x4030: rsel = 3; break; + default: + nv_error(priv, "ref: bad pll 0x%06x\n", base); + return 0; + } + + switch (rsel) { + case 0: id = 1; break; + case 1: return clk->read(clk, nv_clk_src_crystal); + case 2: return clk->read(clk, nv_clk_src_href); + case 3: id = 0; break; + } + + coef = nv_rd32(priv, 0x00e81c + (id * 0x28)); + P = (nv_rd32(priv, 0x00e824 + (id * 0x28)) >> 16) & 7; + P += (coef & 0x00070000) >> 16; + N = (coef & 0x0000ff00) >> 8; + M = (coef & 0x000000ff) >> 0; + break; + default: + BUG_ON(1); + } + + if (M) + return (ref * N / M) >> P; + + return 0; +} + +static u32 +read_pll_ref(struct nv50_clk_priv *priv, u32 base) +{ + struct nvkm_clk *clk = &priv->base; + u32 src, mast = nv_rd32(priv, 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 clk->read(clk, nv_clk_src_crystal); + default: + nv_error(priv, "bad pll 0x%06x\n", base); + return 0; + } + + if (src) + return clk->read(clk, nv_clk_src_href); + + return read_pll_src(priv, base); +} + +static u32 +read_pll(struct nv50_clk_priv *priv, u32 base) +{ + struct nvkm_clk *clk = &priv->base; + u32 mast = nv_rd32(priv, 0x00c040); + u32 ctrl = nv_rd32(priv, base + 0); + u32 coef = nv_rd32(priv, base + 4); + u32 ref = read_pll_ref(priv, base); + u32 freq = 0; + int N1, N2, M1, M2; + + if (base == 0x004028 && (mast & 0x00100000)) { + /* wtf, appears to only disable post-divider on gt200 */ + if (nv_device(priv)->chipset != 0xa0) + return clk->read(clk, 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; +} + +static int +nv50_clk_read(struct nvkm_clk *clk, enum nv_clk_src src) +{ + struct nv50_clk_priv *priv = (void *)clk; + u32 mast = nv_rd32(priv, 0x00c040); + u32 P = 0; + + switch (src) { + case nv_clk_src_crystal: + return nv_device(priv)->crystal; + case nv_clk_src_href: + return 100000; /* PCIE reference clock */ + case nv_clk_src_hclk: + return div_u64((u64)clk->read(clk, nv_clk_src_href) * 27778, 10000); + case nv_clk_src_hclkm3: + return clk->read(clk, nv_clk_src_hclk) * 3; + case nv_clk_src_hclkm3d2: + return clk->read(clk, nv_clk_src_hclk) * 3 / 2; + case nv_clk_src_host: + switch (mast & 0x30000000) { + case 0x00000000: return clk->read(clk, nv_clk_src_href); + case 0x10000000: break; + case 0x20000000: /* !0x50 */ + case 0x30000000: return clk->read(clk, nv_clk_src_hclk); + } + break; + case nv_clk_src_core: + if (!(mast & 0x00100000)) + P = (nv_rd32(priv, 0x004028) & 0x00070000) >> 16; + switch (mast & 0x00000003) { + case 0x00000000: return clk->read(clk, nv_clk_src_crystal) >> P; + case 0x00000001: return clk->read(clk, nv_clk_src_dom6); + case 0x00000002: return read_pll(priv, 0x004020) >> P; + case 0x00000003: return read_pll(priv, 0x004028) >> P; + } + break; + case nv_clk_src_shader: + P = (nv_rd32(priv, 0x004020) & 0x00070000) >> 16; + switch (mast & 0x00000030) { + case 0x00000000: + if (mast & 0x00000080) + return clk->read(clk, nv_clk_src_host) >> P; + return clk->read(clk, nv_clk_src_crystal) >> P; + case 0x00000010: break; + case 0x00000020: return read_pll(priv, 0x004028) >> P; + case 0x00000030: return read_pll(priv, 0x004020) >> P; + } + break; + case nv_clk_src_mem: + P = (nv_rd32(priv, 0x004008) & 0x00070000) >> 16; + if (nv_rd32(priv, 0x004008) & 0x00000200) { + switch (mast & 0x0000c000) { + case 0x00000000: + return clk->read(clk, nv_clk_src_crystal) >> P; + case 0x00008000: + case 0x0000c000: + return clk->read(clk, nv_clk_src_href) >> P; + } + } else { + return read_pll(priv, 0x004008) >> P; + } + break; + case nv_clk_src_vdec: + P = (read_div(priv) & 0x00000700) >> 8; + switch (nv_device(priv)->chipset) { + case 0x84: + case 0x86: + case 0x92: + case 0x94: + case 0x96: + case 0xa0: + switch (mast & 0x00000c00) { + case 0x00000000: + if (nv_device(priv)->chipset == 0xa0) /* wtf?? */ + return clk->read(clk, nv_clk_src_core) >> P; + return clk->read(clk, nv_clk_src_crystal) >> P; + case 0x00000400: + return 0; + case 0x00000800: + if (mast & 0x01000000) + return read_pll(priv, 0x004028) >> P; + return read_pll(priv, 0x004030) >> P; + case 0x00000c00: + return clk->read(clk, nv_clk_src_core) >> P; + } + break; + case 0x98: + switch (mast & 0x00000c00) { + case 0x00000000: + return clk->read(clk, nv_clk_src_core) >> P; + case 0x00000400: + return 0; + case 0x00000800: + return clk->read(clk, nv_clk_src_hclkm3d2) >> P; + case 0x00000c00: + return clk->read(clk, nv_clk_src_mem) >> P; + } + break; + } + break; + case nv_clk_src_dom6: + switch (nv_device(priv)->chipset) { + case 0x50: + case 0xa0: + return read_pll(priv, 0x00e810) >> 2; + case 0x84: + case 0x86: + case 0x92: + case 0x94: + case 0x96: + case 0x98: + P = (read_div(priv) & 0x00000007) >> 0; + switch (mast & 0x0c000000) { + case 0x00000000: return clk->read(clk, nv_clk_src_href); + case 0x04000000: break; + case 0x08000000: return clk->read(clk, nv_clk_src_hclk); + case 0x0c000000: + return clk->read(clk, nv_clk_src_hclkm3) >> P; + } + break; + default: + break; + } + default: + break; + } + + nv_debug(priv, "unknown clock source %d 0x%08x\n", src, mast); + return -EINVAL; +} + +static u32 +calc_pll(struct nv50_clk_priv *priv, u32 reg, u32 clk, int *N, int *M, int *P) +{ + struct nvkm_bios *bios = nvkm_bios(priv); + struct nvbios_pll pll; + int ret; + + ret = nvbios_pll_parse(bios, reg, &pll); + if (ret) + return 0; + + pll.vco2.max_freq = 0; + pll.refclk = read_pll_ref(priv, reg); + if (!pll.refclk) + return 0; + + return nv04_pll_calc(nv_subdev(priv), &pll, clk, 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)); +} + +static int +nv50_clk_calc(struct nvkm_clk *clk, struct nvkm_cstate *cstate) +{ + struct nv50_clk_priv *priv = (void *)clk; + struct nv50_clk_hwsq *hwsq = &priv->hwsq; + 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, nv_subdev(clk)); + 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 (nv_device(priv)->chipset != 0x98) + out = read_pll(priv, 0x004030); + else + out = clk->read(clk, nv_clk_src_hclkm3d2); + out = calc_div(out, vdec, &P2); + + /* select whichever gets us closest */ + if (abs(vdec - freq) <= abs(vdec - out)) { + if (nv_device(priv)->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, clk->read(clk, nv_clk_src_href))) { + mastv |= 0x00000000; + } else + if (clk_same(dom6, clk->read(clk, nv_clk_src_hclk))) { + mastv |= 0x08000000; + } else { + freq = clk->read(clk, nv_clk_src_hclk) * 3; + freq = 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 (nv_device(priv)->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(priv, 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(priv, 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; +} + +static int +nv50_clk_prog(struct nvkm_clk *clk) +{ + struct nv50_clk_priv *priv = (void *)clk; + return clk_exec(&priv->hwsq, true); +} + +static void +nv50_clk_tidy(struct nvkm_clk *clk) +{ + struct nv50_clk_priv *priv = (void *)clk; + clk_exec(&priv->hwsq, false); +} + +int +nv50_clk_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv50_clk_oclass *pclass = (void *)oclass; + struct nv50_clk_priv *priv; + int ret; + + ret = nvkm_clk_create(parent, engine, oclass, pclass->domains, + NULL, 0, false, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->hwsq.r_fifo = hwsq_reg(0x002504); + priv->hwsq.r_spll[0] = hwsq_reg(0x004020); + priv->hwsq.r_spll[1] = hwsq_reg(0x004024); + priv->hwsq.r_nvpll[0] = hwsq_reg(0x004028); + priv->hwsq.r_nvpll[1] = hwsq_reg(0x00402c); + switch (nv_device(priv)->chipset) { + case 0x92: + case 0x94: + case 0x96: + priv->hwsq.r_divs = hwsq_reg(0x004800); + break; + default: + priv->hwsq.r_divs = hwsq_reg(0x004700); + break; + } + priv->hwsq.r_mast = hwsq_reg(0x00c040); + + priv->base.read = nv50_clk_read; + priv->base.calc = nv50_clk_calc; + priv->base.prog = nv50_clk_prog; + priv->base.tidy = nv50_clk_tidy; + return 0; +} + +static struct nvkm_domain +nv50_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 } +}; + +struct nvkm_oclass * +nv50_clk_oclass = &(struct nv50_clk_oclass) { + .base.handle = NV_SUBDEV(CLK, 0x50), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_clk_ctor, + .dtor = _nvkm_clk_dtor, + .init = _nvkm_clk_init, + .fini = _nvkm_clk_fini, + }, + .domains = nv50_domains, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv50.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv50.h new file mode 100644 index 000000000..0ead76a32 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv50.h @@ -0,0 +1,28 @@ +#ifndef __NVKM_CLK_NV50_H__ +#define __NVKM_CLK_NV50_H__ +#include +#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_priv { + struct nvkm_clk base; + struct nv50_clk_hwsq hwsq; +}; + +int nv50_clk_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); + +struct nv50_clk_oclass { + struct nvkm_oclass base; + struct nvkm_domain *domains; +}; +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/pll.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/pll.h new file mode 100644 index 000000000..44020a30d --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/pll.h @@ -0,0 +1,11 @@ +#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/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/pllgt215.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/pllgt215.c new file mode 100644 index 000000000..783a3e78d --- /dev/null +++ b/kernel/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)) { + nv_error(subdev, "unable to find matching pll values\n"); + return -EINVAL; + } + + return info->refclk * *pN / *pM / *P; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/pllnv04.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/pllnv04.c new file mode 100644 index 000000000..f2292895a --- /dev/null +++ b/kernel/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 = nvkm_bios(subdev); + 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 = nvkm_bios(subdev)->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) + nv_error(subdev, "unable to compute acceptable pll values\n"); + return ret; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/seq.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/seq.h new file mode 100644 index 000000000..d717e8b8f --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/clk/seq.h @@ -0,0 +1,14 @@ +#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/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/Kbuild new file mode 100644 index 000000000..793e73d16 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/Kbuild @@ -0,0 +1,14 @@ +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/gm204.o diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/base.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/base.c new file mode 100644 index 000000000..b0d7c5f40 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/base.c @@ -0,0 +1,96 @@ +/* + * 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 + +int +_nvkm_devinit_fini(struct nvkm_object *object, bool suspend) +{ + struct nvkm_devinit *devinit = (void *)object; + + /* force full reinit on resume */ + if (suspend) + devinit->post = true; + + /* unlock the extended vga crtc regs */ + nv_lockvgac(devinit, false); + + return nvkm_subdev_fini(&devinit->base, suspend); +} + +int +_nvkm_devinit_init(struct nvkm_object *object) +{ + struct nvkm_devinit_impl *impl = (void *)object->oclass; + struct nvkm_devinit *devinit = (void *)object; + int ret; + + ret = nvkm_subdev_init(&devinit->base); + if (ret) + return ret; + + ret = impl->post(&devinit->base, devinit->post); + if (ret) + return ret; + + if (impl->disable) + nv_device(devinit)->disable_mask |= impl->disable(devinit); + return 0; +} + +void +_nvkm_devinit_dtor(struct nvkm_object *object) +{ + struct nvkm_devinit *devinit = (void *)object; + + /* lock crtc regs */ + nv_lockvgac(devinit, true); + + nvkm_subdev_destroy(&devinit->base); +} + +int +nvkm_devinit_create_(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, int size, void **pobject) +{ + struct nvkm_devinit_impl *impl = (void *)oclass; + struct nvkm_device *device = nv_device(parent); + struct nvkm_devinit *devinit; + int ret; + + ret = nvkm_subdev_create_(parent, engine, oclass, 0, "DEVINIT", + "init", size, pobject); + devinit = *pobject; + if (ret) + return ret; + + devinit->post = nvkm_boolopt(device->cfgopt, "NvForcePost", false); + devinit->meminit = impl->meminit; + devinit->pll_set = impl->pll_set; + devinit->mmio = impl->mmio; + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/fbmem.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/fbmem.h new file mode 100644 index 000000000..36684c3f9 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/fbmem.h @@ -0,0 +1,84 @@ +/* + * 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 +#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(nv_device_resource_start(dev, 1), + nv_device_resource_len(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/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/g84.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/g84.c new file mode 100644 index 000000000..ca776ce75 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/g84.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 +g84_devinit_disable(struct nvkm_devinit *devinit) +{ + struct nv50_devinit_priv *priv = (void *)devinit; + u32 r001540 = nv_rd32(priv, 0x001540); + u32 r00154c = nv_rd32(priv, 0x00154c); + u64 disable = 0ULL; + + if (!(r001540 & 0x40000000)) { + disable |= (1ULL << NVDEV_ENGINE_MPEG); + disable |= (1ULL << NVDEV_ENGINE_VP); + disable |= (1ULL << NVDEV_ENGINE_BSP); + disable |= (1ULL << NVDEV_ENGINE_CIPHER); + } + + if (!(r00154c & 0x00000004)) + disable |= (1ULL << NVDEV_ENGINE_DISP); + if (!(r00154c & 0x00000020)) + disable |= (1ULL << NVDEV_ENGINE_BSP); + if (!(r00154c & 0x00000040)) + disable |= (1ULL << NVDEV_ENGINE_CIPHER); + + return disable; +} + +struct nvkm_oclass * +g84_devinit_oclass = &(struct nvkm_devinit_impl) { + .base.handle = NV_SUBDEV(DEVINIT, 0x84), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_devinit_ctor, + .dtor = _nvkm_devinit_dtor, + .init = nv50_devinit_init, + .fini = _nvkm_devinit_fini, + }, + .pll_set = nv50_devinit_pll_set, + .disable = g84_devinit_disable, + .post = nvbios_init, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/g98.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/g98.c new file mode 100644 index 000000000..d29bacee6 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/g98.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 "nv50.h" + +#include +#include + +static u64 +g98_devinit_disable(struct nvkm_devinit *devinit) +{ + struct nv50_devinit_priv *priv = (void *)devinit; + u32 r001540 = nv_rd32(priv, 0x001540); + u32 r00154c = nv_rd32(priv, 0x00154c); + u64 disable = 0ULL; + + if (!(r001540 & 0x40000000)) { + disable |= (1ULL << NVDEV_ENGINE_MSPDEC); + disable |= (1ULL << NVDEV_ENGINE_MSVLD); + disable |= (1ULL << NVDEV_ENGINE_MSPPP); + } + + if (!(r00154c & 0x00000004)) + disable |= (1ULL << NVDEV_ENGINE_DISP); + if (!(r00154c & 0x00000020)) + disable |= (1ULL << NVDEV_ENGINE_MSVLD); + if (!(r00154c & 0x00000040)) + disable |= (1ULL << NVDEV_ENGINE_SEC); + + return disable; +} + +struct nvkm_oclass * +g98_devinit_oclass = &(struct nvkm_devinit_impl) { + .base.handle = NV_SUBDEV(DEVINIT, 0x98), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_devinit_ctor, + .dtor = _nvkm_devinit_dtor, + .init = nv50_devinit_init, + .fini = _nvkm_devinit_fini, + }, + .pll_set = nv50_devinit_pll_set, + .disable = g98_devinit_disable, + .post = nvbios_init, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gf100.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gf100.c new file mode 100644 index 000000000..c61102f70 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gf100.c @@ -0,0 +1,127 @@ +/* + * 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 *devinit, u32 type, u32 freq) +{ + struct nv50_devinit_priv *priv = (void *)devinit; + struct nvkm_bios *bios = nvkm_bios(priv); + struct nvbios_pll info; + int N, fN, M, P; + int ret; + + ret = nvbios_pll_parse(bios, type, &info); + if (ret) + return ret; + + ret = gt215_pll_calc(nv_subdev(devinit), &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: + nv_mask(priv, info.reg + 0x0c, 0x00000000, 0x00000100); + nv_wr32(priv, info.reg + 0x04, (P << 16) | (N << 8) | M); + nv_wr32(priv, info.reg + 0x10, fN << 16); + break; + default: + nv_warn(priv, "0x%08x/%dKhz unimplemented\n", type, freq); + ret = -EINVAL; + break; + } + + return ret; +} + +static u64 +gf100_devinit_disable(struct nvkm_devinit *devinit) +{ + struct nv50_devinit_priv *priv = (void *)devinit; + u32 r022500 = nv_rd32(priv, 0x022500); + u64 disable = 0ULL; + + if (r022500 & 0x00000001) + disable |= (1ULL << NVDEV_ENGINE_DISP); + + if (r022500 & 0x00000002) { + disable |= (1ULL << NVDEV_ENGINE_MSPDEC); + disable |= (1ULL << NVDEV_ENGINE_MSPPP); + } + + if (r022500 & 0x00000004) + disable |= (1ULL << NVDEV_ENGINE_MSVLD); + if (r022500 & 0x00000008) + disable |= (1ULL << NVDEV_ENGINE_MSENC); + if (r022500 & 0x00000100) + disable |= (1ULL << NVDEV_ENGINE_CE0); + if (r022500 & 0x00000200) + disable |= (1ULL << NVDEV_ENGINE_CE1); + + return disable; +} + +int +gf100_devinit_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_devinit_impl *impl = (void *)oclass; + struct nv50_devinit_priv *priv; + u64 disable; + int ret; + + ret = nvkm_devinit_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + disable = impl->disable(&priv->base); + if (disable & (1ULL << NVDEV_ENGINE_DISP)) + priv->base.post = true; + + return 0; +} + +struct nvkm_oclass * +gf100_devinit_oclass = &(struct nvkm_devinit_impl) { + .base.handle = NV_SUBDEV(DEVINIT, 0xc0), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_devinit_ctor, + .dtor = _nvkm_devinit_dtor, + .init = nv50_devinit_init, + .fini = _nvkm_devinit_fini, + }, + .pll_set = gf100_devinit_pll_set, + .disable = gf100_devinit_disable, + .post = nvbios_init, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm107.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm107.c new file mode 100644 index 000000000..87ca0ece3 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm107.c @@ -0,0 +1,59 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv50.h" + +#include +#include + +u64 +gm107_devinit_disable(struct nvkm_devinit *devinit) +{ + struct nv50_devinit_priv *priv = (void *)devinit; + u32 r021c00 = nv_rd32(priv, 0x021c00); + u32 r021c04 = nv_rd32(priv, 0x021c04); + u64 disable = 0ULL; + + if (r021c00 & 0x00000001) + disable |= (1ULL << NVDEV_ENGINE_CE0); + if (r021c00 & 0x00000004) + disable |= (1ULL << NVDEV_ENGINE_CE2); + if (r021c04 & 0x00000001) + disable |= (1ULL << NVDEV_ENGINE_DISP); + + return disable; +} + +struct nvkm_oclass * +gm107_devinit_oclass = &(struct nvkm_devinit_impl) { + .base.handle = NV_SUBDEV(DEVINIT, 0x07), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_devinit_ctor, + .dtor = _nvkm_devinit_dtor, + .init = nv50_devinit_init, + .fini = _nvkm_devinit_fini, + }, + .pll_set = gf100_devinit_pll_set, + .disable = gm107_devinit_disable, + .post = nvbios_init, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm204.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm204.c new file mode 100644 index 000000000..1076fcf0d --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm204.c @@ -0,0 +1,172 @@ +/* + * 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 + +static void +pmu_code(struct nv50_devinit_priv *priv, u32 pmu, u32 img, u32 len, bool sec) +{ + struct nvkm_bios *bios = nvkm_bios(priv); + int i; + + nv_wr32(priv, 0x10a180, 0x01000000 | (sec ? 0x10000000 : 0) | pmu); + for (i = 0; i < len; i += 4) { + if ((i & 0xff) == 0) + nv_wr32(priv, 0x10a188, (pmu + i) >> 8); + nv_wr32(priv, 0x10a184, nv_ro32(bios, img + i)); + } + + while (i & 0xff) { + nv_wr32(priv, 0x10a184, 0x00000000); + i += 4; + } +} + +static void +pmu_data(struct nv50_devinit_priv *priv, u32 pmu, u32 img, u32 len) +{ + struct nvkm_bios *bios = nvkm_bios(priv); + int i; + + nv_wr32(priv, 0x10a1c0, 0x01000000 | pmu); + for (i = 0; i < len; i += 4) + nv_wr32(priv, 0x10a1c4, nv_ro32(bios, img + i)); +} + +static u32 +pmu_args(struct nv50_devinit_priv *priv, u32 argp, u32 argi) +{ + nv_wr32(priv, 0x10a1c0, argp); + nv_wr32(priv, 0x10a1c0, nv_rd32(priv, 0x10a1c4) + argi); + return nv_rd32(priv, 0x10a1c4); +} + +static void +pmu_exec(struct nv50_devinit_priv *priv, u32 init_addr) +{ + nv_wr32(priv, 0x10a104, init_addr); + nv_wr32(priv, 0x10a10c, 0x00000000); + nv_wr32(priv, 0x10a100, 0x00000002); +} + +static int +pmu_load(struct nv50_devinit_priv *priv, u8 type, bool post, + u32 *init_addr_pmu, u32 *args_addr_pmu) +{ + struct nvkm_bios *bios = nvkm_bios(priv); + struct nvbios_pmuR pmu; + + if (!nvbios_pmuRm(bios, type, &pmu)) { + nv_error(priv, "VBIOS PMU fuc %02x not found\n", type); + return -EINVAL; + } + + if (!post) + return 0; + + pmu_code(priv, pmu.boot_addr_pmu, pmu.boot_addr, pmu.boot_size, false); + pmu_code(priv, pmu.code_addr_pmu, pmu.code_addr, pmu.code_size, true); + pmu_data(priv, 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(priv, pmu.init_addr_pmu), 0; +} + +static int +gm204_devinit_post(struct nvkm_subdev *subdev, bool post) +{ + struct nv50_devinit_priv *priv = (void *)nvkm_devinit(subdev); + struct nvkm_bios *bios = nvkm_bios(priv); + struct bit_entry bit_I; + u32 init, args; + int ret; + + if (bit_entry(bios, 'I', &bit_I) || bit_I.version != 1 || + bit_I.length < 0x1c) { + nv_error(priv, "VBIOS PMU init data not found\n"); + return -EINVAL; + } + + /* reset PMU and load init table parser ucode */ + if (post) { + nv_mask(priv, 0x000200, 0x00002000, 0x00000000); + nv_mask(priv, 0x000200, 0x00002000, 0x00002000); + nv_rd32(priv, 0x000200); + while (nv_rd32(priv, 0x10a10c) & 0x00000006) { + } + } + + ret = pmu_load(priv, 0x04, post, &init, &args); + if (ret) + return ret; + + /* upload first chunk of init data */ + if (post) { + u32 pmu = pmu_args(priv, args + 0x08, 0x08); + u32 img = nv_ro16(bios, bit_I.offset + 0x14); + u32 len = nv_ro16(bios, bit_I.offset + 0x16); + pmu_data(priv, pmu, img, len); + } + + /* upload second chunk of init data */ + if (post) { + u32 pmu = pmu_args(priv, args + 0x08, 0x10); + u32 img = nv_ro16(bios, bit_I.offset + 0x18); + u32 len = nv_ro16(bios, bit_I.offset + 0x1a); + pmu_data(priv, pmu, img, len); + } + + /* execute init tables */ + if (post) { + nv_wr32(priv, 0x10a040, 0x00005000); + pmu_exec(priv, init); + while (!(nv_rd32(priv, 0x10a040) & 0x00002000)) { + } + } + + /* load and execute some other ucode image (bios therm?) */ + return pmu_load(priv, 0x01, post, NULL, NULL); +} + +struct nvkm_oclass * +gm204_devinit_oclass = &(struct nvkm_devinit_impl) { + .base.handle = NV_SUBDEV(DEVINIT, 0x07), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_devinit_ctor, + .dtor = _nvkm_devinit_dtor, + .init = nv50_devinit_init, + .fini = _nvkm_devinit_fini, + }, + .pll_set = gf100_devinit_pll_set, + .disable = gm107_devinit_disable, + .post = gm204_devinit_post, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gt215.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gt215.c new file mode 100644 index 000000000..6a3e8d4ef --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gt215.c @@ -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 + */ +#include "nv50.h" + +#include +#include +#include +#include + +int +gt215_devinit_pll_set(struct nvkm_devinit *devinit, u32 type, u32 freq) +{ + struct nv50_devinit_priv *priv = (void *)devinit; + struct nvkm_bios *bios = nvkm_bios(priv); + struct nvbios_pll info; + int N, fN, M, P; + int ret; + + ret = nvbios_pll_parse(bios, type, &info); + if (ret) + return ret; + + ret = gt215_pll_calc(nv_subdev(devinit), &info, freq, &N, &fN, &M, &P); + if (ret < 0) + return ret; + + switch (info.type) { + case PLL_VPLL0: + case PLL_VPLL1: + nv_wr32(priv, info.reg + 0, 0x50000610); + nv_mask(priv, info.reg + 4, 0x003fffff, + (P << 16) | (M << 8) | N); + nv_wr32(priv, info.reg + 8, fN); + break; + default: + nv_warn(priv, "0x%08x/%dKhz unimplemented\n", type, freq); + ret = -EINVAL; + break; + } + + return ret; +} + +static u64 +gt215_devinit_disable(struct nvkm_devinit *devinit) +{ + struct nv50_devinit_priv *priv = (void *)devinit; + u32 r001540 = nv_rd32(priv, 0x001540); + u32 r00154c = nv_rd32(priv, 0x00154c); + u64 disable = 0ULL; + + if (!(r001540 & 0x40000000)) { + disable |= (1ULL << NVDEV_ENGINE_MSPDEC); + disable |= (1ULL << NVDEV_ENGINE_MSPPP); + } + + if (!(r00154c & 0x00000004)) + disable |= (1ULL << NVDEV_ENGINE_DISP); + if (!(r00154c & 0x00000020)) + disable |= (1ULL << NVDEV_ENGINE_MSVLD); + if (!(r00154c & 0x00000200)) + disable |= (1ULL << NVDEV_ENGINE_CE0); + + return disable; +} + +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 *devinit, u32 addr) +{ + struct nv50_devinit_priv *priv = (void *)devinit; + 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 priv 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 (!priv->r001540) + priv->r001540 = nv_rd32(priv, 0x001540); + if (part >= hweight8((priv->r001540 >> 16) & 0xff)) + return ~0; + return addr; + } + mmio += 3; + } + + return addr; +} + +struct nvkm_oclass * +gt215_devinit_oclass = &(struct nvkm_devinit_impl) { + .base.handle = NV_SUBDEV(DEVINIT, 0xa3), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_devinit_ctor, + .dtor = _nvkm_devinit_dtor, + .init = nv50_devinit_init, + .fini = _nvkm_devinit_fini, + }, + .pll_set = gt215_devinit_pll_set, + .disable = gt215_devinit_disable, + .mmio = gt215_devinit_mmio, + .post = nvbios_init, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/mcp89.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/mcp89.c new file mode 100644 index 000000000..55cf48bbc --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/mcp89.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 +mcp89_devinit_disable(struct nvkm_devinit *devinit) +{ + struct nv50_devinit_priv *priv = (void *)devinit; + u32 r001540 = nv_rd32(priv, 0x001540); + u32 r00154c = nv_rd32(priv, 0x00154c); + u64 disable = 0; + + if (!(r001540 & 0x40000000)) { + disable |= (1ULL << NVDEV_ENGINE_MSPDEC); + disable |= (1ULL << NVDEV_ENGINE_MSPPP); + } + + if (!(r00154c & 0x00000004)) + disable |= (1ULL << NVDEV_ENGINE_DISP); + if (!(r00154c & 0x00000020)) + disable |= (1ULL << NVDEV_ENGINE_MSVLD); + if (!(r00154c & 0x00000040)) + disable |= (1ULL << NVDEV_ENGINE_VIC); + if (!(r00154c & 0x00000200)) + disable |= (1ULL << NVDEV_ENGINE_CE0); + + return disable; +} + +struct nvkm_oclass * +mcp89_devinit_oclass = &(struct nvkm_devinit_impl) { + .base.handle = NV_SUBDEV(DEVINIT, 0xaf), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_devinit_ctor, + .dtor = _nvkm_devinit_dtor, + .init = nv50_devinit_init, + .fini = _nvkm_devinit_fini, + }, + .pll_set = gt215_devinit_pll_set, + .disable = mcp89_devinit_disable, + .post = nvbios_init, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.c new file mode 100644 index 000000000..03a0da834 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.c @@ -0,0 +1,470 @@ +/* + * 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 *devinit) +{ + struct nv04_devinit_priv *priv = (void *)devinit; + u32 patt = 0xdeadbeef; + struct io_mapping *fb; + int i; + + /* Map the framebuffer aperture */ + fb = fbmem_init(nv_device(priv)); + if (!fb) { + nv_error(priv, "failed to map fb\n"); + return; + } + + /* Sequencer and refresh off */ + nv_wrvgas(priv, 0, 1, nv_rdvgas(priv, 0, 1) | 0x20); + nv_mask(priv, NV04_PFB_DEBUG_0, 0, NV04_PFB_DEBUG_0_REFRESH_OFF); + + nv_mask(priv, 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) { + nv_mask(priv, NV04_PFB_BOOT_0, + NV04_PFB_BOOT_0_RAM_TYPE, + NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_16MBIT); + nv_mask(priv, 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)) + nv_mask(priv, 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)) { + nv_mask(priv, 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)) + nv_mask(priv, NV04_PFB_BOOT_0, + NV04_PFB_BOOT_0_RAM_AMOUNT, + NV04_PFB_BOOT_0_RAM_AMOUNT_8MB); + else + nv_mask(priv, NV04_PFB_BOOT_0, + NV04_PFB_BOOT_0_RAM_AMOUNT, + NV04_PFB_BOOT_0_RAM_AMOUNT_4MB); + + nv_mask(priv, 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)) { + nv_mask(priv, NV04_PFB_BOOT_0, NV04_PFB_BOOT_0_RAM_AMOUNT, + NV04_PFB_BOOT_0_RAM_AMOUNT_8MB); + + } + + /* Refresh on, sequencer on */ + nv_mask(priv, NV04_PFB_DEBUG_0, NV04_PFB_DEBUG_0_REFRESH_OFF, 0); + nv_wrvgas(priv, 0, 1, nv_rdvgas(priv, 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; + case 0x680508: + shift += 4; + case 0x680504: + shift += 4; + 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 *devinit, u32 reg, + struct nvkm_pll_vals *pv) +{ + int chip_version = nvkm_bios(devinit)->version.chip; + uint32_t oldpll = nv_rd32(devinit, 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 = nv_rd32(devinit, 0x001584); + nv_wr32(devinit, 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 */ + nv_wr32(devinit, reg, pv->log2P << 16 | (oldpll & 0xffff)); + else + /* downclock -- write new NM first */ + nv_wr32(devinit, reg, (oldpll & 0xffff0000) | pv->NM1); + + if ((chip_version < 0x17 || chip_version == 0x1a) && + chip_version != 0x11) + /* wait a bit on older chips */ + msleep(64); + nv_rd32(devinit, reg); + + /* then write the other half as well */ + nv_wr32(devinit, reg, pll); + + if (shift_powerctrl_1 >= 0) + nv_wr32(devinit, 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 *devinit, u32 reg1, + struct nvkm_pll_vals *pv) +{ + int chip_version = nvkm_bios(devinit)->version.chip; + bool nv3035 = chip_version == 0x30 || chip_version == 0x35; + uint32_t reg2 = reg1 + ((reg1 == 0x680520) ? 0x5c : 0x70); + uint32_t oldpll1 = nv_rd32(devinit, reg1); + uint32_t oldpll2 = !nv3035 ? nv_rd32(devinit, 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 = nv_rd32(devinit, 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 = nv_rd32(devinit, 0x001584); + nv_wr32(devinit, 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; + case 0x680500: + shift_c040 += 2; + case 0x680520: + shift_c040 += 2; + case 0x680508: + shift_c040 += 2; + } + + savedc040 = nv_rd32(devinit, 0xc040); + if (shift_c040 != 14) + nv_wr32(devinit, 0xc040, savedc040 & ~(3 << shift_c040)); + } + + if (oldramdac580 != ramdac580) + nv_wr32(devinit, 0x680580, ramdac580); + + if (!nv3035) + nv_wr32(devinit, reg2, pll2); + nv_wr32(devinit, reg1, pll1); + + if (shift_powerctrl_1 >= 0) + nv_wr32(devinit, 0x001584, saved_powerctrl_1); + if (chip_version >= 0x40) + nv_wr32(devinit, 0xc040, savedc040); +} + +void +setPLL_double_lowregs(struct nvkm_devinit *devinit, 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 :) + */ + + uint32_t Preg = NMNMreg - 4; + bool mpll = Preg == 0x4020; + uint32_t oldPval = nv_rd32(devinit, 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 (nv_rd32(devinit, 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(nvkm_bios(devinit), Preg, &info)) + return; + + Pval2 = pv->log2P + info.bias_p; + if (Pval2 > info.max_p) + Pval2 = info.max_p; + Pval |= 1 << 28 | Pval2 << 20; + + saved4600 = nv_rd32(devinit, 0x4600); + nv_wr32(devinit, 0x4600, saved4600 | 8 << 28); + } + if (single_stage) + Pval |= mpll ? 1 << 12 : 1 << 8; + + nv_wr32(devinit, Preg, oldPval | 1 << 28); + nv_wr32(devinit, Preg, Pval & ~(4 << 28)); + if (mpll) { + Pval |= 8 << 20; + nv_wr32(devinit, 0x4020, Pval & ~(0xc << 28)); + nv_wr32(devinit, 0x4038, Pval & ~(0xc << 28)); + } + + savedc040 = nv_rd32(devinit, 0xc040); + nv_wr32(devinit, 0xc040, savedc040 & maskc040); + + nv_wr32(devinit, NMNMreg, NMNM); + if (NMNMreg == 0x4024) + nv_wr32(devinit, 0x403c, NMNM); + + nv_wr32(devinit, Preg, Pval); + if (mpll) { + Pval &= ~(8 << 20); + nv_wr32(devinit, 0x4020, Pval); + nv_wr32(devinit, 0x4038, Pval); + nv_wr32(devinit, 0x4600, saved4600); + } + + nv_wr32(devinit, 0xc040, savedc040); + + if (mpll) { + nv_wr32(devinit, 0x4020, Pval & ~(1 << 28)); + nv_wr32(devinit, 0x4038, Pval & ~(1 << 28)); + } +} + +int +nv04_devinit_pll_set(struct nvkm_devinit *devinit, u32 type, u32 freq) +{ + struct nvkm_bios *bios = nvkm_bios(devinit); + 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(nv_subdev(devinit), &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_fini(struct nvkm_object *object, bool suspend) +{ + struct nv04_devinit_priv *priv = (void *)object; + int ret; + + /* make i2c busses accessible */ + nv_mask(priv, 0x000200, 0x00000001, 0x00000001); + + ret = nvkm_devinit_fini(&priv->base, suspend); + if (ret) + return ret; + + /* unslave crtcs */ + if (priv->owner < 0) + priv->owner = nv_rdvgaowner(priv); + nv_wrvgaowner(priv, 0); + return 0; +} + +int +nv04_devinit_init(struct nvkm_object *object) +{ + struct nv04_devinit_priv *priv = (void *)object; + + if (!priv->base.post) { + u32 htotal = nv_rdvgac(priv, 0, 0x06); + htotal |= (nv_rdvgac(priv, 0, 0x07) & 0x01) << 8; + htotal |= (nv_rdvgac(priv, 0, 0x07) & 0x20) << 4; + htotal |= (nv_rdvgac(priv, 0, 0x25) & 0x01) << 10; + htotal |= (nv_rdvgac(priv, 0, 0x41) & 0x01) << 11; + if (!htotal) { + nv_info(priv, "adaptor not initialised\n"); + priv->base.post = true; + } + } + + return nvkm_devinit_init(&priv->base); +} + +void +nv04_devinit_dtor(struct nvkm_object *object) +{ + struct nv04_devinit_priv *priv = (void *)object; + + /* restore vga owner saved at first init */ + nv_wrvgaowner(priv, priv->owner); + + nvkm_devinit_destroy(&priv->base); +} + +int +nv04_devinit_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv04_devinit_priv *priv; + int ret; + + ret = nvkm_devinit_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->owner = -1; + return 0; +} + +struct nvkm_oclass * +nv04_devinit_oclass = &(struct nvkm_devinit_impl) { + .base.handle = NV_SUBDEV(DEVINIT, 0x04), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_devinit_ctor, + .dtor = nv04_devinit_dtor, + .init = nv04_devinit_init, + .fini = nv04_devinit_fini, + }, + .meminit = nv04_devinit_meminit, + .pll_set = nv04_devinit_pll_set, + .post = nvbios_init, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.h new file mode 100644 index 000000000..7c63abf11 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.h @@ -0,0 +1,22 @@ +#ifndef __NVKM_DEVINIT_NV04_H__ +#define __NVKM_DEVINIT_NV04_H__ +#include "priv.h" +struct nvkm_pll_vals; + +struct nv04_devinit_priv { + struct nvkm_devinit base; + int owner; +}; + +int nv04_devinit_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); +void nv04_devinit_dtor(struct nvkm_object *); +int nv04_devinit_init(struct nvkm_object *); +int nv04_devinit_fini(struct nvkm_object *, bool); +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/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv05.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv05.c new file mode 100644 index 000000000..def864921 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv05.c @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "nv04.h" +#include "fbmem.h" + +#include +#include +#include +#include + +static void +nv05_devinit_meminit(struct nvkm_devinit *devinit) +{ + static const u8 default_config_tab[][2] = { + { 0x24, 0x00 }, + { 0x28, 0x00 }, + { 0x24, 0x01 }, + { 0x1f, 0x00 }, + { 0x0f, 0x00 }, + { 0x17, 0x00 }, + { 0x06, 0x00 }, + { 0x00, 0x00 } + }; + struct nv04_devinit_priv *priv = (void *)devinit; + struct nvkm_bios *bios = nvkm_bios(priv); + struct io_mapping *fb; + u32 patt = 0xdeadbeef; + u16 data; + u8 strap, ramcfg[2]; + int i, v; + + /* Map the framebuffer aperture */ + fb = fbmem_init(nv_device(priv)); + if (!fb) { + nv_error(priv, "failed to map fb\n"); + return; + } + + strap = (nv_rd32(priv, 0x101000) & 0x0000003c) >> 2; + if ((data = bmp_mem_init_table(bios))) { + ramcfg[0] = nv_ro08(bios, data + 2 * strap + 0); + ramcfg[1] = nv_ro08(bios, data + 2 * strap + 1); + } else { + ramcfg[0] = default_config_tab[strap][0]; + ramcfg[1] = default_config_tab[strap][1]; + } + + /* Sequencer off */ + nv_wrvgas(priv, 0, 1, nv_rdvgas(priv, 0, 1) | 0x20); + + if (nv_rd32(priv, NV04_PFB_BOOT_0) & NV04_PFB_BOOT_0_UMA_ENABLE) + goto out; + + nv_mask(priv, 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 = nv_ro32(bios, data); + nv_wr32(priv, NV04_PFB_SCRAMBLE(i), scramble); + } + } + + /* Set memory type/width/length defaults depending on the straps */ + nv_mask(priv, NV04_PFB_BOOT_0, 0x3f, ramcfg[0]); + + if (ramcfg[1] & 0x80) + nv_mask(priv, NV04_PFB_CFG0, 0, NV04_PFB_CFG0_SCRAMBLE); + + nv_mask(priv, NV04_PFB_CFG1, 0x700001, (ramcfg[1] & 1) << 20); + nv_mask(priv, 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) + nv_mask(priv, NV04_PFB_BOOT_0, + NV04_PFB_BOOT_0_RAM_WIDTH_128, 0); + + /* Probe memory length */ + v = nv_rd32(priv, 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))) + nv_mask(priv, 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)) + nv_mask(priv, NV04_PFB_BOOT_0, NV04_PFB_BOOT_0_RAM_AMOUNT, + NV04_PFB_BOOT_0_RAM_AMOUNT_8MB); + + if (!fbmem_readback(fb, 0x400000, ++patt)) + nv_mask(priv, NV04_PFB_BOOT_0, NV04_PFB_BOOT_0_RAM_AMOUNT, + NV04_PFB_BOOT_0_RAM_AMOUNT_4MB); + +out: + /* Sequencer on */ + nv_wrvgas(priv, 0, 1, nv_rdvgas(priv, 0, 1) & ~0x20); + fbmem_fini(fb); +} + +struct nvkm_oclass * +nv05_devinit_oclass = &(struct nvkm_devinit_impl) { + .base.handle = NV_SUBDEV(DEVINIT, 0x05), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_devinit_ctor, + .dtor = nv04_devinit_dtor, + .init = nv04_devinit_init, + .fini = nv04_devinit_fini, + }, + .meminit = nv05_devinit_meminit, + .pll_set = nv04_devinit_pll_set, + .post = nvbios_init, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv10.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv10.c new file mode 100644 index 000000000..7aabc1bf0 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv10.c @@ -0,0 +1,111 @@ +/* + * 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 *devinit) +{ + struct nv04_devinit_priv *priv = (void *)devinit; + 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 (nv_device(priv)->card_type >= NV_11 && + nv_device(priv)->chipset >= 0x17) + mem_width_count = 3; + else + mem_width_count = 2; + + /* Map the framebuffer aperture */ + fb = fbmem_init(nv_device(priv)); + if (!fb) { + nv_error(priv, "failed to map fb\n"); + return; + } + + nv_wr32(priv, NV10_PFB_REFCTRL, NV10_PFB_REFCTRL_VALID_1); + + /* Probe memory bus width */ + for (i = 0; i < mem_width_count; i++) { + nv_mask(priv, 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 = nv_rd32(priv, 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. */ + nv_mask(priv, NV04_PFB_CFG0, 0x1000, 0); + +amount_found: + fbmem_fini(fb); +} + +struct nvkm_oclass * +nv10_devinit_oclass = &(struct nvkm_devinit_impl) { + .base.handle = NV_SUBDEV(DEVINIT, 0x10), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_devinit_ctor, + .dtor = nv04_devinit_dtor, + .init = nv04_devinit_init, + .fini = nv04_devinit_fini, + }, + .meminit = nv10_devinit_meminit, + .pll_set = nv04_devinit_pll_set, + .post = nvbios_init, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv1a.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv1a.c new file mode 100644 index 000000000..9f36fff5a --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv1a.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 "nv04.h" + +#include +#include + +struct nvkm_oclass * +nv1a_devinit_oclass = &(struct nvkm_devinit_impl) { + .base.handle = NV_SUBDEV(DEVINIT, 0x1a), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_devinit_ctor, + .dtor = nv04_devinit_dtor, + .init = nv04_devinit_init, + .fini = nv04_devinit_fini, + }, + .pll_set = nv04_devinit_pll_set, + .post = nvbios_init, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv20.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv20.c new file mode 100644 index 000000000..02fcfd921 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv20.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "nv04.h" +#include "fbmem.h" + +#include +#include + +static void +nv20_devinit_meminit(struct nvkm_devinit *devinit) +{ + struct nv04_devinit_priv *priv = (void *)devinit; + struct nvkm_device *device = nv_device(priv); + uint32_t mask = (device->chipset >= 0x25 ? 0x300 : 0x900); + uint32_t amount, off; + struct io_mapping *fb; + + /* Map the framebuffer aperture */ + fb = fbmem_init(nv_device(priv)); + if (!fb) { + nv_error(priv, "failed to map fb\n"); + return; + } + + nv_wr32(priv, NV10_PFB_REFCTRL, NV10_PFB_REFCTRL_VALID_1); + + /* Allow full addressing */ + nv_mask(priv, NV04_PFB_CFG0, 0, mask); + + amount = nv_rd32(priv, 0x10020c); + for (off = amount; off > 0x2000000; off -= 0x2000000) + fbmem_poke(fb, off - 4, off); + + amount = nv_rd32(priv, 0x10020c); + if (amount != fbmem_peek(fb, amount - 4)) + /* IC missing - disable the upper half memory space. */ + nv_mask(priv, NV04_PFB_CFG0, mask, 0); + + fbmem_fini(fb); +} + +struct nvkm_oclass * +nv20_devinit_oclass = &(struct nvkm_devinit_impl) { + .base.handle = NV_SUBDEV(DEVINIT, 0x20), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_devinit_ctor, + .dtor = nv04_devinit_dtor, + .init = nv04_devinit_init, + .fini = nv04_devinit_fini, + }, + .meminit = nv20_devinit_meminit, + .pll_set = nv04_devinit_pll_set, + .post = nvbios_init, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.c new file mode 100644 index 000000000..26b7cb13e --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.c @@ -0,0 +1,174 @@ +/* + * 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 +#include + +int +nv50_devinit_pll_set(struct nvkm_devinit *devinit, u32 type, u32 freq) +{ + struct nv50_devinit_priv *priv = (void *)devinit; + struct nvkm_bios *bios = nvkm_bios(priv); + struct nvbios_pll info; + int N1, M1, N2, M2, P; + int ret; + + ret = nvbios_pll_parse(bios, type, &info); + if (ret) { + nv_error(devinit, "failed to retrieve pll data, %d\n", ret); + return ret; + } + + ret = nv04_pll_calc(nv_subdev(devinit), &info, freq, &N1, &M1, &N2, &M2, &P); + if (!ret) { + nv_error(devinit, "failed pll calculation\n"); + return ret; + } + + switch (info.type) { + case PLL_VPLL0: + case PLL_VPLL1: + nv_wr32(priv, info.reg + 0, 0x10000611); + nv_mask(priv, info.reg + 4, 0x00ff00ff, (M1 << 16) | N1); + nv_mask(priv, info.reg + 8, 0x7fff00ff, (P << 28) | + (M2 << 16) | N2); + break; + case PLL_MEMORY: + nv_mask(priv, info.reg + 0, 0x01ff0000, (P << 22) | + (info.bias_p << 19) | + (P << 16)); + nv_wr32(priv, info.reg + 4, (N1 << 8) | M1); + break; + default: + nv_mask(priv, info.reg + 0, 0x00070000, (P << 16)); + nv_wr32(priv, info.reg + 4, (N1 << 8) | M1); + break; + } + + return 0; +} + +static u64 +nv50_devinit_disable(struct nvkm_devinit *devinit) +{ + struct nv50_devinit_priv *priv = (void *)devinit; + u32 r001540 = nv_rd32(priv, 0x001540); + u64 disable = 0ULL; + + if (!(r001540 & 0x40000000)) + disable |= (1ULL << NVDEV_ENGINE_MPEG); + + return disable; +} + +int +nv50_devinit_init(struct nvkm_object *object) +{ + struct nvkm_bios *bios = nvkm_bios(object); + struct nvkm_ibus *ibus = nvkm_ibus(object); + struct nv50_devinit_priv *priv = (void *)object; + struct nvbios_outp info; + struct dcb_output outp; + u8 ver = 0xff, hdr, cnt, len; + int ret, i = 0; + + if (!priv->base.post) { + if (!nv_rdvgac(priv, 0, 0x00) && + !nv_rdvgac(priv, 0, 0x1a)) { + nv_info(priv, "adaptor not initialised\n"); + priv->base.post = true; + } + } + + /* some boards appear to require certain priv register timeouts + * to be bumped before runing devinit scripts. not a clue why + * the vbios engineers didn't make the scripts just work... + */ + if (priv->base.post && ibus) + nv_ofuncs(ibus)->init(nv_object(ibus)); + + ret = nvkm_devinit_init(&priv->base); + if (ret) + return ret; + + /* 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 (priv->base.post && dcb_outp_parse(bios, i, &ver, &hdr, &outp)) { + if (nvbios_outp_match(bios, outp.hasht, outp.hashm, + &ver, &hdr, &cnt, &len, &info)) { + struct nvbios_init init = { + .subdev = nv_subdev(priv), + .bios = bios, + .offset = info.script[0], + .outp = &outp, + .crtc = -1, + .execute = 1, + }; + + nvbios_exec(&init); + } + i++; + } + + return 0; +} + +int +nv50_devinit_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv50_devinit_priv *priv; + int ret; + + ret = nvkm_devinit_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + return 0; +} + +struct nvkm_oclass * +nv50_devinit_oclass = &(struct nvkm_devinit_impl) { + .base.handle = NV_SUBDEV(DEVINIT, 0x50), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_devinit_ctor, + .dtor = _nvkm_devinit_dtor, + .init = nv50_devinit_init, + .fini = _nvkm_devinit_fini, + }, + .pll_set = nv50_devinit_pll_set, + .disable = nv50_devinit_disable, + .post = nvbios_init, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.h new file mode 100644 index 000000000..9243521c8 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.h @@ -0,0 +1,24 @@ +#ifndef __NVKM_DEVINIT_NV50_H__ +#define __NVKM_DEVINIT_NV50_H__ +#include "priv.h" + +struct nv50_devinit_priv { + struct nvkm_devinit base; + u32 r001540; +}; + +int nv50_devinit_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); +int nv50_devinit_init(struct nvkm_object *); +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); + +u64 gm107_devinit_disable(struct nvkm_devinit *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/priv.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/priv.h new file mode 100644 index 000000000..bb51a95d8 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/priv.h @@ -0,0 +1,34 @@ +#ifndef __NVKM_DEVINIT_PRIV_H__ +#define __NVKM_DEVINIT_PRIV_H__ +#include + +struct nvkm_devinit_impl { + struct nvkm_oclass base; + void (*meminit)(struct nvkm_devinit *); + int (*pll_set)(struct nvkm_devinit *, u32 type, u32 freq); + u64 (*disable)(struct nvkm_devinit *); + u32 (*mmio)(struct nvkm_devinit *, u32); + int (*post)(struct nvkm_subdev *, bool); +}; + +#define nvkm_devinit_create(p,e,o,d) \ + nvkm_devinit_create_((p), (e), (o), sizeof(**d), (void **)d) +#define nvkm_devinit_destroy(p) ({ \ + struct nvkm_devinit *d = (p); \ + _nvkm_devinit_dtor(nv_object(d)); \ +}) +#define nvkm_devinit_init(p) ({ \ + struct nvkm_devinit *d = (p); \ + _nvkm_devinit_init(nv_object(d)); \ +}) +#define nvkm_devinit_fini(p,s) ({ \ + struct nvkm_devinit *d = (p); \ + _nvkm_devinit_fini(nv_object(d), (s)); \ +}) + +int nvkm_devinit_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, int, void **); +void _nvkm_devinit_dtor(struct nvkm_object *); +int _nvkm_devinit_init(struct nvkm_object *); +int _nvkm_devinit_fini(struct nvkm_object *, bool suspend); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/Kbuild new file mode 100644 index 000000000..d6be4c6c5 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/Kbuild @@ -0,0 +1,44 @@ +nvkm-y += nvkm/subdev/fb/base.o +nvkm-y += nvkm/subdev/fb/nv04.o +nvkm-y += nvkm/subdev/fb/nv10.o +nvkm-y += nvkm/subdev/fb/nv1a.o +nvkm-y += nvkm/subdev/fb/nv20.o +nvkm-y += nvkm/subdev/fb/nv25.o +nvkm-y += nvkm/subdev/fb/nv30.o +nvkm-y += nvkm/subdev/fb/nv35.o +nvkm-y += nvkm/subdev/fb/nv36.o +nvkm-y += nvkm/subdev/fb/nv40.o +nvkm-y += nvkm/subdev/fb/nv41.o +nvkm-y += nvkm/subdev/fb/nv44.o +nvkm-y += nvkm/subdev/fb/nv46.o +nvkm-y += nvkm/subdev/fb/nv47.o +nvkm-y += nvkm/subdev/fb/nv49.o +nvkm-y += nvkm/subdev/fb/nv4e.o +nvkm-y += nvkm/subdev/fb/nv50.o +nvkm-y += nvkm/subdev/fb/g84.o +nvkm-y += nvkm/subdev/fb/gt215.o +nvkm-y += nvkm/subdev/fb/mcp77.o +nvkm-y += nvkm/subdev/fb/mcp89.o +nvkm-y += nvkm/subdev/fb/gf100.o +nvkm-y += nvkm/subdev/fb/gk104.o +nvkm-y += nvkm/subdev/fb/gk20a.o +nvkm-y += nvkm/subdev/fb/gm107.o +nvkm-y += nvkm/subdev/fb/ramnv04.o +nvkm-y += nvkm/subdev/fb/ramnv10.o +nvkm-y += nvkm/subdev/fb/ramnv1a.o +nvkm-y += nvkm/subdev/fb/ramnv20.o +nvkm-y += nvkm/subdev/fb/ramnv40.o +nvkm-y += nvkm/subdev/fb/ramnv41.o +nvkm-y += nvkm/subdev/fb/ramnv44.o +nvkm-y += nvkm/subdev/fb/ramnv49.o +nvkm-y += nvkm/subdev/fb/ramnv4e.o +nvkm-y += nvkm/subdev/fb/ramnv50.o +nvkm-y += nvkm/subdev/fb/ramgt215.o +nvkm-y += nvkm/subdev/fb/rammcp77.o +nvkm-y += nvkm/subdev/fb/ramgf100.o +nvkm-y += nvkm/subdev/fb/ramgk104.o +nvkm-y += nvkm/subdev/fb/ramgm107.o +nvkm-y += nvkm/subdev/fb/sddr2.o +nvkm-y += nvkm/subdev/fb/sddr3.o +nvkm-y += nvkm/subdev/fb/gddr3.o +nvkm-y += nvkm/subdev/fb/gddr5.o diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/base.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/base.c new file mode 100644 index 000000000..61fde43da --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/base.c @@ -0,0 +1,165 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +#include +#include + +int +nvkm_fb_bios_memtype(struct nvkm_bios *bios) +{ + const u8 ramcfg = (nv_rd32(bios, 0x101000) & 0x0000003c) >> 2; + struct nvbios_M0203E M0203E; + u8 ver, hdr; + + if (nvbios_M0203Em(bios, ramcfg, &ver, &hdr, &M0203E)) { + switch (M0203E.type) { + case M0203E_TYPE_DDR2 : return NV_MEM_TYPE_DDR2; + case M0203E_TYPE_DDR3 : return NV_MEM_TYPE_DDR3; + case M0203E_TYPE_GDDR3: return NV_MEM_TYPE_GDDR3; + case M0203E_TYPE_GDDR5: return NV_MEM_TYPE_GDDR5; + default: + nv_warn(bios, "M0203E type %02x\n", M0203E.type); + return NV_MEM_TYPE_UNKNOWN; + } + } + + nv_warn(bios, "M0203E not matched!\n"); + return NV_MEM_TYPE_UNKNOWN; +} + +int +_nvkm_fb_fini(struct nvkm_object *object, bool suspend) +{ + struct nvkm_fb *pfb = (void *)object; + int ret; + + if (pfb->ram) { + ret = nv_ofuncs(pfb->ram)->fini(nv_object(pfb->ram), suspend); + if (ret && suspend) + return ret; + } + + return nvkm_subdev_fini(&pfb->base, suspend); +} + +int +_nvkm_fb_init(struct nvkm_object *object) +{ + struct nvkm_fb *pfb = (void *)object; + int ret, i; + + ret = nvkm_subdev_init(&pfb->base); + if (ret) + return ret; + + if (pfb->ram) { + ret = nv_ofuncs(pfb->ram)->init(nv_object(pfb->ram)); + if (ret) + return ret; + } + + for (i = 0; i < pfb->tile.regions; i++) + pfb->tile.prog(pfb, i, &pfb->tile.region[i]); + + return 0; +} + +void +_nvkm_fb_dtor(struct nvkm_object *object) +{ + struct nvkm_fb *pfb = (void *)object; + int i; + + for (i = 0; i < pfb->tile.regions; i++) + pfb->tile.fini(pfb, i, &pfb->tile.region[i]); + nvkm_mm_fini(&pfb->tags); + + if (pfb->ram) { + nvkm_mm_fini(&pfb->vram); + nvkm_object_ref(NULL, (struct nvkm_object **)&pfb->ram); + } + + nvkm_subdev_destroy(&pfb->base); +} + +int +nvkm_fb_create_(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, int length, void **pobject) +{ + struct nvkm_fb_impl *impl = (void *)oclass; + static const char *name[] = { + [NV_MEM_TYPE_UNKNOWN] = "unknown", + [NV_MEM_TYPE_STOLEN ] = "stolen system memory", + [NV_MEM_TYPE_SGRAM ] = "SGRAM", + [NV_MEM_TYPE_SDRAM ] = "SDRAM", + [NV_MEM_TYPE_DDR1 ] = "DDR1", + [NV_MEM_TYPE_DDR2 ] = "DDR2", + [NV_MEM_TYPE_DDR3 ] = "DDR3", + [NV_MEM_TYPE_GDDR2 ] = "GDDR2", + [NV_MEM_TYPE_GDDR3 ] = "GDDR3", + [NV_MEM_TYPE_GDDR4 ] = "GDDR4", + [NV_MEM_TYPE_GDDR5 ] = "GDDR5", + }; + struct nvkm_object *ram; + struct nvkm_fb *pfb; + int ret; + + ret = nvkm_subdev_create_(parent, engine, oclass, 0, "PFB", "fb", + length, pobject); + pfb = *pobject; + if (ret) + return ret; + + pfb->memtype_valid = impl->memtype; + + if (!impl->ram) + return 0; + + ret = nvkm_object_ctor(nv_object(pfb), NULL, impl->ram, NULL, 0, &ram); + if (ret) { + nv_fatal(pfb, "error detecting memory configuration!!\n"); + return ret; + } + + pfb->ram = (void *)ram; + + if (!nvkm_mm_initialised(&pfb->vram)) { + ret = nvkm_mm_init(&pfb->vram, 0, pfb->ram->size >> 12, 1); + if (ret) + return ret; + } + + if (!nvkm_mm_initialised(&pfb->tags)) { + ret = nvkm_mm_init(&pfb->tags, 0, pfb->ram->tags ? + ++pfb->ram->tags : 0, 1); + if (ret) + return ret; + } + + nv_info(pfb, "RAM type: %s\n", name[pfb->ram->type]); + nv_info(pfb, "RAM size: %d MiB\n", (int)(pfb->ram->size >> 20)); + nv_info(pfb, " ZCOMP: %d tags\n", pfb->ram->tags); + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/g84.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/g84.c new file mode 100644 index 000000000..6c968d1e9 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/g84.c @@ -0,0 +1,38 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv50.h" + +struct nvkm_oclass * +g84_fb_oclass = &(struct nv50_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0x84), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_fb_ctor, + .dtor = nv50_fb_dtor, + .init = nv50_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv50_fb_memtype_valid, + .base.ram = &nv50_ram_oclass, + .trap = 0x001d07ff, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr3.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr3.c new file mode 100644 index 000000000..15b462ae3 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr3.c @@ -0,0 +1,115 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + * Roy Spliet + */ +#include "priv.h" + +struct ramxlat { + int id; + u8 enc; +}; + +static inline int +ramxlat(const struct ramxlat *xlat, int id) +{ + while (xlat->id >= 0) { + if (xlat->id == id) + return xlat->enc; + xlat++; + } + return -EINVAL; +} + +static const struct ramxlat +ramgddr3_cl_lo[] = { + { 7, 7 }, { 8, 0 }, { 9, 1 }, { 10, 2 }, { 11, 3 }, + /* the below are mentioned in some, but not all, gddr3 docs */ + { 12, 4 }, { 13, 5 }, { 14, 6 }, + /* XXX: Per Samsung docs, are these used? They overlap with Qimonda */ + /* { 4, 4 }, { 5, 5 }, { 6, 6 }, { 12, 8 }, { 13, 9 }, { 14, 10 }, + * { 15, 11 }, */ + { -1 } +}; + +static const struct ramxlat +ramgddr3_cl_hi[] = { + { 10, 2 }, { 11, 3 }, { 12, 4 }, { 13, 5 }, { 14, 6 }, { 15, 7 }, + { 16, 0 }, { 17, 1 }, + { -1 } +}; + +static const struct ramxlat +ramgddr3_wr_lo[] = { + { 5, 2 }, { 7, 4 }, { 8, 5 }, { 9, 6 }, { 10, 7 }, + { 11, 0 }, + /* the below are mentioned in some, but not all, gddr3 docs */ + { 4, 1 }, { 6, 3 }, { 12, 1 }, { 13 , 2 }, + { -1 } +}; + +int +nvkm_gddr3_calc(struct nvkm_ram *ram) +{ + int CL, WR, CWL, DLL = 0, ODT = 0, hi; + + switch (ram->next->bios.timing_ver) { + case 0x10: + CWL = ram->next->bios.timing_10_CWL; + CL = ram->next->bios.timing_10_CL; + WR = ram->next->bios.timing_10_WR; + DLL = !ram->next->bios.ramcfg_10_DLLoff; + ODT = ram->next->bios.timing_10_ODT; + break; + case 0x20: + CWL = (ram->next->bios.timing[1] & 0x00000f80) >> 7; + CL = (ram->next->bios.timing[1] & 0x0000001f) >> 0; + WR = (ram->next->bios.timing[2] & 0x007f0000) >> 16; + /* XXX: Get these values from the VBIOS instead */ + DLL = !(ram->mr[1] & 0x1); + ODT = (ram->mr[1] & 0x004) >> 2 | + (ram->mr[1] & 0x040) >> 5 | + (ram->mr[1] & 0x200) >> 7; + break; + default: + return -ENOSYS; + } + + hi = ram->mr[2] & 0x1; + CL = ramxlat(hi ? ramgddr3_cl_hi : ramgddr3_cl_lo, CL); + WR = ramxlat(ramgddr3_wr_lo, WR); + if (CL < 0 || CWL < 1 || CWL > 7 || WR < 0) + return -EINVAL; + + ram->mr[0] &= ~0xf74; + ram->mr[0] |= (CWL & 0x07) << 9; + ram->mr[0] |= (CL & 0x07) << 4; + ram->mr[0] |= (CL & 0x08) >> 1; + + ram->mr[1] &= ~0x3fc; + ram->mr[1] |= (ODT & 0x03) << 2; + ram->mr[1] |= (ODT & 0x03) << 8; + ram->mr[1] |= (WR & 0x03) << 4; + ram->mr[1] |= (WR & 0x04) << 5; + ram->mr[1] |= !DLL << 6; + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr5.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr5.c new file mode 100644 index 000000000..f6f9eee1d --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr5.c @@ -0,0 +1,120 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +/* binary driver only executes this path if the condition (a) is true + * for any configuration (combination of rammap+ramcfg+timing) that + * can be reached on a given card. for now, we will execute the branch + * unconditionally in the hope that a "false everywhere" in the bios + * tables doesn't actually mean "don't touch this". + */ +#define NOTE00(a) 1 + +int +nvkm_gddr5_calc(struct nvkm_ram *ram, bool nuts) +{ + int pd, lf, xd, vh, vr, vo, l3; + int WL, CL, WR, at[2], dt, ds; + int rq = ram->freq < 1000000; /* XXX */ + + switch (ram->next->bios.ramcfg_ver) { + case 0x11: + pd = ram->next->bios.ramcfg_11_01_80; + lf = ram->next->bios.ramcfg_11_01_40; + xd = !ram->next->bios.ramcfg_11_01_20; + vh = ram->next->bios.ramcfg_11_02_10; + vr = ram->next->bios.ramcfg_11_02_04; + vo = ram->next->bios.ramcfg_11_06; + l3 = !ram->next->bios.ramcfg_11_07_02; + break; + default: + return -ENOSYS; + } + + switch (ram->next->bios.timing_ver) { + case 0x20: + WL = (ram->next->bios.timing[1] & 0x00000f80) >> 7; + CL = (ram->next->bios.timing[1] & 0x0000001f); + WR = (ram->next->bios.timing[2] & 0x007f0000) >> 16; + at[0] = ram->next->bios.timing_20_2e_c0; + at[1] = ram->next->bios.timing_20_2e_30; + dt = ram->next->bios.timing_20_2e_03; + ds = ram->next->bios.timing_20_2f_03; + break; + default: + return -ENOSYS; + } + + if (WL < 1 || WL > 7 || CL < 5 || CL > 36 || WR < 4 || WR > 35) + return -EINVAL; + CL -= 5; + WR -= 4; + + ram->mr[0] &= ~0xf7f; + ram->mr[0] |= (WR & 0x0f) << 8; + ram->mr[0] |= (CL & 0x0f) << 3; + ram->mr[0] |= (WL & 0x07) << 0; + + ram->mr[1] &= ~0x0bf; + ram->mr[1] |= (xd & 0x01) << 7; + ram->mr[1] |= (at[0] & 0x03) << 4; + ram->mr[1] |= (dt & 0x03) << 2; + ram->mr[1] |= (ds & 0x03) << 0; + + /* this seems wrong, alternate field used for the broadcast + * on nuts vs non-nuts configs.. meh, it matches for now. + */ + ram->mr1_nuts = ram->mr[1]; + if (nuts) { + ram->mr[1] &= ~0x030; + ram->mr[1] |= (at[1] & 0x03) << 4; + } + + ram->mr[3] &= ~0x020; + ram->mr[3] |= (rq & 0x01) << 5; + + ram->mr[5] &= ~0x004; + ram->mr[5] |= (l3 << 2); + + if (!vo) + vo = (ram->mr[6] & 0xff0) >> 4; + if (ram->mr[6] & 0x001) + pd = 1; /* binary driver does this.. bug? */ + ram->mr[6] &= ~0xff1; + ram->mr[6] |= (vo & 0xff) << 4; + ram->mr[6] |= (pd & 0x01) << 0; + + if (NOTE00(vr)) { + ram->mr[7] &= ~0x300; + ram->mr[7] |= (vr & 0x03) << 8; + } + ram->mr[7] &= ~0x088; + ram->mr[7] |= (vh & 0x01) << 7; + ram->mr[7] |= (lf & 0x01) << 3; + + ram->mr[8] &= ~0x003; + ram->mr[8] |= (WR & 0x10) >> 3; + ram->mr[8] |= (CL & 0x10) >> 4; + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.c new file mode 100644 index 000000000..d51aa0237 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.c @@ -0,0 +1,122 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "gf100.h" + +#include + +extern const u8 gf100_pte_storage_type_map[256]; + +bool +gf100_fb_memtype_valid(struct nvkm_fb *pfb, u32 tile_flags) +{ + u8 memtype = (tile_flags & 0x0000ff00) >> 8; + return likely((gf100_pte_storage_type_map[memtype] != 0xff)); +} + +static void +gf100_fb_intr(struct nvkm_subdev *subdev) +{ + struct gf100_fb_priv *priv = (void *)subdev; + u32 intr = nv_rd32(priv, 0x000100); + if (intr & 0x08000000) { + nv_debug(priv, "PFFB intr\n"); + intr &= ~0x08000000; + } + if (intr & 0x00002000) { + nv_debug(priv, "PBFB intr\n"); + intr &= ~0x00002000; + } +} + +int +gf100_fb_init(struct nvkm_object *object) +{ + struct gf100_fb_priv *priv = (void *)object; + int ret; + + ret = nvkm_fb_init(&priv->base); + if (ret) + return ret; + + if (priv->r100c10_page) + nv_wr32(priv, 0x100c10, priv->r100c10 >> 8); + + nv_mask(priv, 0x100c80, 0x00000001, 0x00000000); /* 128KiB lpg */ + return 0; +} + +void +gf100_fb_dtor(struct nvkm_object *object) +{ + struct nvkm_device *device = nv_device(object); + struct gf100_fb_priv *priv = (void *)object; + + if (priv->r100c10_page) { + dma_unmap_page(nv_device_base(device), priv->r100c10, PAGE_SIZE, + DMA_BIDIRECTIONAL); + __free_page(priv->r100c10_page); + } + + nvkm_fb_destroy(&priv->base); +} + +int +gf100_fb_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_device *device = nv_device(parent); + struct gf100_fb_priv *priv; + int ret; + + ret = nvkm_fb_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->r100c10_page = alloc_page(GFP_KERNEL | __GFP_ZERO); + if (priv->r100c10_page) { + priv->r100c10 = dma_map_page(nv_device_base(device), + priv->r100c10_page, 0, PAGE_SIZE, + DMA_BIDIRECTIONAL); + if (dma_mapping_error(nv_device_base(device), priv->r100c10)) + return -EFAULT; + } + + nv_subdev(priv)->intr = gf100_fb_intr; + return 0; +} + +struct nvkm_oclass * +gf100_fb_oclass = &(struct nvkm_fb_impl) { + .base.handle = NV_SUBDEV(FB, 0xc0), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_fb_ctor, + .dtor = gf100_fb_dtor, + .init = gf100_fb_init, + .fini = _nvkm_fb_fini, + }, + .memtype = gf100_fb_memtype_valid, + .ram = &gf100_ram_oclass, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.h new file mode 100644 index 000000000..0af4da259 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.h @@ -0,0 +1,28 @@ +#ifndef __NVKM_RAM_NVC0_H__ +#define __NVKM_RAM_NVC0_H__ +#include "priv.h" +#include "nv50.h" + +struct gf100_fb_priv { + struct nvkm_fb base; + struct page *r100c10_page; + dma_addr_t r100c10; +}; + +int gf100_fb_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); +void gf100_fb_dtor(struct nvkm_object *); +int gf100_fb_init(struct nvkm_object *); +bool gf100_fb_memtype_valid(struct nvkm_fb *, u32); + +#define gf100_ram_create(p,e,o,m,d) \ + gf100_ram_create_((p), (e), (o), (m), sizeof(**d), (void **)d) +int gf100_ram_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, u32, int, void **); +int gf100_ram_get(struct nvkm_fb *, u64, u32, u32, u32, + struct nvkm_mem **); +void gf100_ram_put(struct nvkm_fb *, struct nvkm_mem **); + +int gk104_ram_init(struct nvkm_object*); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk104.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk104.c new file mode 100644 index 000000000..1c0831766 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk104.c @@ -0,0 +1,37 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "gf100.h" + +struct nvkm_oclass * +gk104_fb_oclass = &(struct nvkm_fb_impl) { + .base.handle = NV_SUBDEV(FB, 0xe0), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_fb_ctor, + .dtor = gf100_fb_dtor, + .init = gf100_fb_init, + .fini = _nvkm_fb_fini, + }, + .memtype = gf100_fb_memtype_valid, + .ram = &gk104_ram_oclass, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk20a.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk20a.c new file mode 100644 index 000000000..a5d7857d3 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk20a.c @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "gf100.h" + +struct gk20a_fb_priv { + struct nvkm_fb base; +}; + +static int +gk20a_fb_init(struct nvkm_object *object) +{ + struct gk20a_fb_priv *priv = (void *)object; + int ret; + + ret = nvkm_fb_init(&priv->base); + if (ret) + return ret; + + nv_mask(priv, 0x100c80, 0x00000001, 0x00000000); /* 128KiB lpg */ + return 0; +} + +static int +gk20a_fb_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gk20a_fb_priv *priv; + int ret; + + ret = nvkm_fb_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + return 0; +} + +struct nvkm_oclass * +gk20a_fb_oclass = &(struct nvkm_fb_impl) { + .base.handle = NV_SUBDEV(FB, 0xea), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gk20a_fb_ctor, + .dtor = _nvkm_fb_dtor, + .init = gk20a_fb_init, + .fini = _nvkm_fb_fini, + }, + .memtype = gf100_fb_memtype_valid, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gm107.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gm107.c new file mode 100644 index 000000000..843f9356b --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gm107.c @@ -0,0 +1,37 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "gf100.h" + +struct nvkm_oclass * +gm107_fb_oclass = &(struct nvkm_fb_impl) { + .base.handle = NV_SUBDEV(FB, 0x07), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_fb_ctor, + .dtor = gf100_fb_dtor, + .init = gf100_fb_init, + .fini = _nvkm_fb_fini, + }, + .memtype = gf100_fb_memtype_valid, + .ram = &gm107_ram_oclass, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gt215.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gt215.c new file mode 100644 index 000000000..dd9b8a0a3 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gt215.c @@ -0,0 +1,38 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv50.h" + +struct nvkm_oclass * +gt215_fb_oclass = &(struct nv50_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0xa3), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_fb_ctor, + .dtor = nv50_fb_dtor, + .init = nv50_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv50_fb_memtype_valid, + .base.ram = >215_ram_oclass, + .trap = 0x000d0fff, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/mcp77.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/mcp77.c new file mode 100644 index 000000000..7be4a47ef --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/mcp77.c @@ -0,0 +1,38 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv50.h" + +struct nvkm_oclass * +mcp77_fb_oclass = &(struct nv50_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0xaa), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_fb_ctor, + .dtor = nv50_fb_dtor, + .init = nv50_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv50_fb_memtype_valid, + .base.ram = &mcp77_ram_oclass, + .trap = 0x001d07ff, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/mcp89.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/mcp89.c new file mode 100644 index 000000000..2d00656fa --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/mcp89.c @@ -0,0 +1,38 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv50.h" + +struct nvkm_oclass * +mcp89_fb_oclass = &(struct nv50_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0xaf), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_fb_ctor, + .dtor = nv50_fb_dtor, + .init = nv50_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv50_fb_memtype_valid, + .base.ram = &mcp77_ram_oclass, + .trap = 0x089d1fff, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv04.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv04.c new file mode 100644 index 000000000..c063dec7d --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv04.c @@ -0,0 +1,87 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv04.h" +#include "regsnv04.h" + +bool +nv04_fb_memtype_valid(struct nvkm_fb *pfb, u32 tile_flags) +{ + if (!(tile_flags & 0xff00)) + return true; + + return false; +} + +static int +nv04_fb_init(struct nvkm_object *object) +{ + struct nv04_fb_priv *priv = (void *)object; + int ret; + + ret = nvkm_fb_init(&priv->base); + if (ret) + return ret; + + /* This is what the DDX did for NV_ARCH_04, but a mmio-trace shows + * nvidia reading PFB_CFG_0, then writing back its original value. + * (which was 0x701114 in this case) + */ + nv_wr32(priv, NV04_PFB_CFG0, 0x1114); + return 0; +} + +int +nv04_fb_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv04_fb_impl *impl = (void *)oclass; + struct nv04_fb_priv *priv; + int ret; + + ret = nvkm_fb_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.tile.regions = impl->tile.regions; + priv->base.tile.init = impl->tile.init; + priv->base.tile.comp = impl->tile.comp; + priv->base.tile.fini = impl->tile.fini; + priv->base.tile.prog = impl->tile.prog; + return 0; +} + +struct nvkm_oclass * +nv04_fb_oclass = &(struct nv04_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0x04), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_fb_ctor, + .dtor = _nvkm_fb_dtor, + .init = nv04_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv04_fb_memtype_valid, + .base.ram = &nv04_ram_oclass, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv04.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv04.h new file mode 100644 index 000000000..caa0d03aa --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv04.h @@ -0,0 +1,53 @@ +#ifndef __NVKM_FB_NV04_H__ +#define __NVKM_FB_NV04_H__ +#include "priv.h" + +struct nv04_fb_priv { + struct nvkm_fb base; +}; + +int nv04_fb_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); + +struct nv04_fb_impl { + struct nvkm_fb_impl base; + struct { + int regions; + void (*init)(struct nvkm_fb *, int i, u32 addr, u32 size, + u32 pitch, u32 flags, struct nvkm_fb_tile *); + void (*comp)(struct nvkm_fb *, int i, u32 size, u32 flags, + struct nvkm_fb_tile *); + void (*fini)(struct nvkm_fb *, int i, + struct nvkm_fb_tile *); + void (*prog)(struct nvkm_fb *, int i, + struct nvkm_fb_tile *); + } tile; +}; + +void nv10_fb_tile_init(struct nvkm_fb *, int i, u32 addr, u32 size, + u32 pitch, u32 flags, struct nvkm_fb_tile *); +void nv10_fb_tile_fini(struct nvkm_fb *, int i, struct nvkm_fb_tile *); +void nv10_fb_tile_prog(struct nvkm_fb *, int, struct nvkm_fb_tile *); + +void nv20_fb_tile_init(struct nvkm_fb *, int i, u32 addr, u32 size, + u32 pitch, u32 flags, struct nvkm_fb_tile *); +void nv20_fb_tile_fini(struct nvkm_fb *, int i, struct nvkm_fb_tile *); +void nv20_fb_tile_prog(struct nvkm_fb *, int, struct nvkm_fb_tile *); + +int nv30_fb_init(struct nvkm_object *); +void nv30_fb_tile_init(struct nvkm_fb *, int i, u32 addr, u32 size, + u32 pitch, u32 flags, struct nvkm_fb_tile *); + +void nv40_fb_tile_comp(struct nvkm_fb *, int i, u32 size, u32 flags, + struct nvkm_fb_tile *); + +int nv41_fb_init(struct nvkm_object *); +void nv41_fb_tile_prog(struct nvkm_fb *, int, struct nvkm_fb_tile *); + +int nv44_fb_init(struct nvkm_object *); +void nv44_fb_tile_prog(struct nvkm_fb *, int, struct nvkm_fb_tile *); + +void nv46_fb_tile_init(struct nvkm_fb *, int i, u32 addr, u32 size, + u32 pitch, u32 flags, struct nvkm_fb_tile *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv10.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv10.c new file mode 100644 index 000000000..f3530e4a6 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv10.c @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "nv04.h" + +void +nv10_fb_tile_init(struct nvkm_fb *pfb, int i, u32 addr, u32 size, u32 pitch, + u32 flags, struct nvkm_fb_tile *tile) +{ + tile->addr = 0x80000000 | addr; + tile->limit = max(1u, addr + size) - 1; + tile->pitch = pitch; +} + +void +nv10_fb_tile_fini(struct nvkm_fb *pfb, int i, struct nvkm_fb_tile *tile) +{ + tile->addr = 0; + tile->limit = 0; + tile->pitch = 0; + tile->zcomp = 0; +} + +void +nv10_fb_tile_prog(struct nvkm_fb *pfb, int i, struct nvkm_fb_tile *tile) +{ + nv_wr32(pfb, 0x100244 + (i * 0x10), tile->limit); + nv_wr32(pfb, 0x100248 + (i * 0x10), tile->pitch); + nv_wr32(pfb, 0x100240 + (i * 0x10), tile->addr); + nv_rd32(pfb, 0x100240 + (i * 0x10)); +} + +struct nvkm_oclass * +nv10_fb_oclass = &(struct nv04_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0x10), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_fb_ctor, + .dtor = _nvkm_fb_dtor, + .init = _nvkm_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv04_fb_memtype_valid, + .base.ram = &nv10_ram_oclass, + .tile.regions = 8, + .tile.init = nv10_fb_tile_init, + .tile.fini = nv10_fb_tile_fini, + .tile.prog = nv10_fb_tile_prog, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv1a.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv1a.c new file mode 100644 index 000000000..83bcb73ca --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv1a.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "nv04.h" + +struct nvkm_oclass * +nv1a_fb_oclass = &(struct nv04_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0x1a), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_fb_ctor, + .dtor = _nvkm_fb_dtor, + .init = _nvkm_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv04_fb_memtype_valid, + .base.ram = &nv1a_ram_oclass, + .tile.regions = 8, + .tile.init = nv10_fb_tile_init, + .tile.fini = nv10_fb_tile_fini, + .tile.prog = nv10_fb_tile_prog, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv20.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv20.c new file mode 100644 index 000000000..e37084b8d --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv20.c @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "nv04.h" + +void +nv20_fb_tile_init(struct nvkm_fb *pfb, int i, u32 addr, u32 size, u32 pitch, + u32 flags, struct nvkm_fb_tile *tile) +{ + tile->addr = 0x00000001 | addr; + tile->limit = max(1u, addr + size) - 1; + tile->pitch = pitch; + if (flags & 4) { + pfb->tile.comp(pfb, i, size, flags, tile); + tile->addr |= 2; + } +} + +static void +nv20_fb_tile_comp(struct nvkm_fb *pfb, int i, u32 size, u32 flags, + struct nvkm_fb_tile *tile) +{ + u32 tiles = DIV_ROUND_UP(size, 0x40); + u32 tags = round_up(tiles / pfb->ram->parts, 0x40); + if (!nvkm_mm_head(&pfb->tags, 0, 1, tags, tags, 1, &tile->tag)) { + if (!(flags & 2)) tile->zcomp = 0x00000000; /* Z16 */ + else tile->zcomp = 0x04000000; /* Z24S8 */ + tile->zcomp |= tile->tag->offset; + tile->zcomp |= 0x80000000; /* enable */ +#ifdef __BIG_ENDIAN + tile->zcomp |= 0x08000000; +#endif + } +} + +void +nv20_fb_tile_fini(struct nvkm_fb *pfb, int i, struct nvkm_fb_tile *tile) +{ + tile->addr = 0; + tile->limit = 0; + tile->pitch = 0; + tile->zcomp = 0; + nvkm_mm_free(&pfb->tags, &tile->tag); +} + +void +nv20_fb_tile_prog(struct nvkm_fb *pfb, int i, struct nvkm_fb_tile *tile) +{ + nv_wr32(pfb, 0x100244 + (i * 0x10), tile->limit); + nv_wr32(pfb, 0x100248 + (i * 0x10), tile->pitch); + nv_wr32(pfb, 0x100240 + (i * 0x10), tile->addr); + nv_rd32(pfb, 0x100240 + (i * 0x10)); + nv_wr32(pfb, 0x100300 + (i * 0x04), tile->zcomp); +} + +struct nvkm_oclass * +nv20_fb_oclass = &(struct nv04_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0x20), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_fb_ctor, + .dtor = _nvkm_fb_dtor, + .init = _nvkm_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv04_fb_memtype_valid, + .base.ram = &nv20_ram_oclass, + .tile.regions = 8, + .tile.init = nv20_fb_tile_init, + .tile.comp = nv20_fb_tile_comp, + .tile.fini = nv20_fb_tile_fini, + .tile.prog = nv20_fb_tile_prog, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv25.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv25.c new file mode 100644 index 000000000..bc9f54f38 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv25.c @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "nv04.h" + +static void +nv25_fb_tile_comp(struct nvkm_fb *pfb, int i, u32 size, u32 flags, + struct nvkm_fb_tile *tile) +{ + u32 tiles = DIV_ROUND_UP(size, 0x40); + u32 tags = round_up(tiles / pfb->ram->parts, 0x40); + if (!nvkm_mm_head(&pfb->tags, 0, 1, tags, tags, 1, &tile->tag)) { + if (!(flags & 2)) tile->zcomp = 0x00100000; /* Z16 */ + else tile->zcomp = 0x00200000; /* Z24S8 */ + tile->zcomp |= tile->tag->offset; +#ifdef __BIG_ENDIAN + tile->zcomp |= 0x01000000; +#endif + } +} + +struct nvkm_oclass * +nv25_fb_oclass = &(struct nv04_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0x25), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_fb_ctor, + .dtor = _nvkm_fb_dtor, + .init = _nvkm_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv04_fb_memtype_valid, + .base.ram = &nv20_ram_oclass, + .tile.regions = 8, + .tile.init = nv20_fb_tile_init, + .tile.comp = nv25_fb_tile_comp, + .tile.fini = nv20_fb_tile_fini, + .tile.prog = nv20_fb_tile_prog, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv30.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv30.c new file mode 100644 index 000000000..09ebb9477 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv30.c @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "nv04.h" + +#include + +void +nv30_fb_tile_init(struct nvkm_fb *pfb, int i, u32 addr, u32 size, u32 pitch, + u32 flags, struct nvkm_fb_tile *tile) +{ + /* for performance, select alternate bank offset for zeta */ + if (!(flags & 4)) { + tile->addr = (0 << 4); + } else { + if (pfb->tile.comp) /* z compression */ + pfb->tile.comp(pfb, i, size, flags, tile); + tile->addr = (1 << 4); + } + + tile->addr |= 0x00000001; /* enable */ + tile->addr |= addr; + tile->limit = max(1u, addr + size) - 1; + tile->pitch = pitch; +} + +static void +nv30_fb_tile_comp(struct nvkm_fb *pfb, int i, u32 size, u32 flags, + struct nvkm_fb_tile *tile) +{ + u32 tiles = DIV_ROUND_UP(size, 0x40); + u32 tags = round_up(tiles / pfb->ram->parts, 0x40); + if (!nvkm_mm_head(&pfb->tags, 0, 1, tags, tags, 1, &tile->tag)) { + if (flags & 2) tile->zcomp |= 0x01000000; /* Z16 */ + else tile->zcomp |= 0x02000000; /* Z24S8 */ + tile->zcomp |= ((tile->tag->offset ) >> 6); + tile->zcomp |= ((tile->tag->offset + tags - 1) >> 6) << 12; +#ifdef __BIG_ENDIAN + tile->zcomp |= 0x10000000; +#endif + } +} + +static int +calc_bias(struct nv04_fb_priv *priv, int k, int i, int j) +{ + struct nvkm_device *device = nv_device(priv); + int b = (device->chipset > 0x30 ? + nv_rd32(priv, 0x122c + 0x10 * k + 0x4 * j) >> (4 * (i ^ 1)) : + 0) & 0xf; + + return 2 * (b & 0x8 ? b - 0x10 : b); +} + +static int +calc_ref(struct nv04_fb_priv *priv, int l, int k, int i) +{ + int j, x = 0; + + for (j = 0; j < 4; j++) { + int m = (l >> (8 * i) & 0xff) + calc_bias(priv, k, i, j); + + x |= (0x80 | clamp(m, 0, 0x1f)) << (8 * j); + } + + return x; +} + +int +nv30_fb_init(struct nvkm_object *object) +{ + struct nvkm_device *device = nv_device(object); + struct nv04_fb_priv *priv = (void *)object; + int ret, i, j; + + ret = nvkm_fb_init(&priv->base); + if (ret) + return ret; + + /* Init the memory timing regs at 0x10037c/0x1003ac */ + if (device->chipset == 0x30 || + device->chipset == 0x31 || + device->chipset == 0x35) { + /* Related to ROP count */ + int n = (device->chipset == 0x31 ? 2 : 4); + int l = nv_rd32(priv, 0x1003d0); + + for (i = 0; i < n; i++) { + for (j = 0; j < 3; j++) + nv_wr32(priv, 0x10037c + 0xc * i + 0x4 * j, + calc_ref(priv, l, 0, j)); + + for (j = 0; j < 2; j++) + nv_wr32(priv, 0x1003ac + 0x8 * i + 0x4 * j, + calc_ref(priv, l, 1, j)); + } + } + + return 0; +} + +struct nvkm_oclass * +nv30_fb_oclass = &(struct nv04_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0x30), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_fb_ctor, + .dtor = _nvkm_fb_dtor, + .init = nv30_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv04_fb_memtype_valid, + .base.ram = &nv20_ram_oclass, + .tile.regions = 8, + .tile.init = nv30_fb_tile_init, + .tile.comp = nv30_fb_tile_comp, + .tile.fini = nv20_fb_tile_fini, + .tile.prog = nv20_fb_tile_prog, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv35.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv35.c new file mode 100644 index 000000000..c01dc1839 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv35.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "nv04.h" + +static void +nv35_fb_tile_comp(struct nvkm_fb *pfb, int i, u32 size, u32 flags, + struct nvkm_fb_tile *tile) +{ + u32 tiles = DIV_ROUND_UP(size, 0x40); + u32 tags = round_up(tiles / pfb->ram->parts, 0x40); + if (!nvkm_mm_head(&pfb->tags, 0, 1, tags, tags, 1, &tile->tag)) { + if (flags & 2) tile->zcomp |= 0x04000000; /* Z16 */ + else tile->zcomp |= 0x08000000; /* Z24S8 */ + tile->zcomp |= ((tile->tag->offset ) >> 6); + tile->zcomp |= ((tile->tag->offset + tags - 1) >> 6) << 13; +#ifdef __BIG_ENDIAN + tile->zcomp |= 0x40000000; +#endif + } +} + +struct nvkm_oclass * +nv35_fb_oclass = &(struct nv04_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0x35), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_fb_ctor, + .dtor = _nvkm_fb_dtor, + .init = nv30_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv04_fb_memtype_valid, + .base.ram = &nv20_ram_oclass, + .tile.regions = 8, + .tile.init = nv30_fb_tile_init, + .tile.comp = nv35_fb_tile_comp, + .tile.fini = nv20_fb_tile_fini, + .tile.prog = nv20_fb_tile_prog, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv36.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv36.c new file mode 100644 index 000000000..cad75a1ce --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv36.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "nv04.h" + +static void +nv36_fb_tile_comp(struct nvkm_fb *pfb, int i, u32 size, u32 flags, + struct nvkm_fb_tile *tile) +{ + u32 tiles = DIV_ROUND_UP(size, 0x40); + u32 tags = round_up(tiles / pfb->ram->parts, 0x40); + if (!nvkm_mm_head(&pfb->tags, 0, 1, tags, tags, 1, &tile->tag)) { + if (flags & 2) tile->zcomp |= 0x10000000; /* Z16 */ + else tile->zcomp |= 0x20000000; /* Z24S8 */ + tile->zcomp |= ((tile->tag->offset ) >> 6); + tile->zcomp |= ((tile->tag->offset + tags - 1) >> 6) << 14; +#ifdef __BIG_ENDIAN + tile->zcomp |= 0x80000000; +#endif + } +} + +struct nvkm_oclass * +nv36_fb_oclass = &(struct nv04_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0x36), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_fb_ctor, + .dtor = _nvkm_fb_dtor, + .init = nv30_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv04_fb_memtype_valid, + .base.ram = &nv20_ram_oclass, + .tile.regions = 8, + .tile.init = nv30_fb_tile_init, + .tile.comp = nv36_fb_tile_comp, + .tile.fini = nv20_fb_tile_fini, + .tile.prog = nv20_fb_tile_prog, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv40.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv40.c new file mode 100644 index 000000000..dbe5c1910 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv40.c @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "nv04.h" + +void +nv40_fb_tile_comp(struct nvkm_fb *pfb, int i, u32 size, u32 flags, + struct nvkm_fb_tile *tile) +{ + u32 tiles = DIV_ROUND_UP(size, 0x80); + u32 tags = round_up(tiles / pfb->ram->parts, 0x100); + if ( (flags & 2) && + !nvkm_mm_head(&pfb->tags, 0, 1, tags, tags, 1, &tile->tag)) { + tile->zcomp = 0x28000000; /* Z24S8_SPLIT_GRAD */ + tile->zcomp |= ((tile->tag->offset ) >> 8); + tile->zcomp |= ((tile->tag->offset + tags - 1) >> 8) << 13; +#ifdef __BIG_ENDIAN + tile->zcomp |= 0x40000000; +#endif + } +} + +static int +nv40_fb_init(struct nvkm_object *object) +{ + struct nv04_fb_priv *priv = (void *)object; + int ret; + + ret = nvkm_fb_init(&priv->base); + if (ret) + return ret; + + nv_mask(priv, 0x10033c, 0x00008000, 0x00000000); + return 0; +} + +struct nvkm_oclass * +nv40_fb_oclass = &(struct nv04_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0x40), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_fb_ctor, + .dtor = _nvkm_fb_dtor, + .init = nv40_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv04_fb_memtype_valid, + .base.ram = &nv40_ram_oclass, + .tile.regions = 8, + .tile.init = nv30_fb_tile_init, + .tile.comp = nv40_fb_tile_comp, + .tile.fini = nv20_fb_tile_fini, + .tile.prog = nv20_fb_tile_prog, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv40.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv40.h new file mode 100644 index 000000000..602182661 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv40.h @@ -0,0 +1,14 @@ +#ifndef __NVKM_FB_NV40_H__ +#define __NVKM_FB_NV40_H__ +#include "priv.h" + +struct nv40_ram { + struct nvkm_ram base; + u32 ctrl; + u32 coef; +}; + +int nv40_ram_calc(struct nvkm_fb *, u32); +int nv40_ram_prog(struct nvkm_fb *); +void nv40_ram_tidy(struct nvkm_fb *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv41.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv41.c new file mode 100644 index 000000000..d9e1a40a2 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv41.c @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "nv04.h" + +void +nv41_fb_tile_prog(struct nvkm_fb *pfb, int i, struct nvkm_fb_tile *tile) +{ + nv_wr32(pfb, 0x100604 + (i * 0x10), tile->limit); + nv_wr32(pfb, 0x100608 + (i * 0x10), tile->pitch); + nv_wr32(pfb, 0x100600 + (i * 0x10), tile->addr); + nv_rd32(pfb, 0x100600 + (i * 0x10)); + nv_wr32(pfb, 0x100700 + (i * 0x04), tile->zcomp); +} + +int +nv41_fb_init(struct nvkm_object *object) +{ + struct nv04_fb_priv *priv = (void *)object; + int ret; + + ret = nvkm_fb_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, 0x100800, 0x00000001); + return 0; +} + +struct nvkm_oclass * +nv41_fb_oclass = &(struct nv04_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0x41), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_fb_ctor, + .dtor = _nvkm_fb_dtor, + .init = nv41_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv04_fb_memtype_valid, + .base.ram = &nv41_ram_oclass, + .tile.regions = 12, + .tile.init = nv30_fb_tile_init, + .tile.comp = nv40_fb_tile_comp, + .tile.fini = nv20_fb_tile_fini, + .tile.prog = nv41_fb_tile_prog, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv44.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv44.c new file mode 100644 index 000000000..20b97c83c --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv44.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "nv04.h" + +static void +nv44_fb_tile_init(struct nvkm_fb *pfb, int i, u32 addr, u32 size, u32 pitch, + u32 flags, struct nvkm_fb_tile *tile) +{ + tile->addr = 0x00000001; /* mode = vram */ + tile->addr |= addr; + tile->limit = max(1u, addr + size) - 1; + tile->pitch = pitch; +} + +void +nv44_fb_tile_prog(struct nvkm_fb *pfb, int i, struct nvkm_fb_tile *tile) +{ + nv_wr32(pfb, 0x100604 + (i * 0x10), tile->limit); + nv_wr32(pfb, 0x100608 + (i * 0x10), tile->pitch); + nv_wr32(pfb, 0x100600 + (i * 0x10), tile->addr); + nv_rd32(pfb, 0x100600 + (i * 0x10)); +} + +int +nv44_fb_init(struct nvkm_object *object) +{ + struct nv04_fb_priv *priv = (void *)object; + int ret; + + ret = nvkm_fb_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, 0x100850, 0x80000000); + nv_wr32(priv, 0x100800, 0x00000001); + return 0; +} + +struct nvkm_oclass * +nv44_fb_oclass = &(struct nv04_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0x44), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_fb_ctor, + .dtor = _nvkm_fb_dtor, + .init = nv44_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv04_fb_memtype_valid, + .base.ram = &nv44_ram_oclass, + .tile.regions = 12, + .tile.init = nv44_fb_tile_init, + .tile.fini = nv20_fb_tile_fini, + .tile.prog = nv44_fb_tile_prog, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv46.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv46.c new file mode 100644 index 000000000..5bfac38cd --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv46.c @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "nv04.h" + +void +nv46_fb_tile_init(struct nvkm_fb *pfb, int i, u32 addr, u32 size, u32 pitch, + u32 flags, struct nvkm_fb_tile *tile) +{ + /* for performance, select alternate bank offset for zeta */ + if (!(flags & 4)) tile->addr = (0 << 3); + else tile->addr = (1 << 3); + + tile->addr |= 0x00000001; /* mode = vram */ + tile->addr |= addr; + tile->limit = max(1u, addr + size) - 1; + tile->pitch = pitch; +} + +struct nvkm_oclass * +nv46_fb_oclass = &(struct nv04_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0x46), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_fb_ctor, + .dtor = _nvkm_fb_dtor, + .init = nv44_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv04_fb_memtype_valid, + .base.ram = &nv44_ram_oclass, + .tile.regions = 15, + .tile.init = nv46_fb_tile_init, + .tile.fini = nv20_fb_tile_fini, + .tile.prog = nv44_fb_tile_prog, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv47.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv47.c new file mode 100644 index 000000000..d3b3988d1 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv47.c @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "nv04.h" + +struct nvkm_oclass * +nv47_fb_oclass = &(struct nv04_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0x47), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_fb_ctor, + .dtor = _nvkm_fb_dtor, + .init = nv41_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv04_fb_memtype_valid, + .base.ram = &nv41_ram_oclass, + .tile.regions = 15, + .tile.init = nv30_fb_tile_init, + .tile.comp = nv40_fb_tile_comp, + .tile.fini = nv20_fb_tile_fini, + .tile.prog = nv41_fb_tile_prog, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv49.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv49.c new file mode 100644 index 000000000..236e36c50 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv49.c @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "nv04.h" + +struct nvkm_oclass * +nv49_fb_oclass = &(struct nv04_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0x49), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_fb_ctor, + .dtor = _nvkm_fb_dtor, + .init = nv41_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv04_fb_memtype_valid, + .base.ram = &nv49_ram_oclass, + .tile.regions = 15, + .tile.init = nv30_fb_tile_init, + .tile.comp = nv40_fb_tile_comp, + .tile.fini = nv20_fb_tile_fini, + .tile.prog = nv41_fb_tile_prog, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv4e.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv4e.c new file mode 100644 index 000000000..1352b6a73 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv4e.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "nv04.h" + +struct nvkm_oclass * +nv4e_fb_oclass = &(struct nv04_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0x4e), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_fb_ctor, + .dtor = _nvkm_fb_dtor, + .init = nv44_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv04_fb_memtype_valid, + .base.ram = &nv4e_ram_oclass, + .tile.regions = 12, + .tile.init = nv46_fb_tile_init, + .tile.fini = nv20_fb_tile_fini, + .tile.prog = nv44_fb_tile_prog, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.c new file mode 100644 index 000000000..0480ce52a --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.c @@ -0,0 +1,320 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv50.h" + +#include +#include +#include +#include + +int +nv50_fb_memtype[0x80] = { + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 0, 0, + 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 1, 0, 2, 0, 1, 0, 2, 0, 1, 1, 2, 2, 1, 1, 0, 0 +}; + +bool +nv50_fb_memtype_valid(struct nvkm_fb *pfb, u32 memtype) +{ + return nv50_fb_memtype[(memtype & 0xff00) >> 8] != 0; +} + +static const struct nvkm_enum vm_dispatch_subclients[] = { + { 0x00000000, "GRCTX", NULL }, + { 0x00000001, "NOTIFY", NULL }, + { 0x00000002, "QUERY", NULL }, + { 0x00000003, "COND", NULL }, + { 0x00000004, "M2M_IN", NULL }, + { 0x00000005, "M2M_OUT", NULL }, + { 0x00000006, "M2M_NOTIFY", NULL }, + {} +}; + +static const struct nvkm_enum vm_ccache_subclients[] = { + { 0x00000000, "CB", NULL }, + { 0x00000001, "TIC", NULL }, + { 0x00000002, "TSC", NULL }, + {} +}; + +static const struct nvkm_enum vm_prop_subclients[] = { + { 0x00000000, "RT0", NULL }, + { 0x00000001, "RT1", NULL }, + { 0x00000002, "RT2", NULL }, + { 0x00000003, "RT3", NULL }, + { 0x00000004, "RT4", NULL }, + { 0x00000005, "RT5", NULL }, + { 0x00000006, "RT6", NULL }, + { 0x00000007, "RT7", NULL }, + { 0x00000008, "ZETA", NULL }, + { 0x00000009, "LOCAL", NULL }, + { 0x0000000a, "GLOBAL", NULL }, + { 0x0000000b, "STACK", NULL }, + { 0x0000000c, "DST2D", NULL }, + {} +}; + +static const struct nvkm_enum vm_pfifo_subclients[] = { + { 0x00000000, "PUSHBUF", NULL }, + { 0x00000001, "SEMAPHORE", NULL }, + {} +}; + +static const struct nvkm_enum vm_bar_subclients[] = { + { 0x00000000, "FB", NULL }, + { 0x00000001, "IN", NULL }, + {} +}; + +static const struct nvkm_enum vm_client[] = { + { 0x00000000, "STRMOUT", NULL }, + { 0x00000003, "DISPATCH", vm_dispatch_subclients }, + { 0x00000004, "PFIFO_WRITE", NULL }, + { 0x00000005, "CCACHE", vm_ccache_subclients }, + { 0x00000006, "PMSPPP", NULL }, + { 0x00000007, "CLIPID", NULL }, + { 0x00000008, "PFIFO_READ", NULL }, + { 0x00000009, "VFETCH", NULL }, + { 0x0000000a, "TEXTURE", NULL }, + { 0x0000000b, "PROP", vm_prop_subclients }, + { 0x0000000c, "PVP", NULL }, + { 0x0000000d, "PBSP", NULL }, + { 0x0000000e, "PCRYPT", NULL }, + { 0x0000000f, "PCOUNTER", NULL }, + { 0x00000011, "PDAEMON", NULL }, + {} +}; + +static const struct nvkm_enum vm_engine[] = { + { 0x00000000, "PGRAPH", NULL, NVDEV_ENGINE_GR }, + { 0x00000001, "PVP", NULL, NVDEV_ENGINE_VP }, + { 0x00000004, "PEEPHOLE", NULL }, + { 0x00000005, "PFIFO", vm_pfifo_subclients, NVDEV_ENGINE_FIFO }, + { 0x00000006, "BAR", vm_bar_subclients }, + { 0x00000008, "PMSPPP", NULL, NVDEV_ENGINE_MSPPP }, + { 0x00000008, "PMPEG", NULL, NVDEV_ENGINE_MPEG }, + { 0x00000009, "PBSP", NULL, NVDEV_ENGINE_BSP }, + { 0x0000000a, "PCRYPT", NULL, NVDEV_ENGINE_CIPHER }, + { 0x0000000b, "PCOUNTER", NULL }, + { 0x0000000c, "SEMAPHORE_BG", NULL }, + { 0x0000000d, "PCE0", NULL, NVDEV_ENGINE_CE0 }, + { 0x0000000e, "PDAEMON", NULL }, + {} +}; + +static const struct nvkm_enum vm_fault[] = { + { 0x00000000, "PT_NOT_PRESENT", NULL }, + { 0x00000001, "PT_TOO_SHORT", NULL }, + { 0x00000002, "PAGE_NOT_PRESENT", NULL }, + { 0x00000003, "PAGE_SYSTEM_ONLY", NULL }, + { 0x00000004, "PAGE_READ_ONLY", NULL }, + { 0x00000006, "NULL_DMAOBJ", NULL }, + { 0x00000007, "WRONG_MEMTYPE", NULL }, + { 0x0000000b, "VRAM_LIMIT", NULL }, + { 0x0000000f, "DMAOBJ_LIMIT", NULL }, + {} +}; + +static void +nv50_fb_intr(struct nvkm_subdev *subdev) +{ + struct nvkm_device *device = nv_device(subdev); + struct nvkm_engine *engine; + struct nv50_fb_priv *priv = (void *)subdev; + const struct nvkm_enum *en, *cl; + struct nvkm_object *engctx = NULL; + u32 trap[6], idx, chan; + u8 st0, st1, st2, st3; + int i; + + idx = nv_rd32(priv, 0x100c90); + if (!(idx & 0x80000000)) + return; + idx &= 0x00ffffff; + + for (i = 0; i < 6; i++) { + nv_wr32(priv, 0x100c90, idx | i << 24); + trap[i] = nv_rd32(priv, 0x100c94); + } + nv_wr32(priv, 0x100c90, idx | 0x80000000); + + /* decode status bits into something more useful */ + if (device->chipset < 0xa3 || + device->chipset == 0xaa || device->chipset == 0xac) { + st0 = (trap[0] & 0x0000000f) >> 0; + st1 = (trap[0] & 0x000000f0) >> 4; + st2 = (trap[0] & 0x00000f00) >> 8; + st3 = (trap[0] & 0x0000f000) >> 12; + } else { + st0 = (trap[0] & 0x000000ff) >> 0; + st1 = (trap[0] & 0x0000ff00) >> 8; + st2 = (trap[0] & 0x00ff0000) >> 16; + st3 = (trap[0] & 0xff000000) >> 24; + } + chan = (trap[2] << 16) | trap[1]; + + en = nvkm_enum_find(vm_engine, st0); + + if (en && en->data2) { + const struct nvkm_enum *orig_en = en; + while (en->name && en->value == st0 && en->data2) { + engine = nvkm_engine(subdev, en->data2); + /*XXX: clean this up */ + if (!engine && en->data2 == NVDEV_ENGINE_BSP) + engine = nvkm_engine(subdev, NVDEV_ENGINE_MSVLD); + if (!engine && en->data2 == NVDEV_ENGINE_CIPHER) + engine = nvkm_engine(subdev, NVDEV_ENGINE_SEC); + if (!engine && en->data2 == NVDEV_ENGINE_VP) + engine = nvkm_engine(subdev, NVDEV_ENGINE_MSPDEC); + if (engine) { + engctx = nvkm_engctx_get(engine, chan); + if (engctx) + break; + } + en++; + } + if (!engctx) + en = orig_en; + } + + nv_error(priv, "trapped %s at 0x%02x%04x%04x on channel 0x%08x [%s] ", + (trap[5] & 0x00000100) ? "read" : "write", + trap[5] & 0xff, trap[4] & 0xffff, trap[3] & 0xffff, chan, + nvkm_client_name(engctx)); + + nvkm_engctx_put(engctx); + + if (en) + pr_cont("%s/", en->name); + else + pr_cont("%02x/", st0); + + cl = nvkm_enum_find(vm_client, st2); + if (cl) + pr_cont("%s/", cl->name); + else + pr_cont("%02x/", st2); + + if (cl && cl->data) cl = nvkm_enum_find(cl->data, st3); + else if (en && en->data) cl = nvkm_enum_find(en->data, st3); + else cl = NULL; + if (cl) + pr_cont("%s", cl->name); + else + pr_cont("%02x", st3); + + pr_cont(" reason: "); + en = nvkm_enum_find(vm_fault, st1); + if (en) + pr_cont("%s\n", en->name); + else + pr_cont("0x%08x\n", st1); +} + +int +nv50_fb_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_device *device = nv_device(parent); + struct nv50_fb_priv *priv; + int ret; + + ret = nvkm_fb_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->r100c08_page = alloc_page(GFP_KERNEL | __GFP_ZERO); + if (priv->r100c08_page) { + priv->r100c08 = dma_map_page(nv_device_base(device), + priv->r100c08_page, 0, PAGE_SIZE, + DMA_BIDIRECTIONAL); + if (dma_mapping_error(nv_device_base(device), priv->r100c08)) + return -EFAULT; + } else { + nv_warn(priv, "failed 0x100c08 page alloc\n"); + } + + nv_subdev(priv)->intr = nv50_fb_intr; + return 0; +} + +void +nv50_fb_dtor(struct nvkm_object *object) +{ + struct nvkm_device *device = nv_device(object); + struct nv50_fb_priv *priv = (void *)object; + + if (priv->r100c08_page) { + dma_unmap_page(nv_device_base(device), priv->r100c08, PAGE_SIZE, + DMA_BIDIRECTIONAL); + __free_page(priv->r100c08_page); + } + + nvkm_fb_destroy(&priv->base); +} + +int +nv50_fb_init(struct nvkm_object *object) +{ + struct nv50_fb_impl *impl = (void *)object->oclass; + struct nv50_fb_priv *priv = (void *)object; + int ret; + + ret = nvkm_fb_init(&priv->base); + if (ret) + return ret; + + /* Not a clue what this is exactly. Without pointing it at a + * scratch page, VRAM->GART blits with M2MF (as in DDX DFS) + * cause IOMMU "read from address 0" errors (rh#561267) + */ + nv_wr32(priv, 0x100c08, priv->r100c08 >> 8); + + /* This is needed to get meaningful information from 100c90 + * on traps. No idea what these values mean exactly. */ + nv_wr32(priv, 0x100c90, impl->trap); + return 0; +} + +struct nvkm_oclass * +nv50_fb_oclass = &(struct nv50_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0x50), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_fb_ctor, + .dtor = nv50_fb_dtor, + .init = nv50_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv50_fb_memtype_valid, + .base.ram = &nv50_ram_oclass, + .trap = 0x000707ff, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.h new file mode 100644 index 000000000..f3cde3f1f --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.h @@ -0,0 +1,31 @@ +#ifndef __NVKM_FB_NV50_H__ +#define __NVKM_FB_NV50_H__ +#include "priv.h" + +struct nv50_fb_priv { + struct nvkm_fb base; + struct page *r100c08_page; + dma_addr_t r100c08; +}; + +int nv50_fb_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); +void nv50_fb_dtor(struct nvkm_object *); +int nv50_fb_init(struct nvkm_object *); + +struct nv50_fb_impl { + struct nvkm_fb_impl base; + u32 trap; +}; + +#define nv50_ram_create(p,e,o,d) \ + nv50_ram_create_((p), (e), (o), sizeof(**d), (void **)d) +int nv50_ram_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, int, void **); +int nv50_ram_get(struct nvkm_fb *, u64 size, u32 align, u32 ncmin, + u32 memtype, struct nvkm_mem **); +void nv50_ram_put(struct nvkm_fb *, struct nvkm_mem **); +void __nv50_ram_put(struct nvkm_fb *, struct nvkm_mem *); +extern int nv50_fb_memtype[0x80]; +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/priv.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/priv.h new file mode 100644 index 000000000..485c4b648 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/priv.h @@ -0,0 +1,73 @@ +#ifndef __NVKM_FB_PRIV_H__ +#define __NVKM_FB_PRIV_H__ +#include +struct nvkm_bios; + +#define nvkm_ram_create(p,e,o,d) \ + nvkm_object_create_((p), (e), (o), 0, sizeof(**d), (void **)d) +#define nvkm_ram_destroy(p) \ + nvkm_object_destroy(&(p)->base) +#define nvkm_ram_init(p) \ + nvkm_object_init(&(p)->base) +#define nvkm_ram_fini(p,s) \ + nvkm_object_fini(&(p)->base, (s)) + +#define nvkm_ram_create_(p,e,o,s,d) \ + nvkm_object_create_((p), (e), (o), 0, (s), (void **)d) +#define _nvkm_ram_dtor nvkm_object_destroy +#define _nvkm_ram_init nvkm_object_init +#define _nvkm_ram_fini nvkm_object_fini + +extern struct nvkm_oclass nv04_ram_oclass; +extern struct nvkm_oclass nv10_ram_oclass; +extern struct nvkm_oclass nv1a_ram_oclass; +extern struct nvkm_oclass nv20_ram_oclass; +extern struct nvkm_oclass nv40_ram_oclass; +extern struct nvkm_oclass nv41_ram_oclass; +extern struct nvkm_oclass nv44_ram_oclass; +extern struct nvkm_oclass nv49_ram_oclass; +extern struct nvkm_oclass nv4e_ram_oclass; +extern struct nvkm_oclass nv50_ram_oclass; +extern struct nvkm_oclass gt215_ram_oclass; +extern struct nvkm_oclass mcp77_ram_oclass; +extern struct nvkm_oclass gf100_ram_oclass; +extern struct nvkm_oclass gk104_ram_oclass; +extern struct nvkm_oclass gm107_ram_oclass; + +int nvkm_sddr2_calc(struct nvkm_ram *ram); +int nvkm_sddr3_calc(struct nvkm_ram *ram); +int nvkm_gddr3_calc(struct nvkm_ram *ram); +int nvkm_gddr5_calc(struct nvkm_ram *ram, bool nuts); + +#define nvkm_fb_create(p,e,c,d) \ + nvkm_fb_create_((p), (e), (c), sizeof(**d), (void **)d) +#define nvkm_fb_destroy(p) ({ \ + struct nvkm_fb *pfb = (p); \ + _nvkm_fb_dtor(nv_object(pfb)); \ +}) +#define nvkm_fb_init(p) ({ \ + struct nvkm_fb *pfb = (p); \ + _nvkm_fb_init(nv_object(pfb)); \ +}) +#define nvkm_fb_fini(p,s) ({ \ + struct nvkm_fb *pfb = (p); \ + _nvkm_fb_fini(nv_object(pfb), (s)); \ +}) + +int nvkm_fb_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, int, void **); +void _nvkm_fb_dtor(struct nvkm_object *); +int _nvkm_fb_init(struct nvkm_object *); +int _nvkm_fb_fini(struct nvkm_object *, bool); + +struct nvkm_fb_impl { + struct nvkm_oclass base; + struct nvkm_oclass *ram; + bool (*memtype)(struct nvkm_fb *, u32); +}; + +bool nv04_fb_memtype_valid(struct nvkm_fb *, u32 memtype); +bool nv50_fb_memtype_valid(struct nvkm_fb *, u32 memtype); + +int nvkm_fb_bios_memtype(struct nvkm_bios *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramfuc.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramfuc.h new file mode 100644 index 000000000..f343682b1 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramfuc.h @@ -0,0 +1,180 @@ +#ifndef __NVKM_FBRAM_FUC_H__ +#define __NVKM_FBRAM_FUC_H__ +#include + +struct ramfuc { + struct nvkm_memx *memx; + struct nvkm_fb *pfb; + int sequence; +}; + +struct ramfuc_reg { + int sequence; + bool force; + u32 addr; + u32 stride; /* in bytes */ + u32 mask; + u32 data; +}; + +static inline struct ramfuc_reg +ramfuc_stride(u32 addr, u32 stride, u32 mask) +{ + return (struct ramfuc_reg) { + .sequence = 0, + .addr = addr, + .stride = stride, + .mask = mask, + .data = 0xdeadbeef, + }; +} + +static inline struct ramfuc_reg +ramfuc_reg2(u32 addr1, u32 addr2) +{ + return (struct ramfuc_reg) { + .sequence = 0, + .addr = addr1, + .stride = addr2 - addr1, + .mask = 0x3, + .data = 0xdeadbeef, + }; +} + +static noinline struct ramfuc_reg +ramfuc_reg(u32 addr) +{ + return (struct ramfuc_reg) { + .sequence = 0, + .addr = addr, + .stride = 0, + .mask = 0x1, + .data = 0xdeadbeef, + }; +} + +static inline int +ramfuc_init(struct ramfuc *ram, struct nvkm_fb *pfb) +{ + struct nvkm_pmu *pmu = nvkm_pmu(pfb); + int ret; + + ret = nvkm_memx_init(pmu, &ram->memx); + if (ret) + return ret; + + ram->sequence++; + ram->pfb = pfb; + return 0; +} + +static inline int +ramfuc_exec(struct ramfuc *ram, bool exec) +{ + int ret = 0; + if (ram->pfb) { + ret = nvkm_memx_fini(&ram->memx, exec); + ram->pfb = NULL; + } + return ret; +} + +static inline u32 +ramfuc_rd32(struct ramfuc *ram, struct ramfuc_reg *reg) +{ + if (reg->sequence != ram->sequence) + reg->data = nv_rd32(ram->pfb, reg->addr); + return reg->data; +} + +static inline void +ramfuc_wr32(struct ramfuc *ram, struct ramfuc_reg *reg, u32 data) +{ + unsigned int mask, off = 0; + + reg->sequence = ram->sequence; + reg->data = data; + + for (mask = reg->mask; mask > 0; mask = (mask & ~1) >> 1) { + if (mask & 1) + nvkm_memx_wr32(ram->memx, reg->addr+off, reg->data); + off += reg->stride; + } +} + +static inline void +ramfuc_nuke(struct ramfuc *ram, struct ramfuc_reg *reg) +{ + reg->force = true; +} + +static inline u32 +ramfuc_mask(struct ramfuc *ram, struct ramfuc_reg *reg, u32 mask, u32 data) +{ + u32 temp = ramfuc_rd32(ram, reg); + if (temp != ((temp & ~mask) | data) || reg->force) { + ramfuc_wr32(ram, reg, (temp & ~mask) | data); + reg->force = false; + } + return temp; +} + +static inline void +ramfuc_wait(struct ramfuc *ram, u32 addr, u32 mask, u32 data, u32 nsec) +{ + nvkm_memx_wait(ram->memx, addr, mask, data, nsec); +} + +static inline void +ramfuc_nsec(struct ramfuc *ram, u32 nsec) +{ + nvkm_memx_nsec(ram->memx, nsec); +} + +static inline void +ramfuc_wait_vblank(struct ramfuc *ram) +{ + nvkm_memx_wait_vblank(ram->memx); +} + +static inline void +ramfuc_train(struct ramfuc *ram) +{ + nvkm_memx_train(ram->memx); +} + +static inline int +ramfuc_train_result(struct nvkm_fb *pfb, u32 *result, u32 rsize) +{ + struct nvkm_pmu *pmu = nvkm_pmu(pfb); + + return nvkm_memx_train_result(pmu, result, rsize); +} + +static inline void +ramfuc_block(struct ramfuc *ram) +{ + nvkm_memx_block(ram->memx); +} + +static inline void +ramfuc_unblock(struct ramfuc *ram) +{ + nvkm_memx_unblock(ram->memx); +} + +#define ram_init(s,p) ramfuc_init(&(s)->base, (p)) +#define ram_exec(s,e) ramfuc_exec(&(s)->base, (e)) +#define ram_have(s,r) ((s)->r_##r.addr != 0x000000) +#define ram_rd32(s,r) ramfuc_rd32(&(s)->base, &(s)->r_##r) +#define ram_wr32(s,r,d) ramfuc_wr32(&(s)->base, &(s)->r_##r, (d)) +#define ram_nuke(s,r) ramfuc_nuke(&(s)->base, &(s)->r_##r) +#define ram_mask(s,r,m,d) ramfuc_mask(&(s)->base, &(s)->r_##r, (m), (d)) +#define ram_wait(s,r,m,d,n) ramfuc_wait(&(s)->base, (r), (m), (d), (n)) +#define ram_nsec(s,n) ramfuc_nsec(&(s)->base, (n)) +#define ram_wait_vblank(s) ramfuc_wait_vblank(&(s)->base) +#define ram_train(s) ramfuc_train(&(s)->base) +#define ram_train_result(s,r,l) ramfuc_train_result((s), (r), (l)) +#define ram_block(s) ramfuc_block(&(s)->base) +#define ram_unblock(s) ramfuc_unblock(&(s)->base) +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgf100.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgf100.c new file mode 100644 index 000000000..de9f39569 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgf100.c @@ -0,0 +1,731 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "gf100.h" +#include "ramfuc.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct gf100_ramfuc { + struct ramfuc base; + + struct ramfuc_reg r_0x10fe20; + struct ramfuc_reg r_0x10fe24; + struct ramfuc_reg r_0x137320; + struct ramfuc_reg r_0x137330; + + struct ramfuc_reg r_0x132000; + struct ramfuc_reg r_0x132004; + struct ramfuc_reg r_0x132100; + + struct ramfuc_reg r_0x137390; + + struct ramfuc_reg r_0x10f290; + struct ramfuc_reg r_0x10f294; + struct ramfuc_reg r_0x10f298; + struct ramfuc_reg r_0x10f29c; + struct ramfuc_reg r_0x10f2a0; + + struct ramfuc_reg r_0x10f300; + struct ramfuc_reg r_0x10f338; + struct ramfuc_reg r_0x10f340; + struct ramfuc_reg r_0x10f344; + struct ramfuc_reg r_0x10f348; + + struct ramfuc_reg r_0x10f910; + struct ramfuc_reg r_0x10f914; + + struct ramfuc_reg r_0x100b0c; + struct ramfuc_reg r_0x10f050; + struct ramfuc_reg r_0x10f090; + struct ramfuc_reg r_0x10f200; + struct ramfuc_reg r_0x10f210; + struct ramfuc_reg r_0x10f310; + struct ramfuc_reg r_0x10f314; + struct ramfuc_reg r_0x10f610; + struct ramfuc_reg r_0x10f614; + struct ramfuc_reg r_0x10f800; + struct ramfuc_reg r_0x10f808; + struct ramfuc_reg r_0x10f824; + struct ramfuc_reg r_0x10f830; + struct ramfuc_reg r_0x10f988; + struct ramfuc_reg r_0x10f98c; + struct ramfuc_reg r_0x10f990; + struct ramfuc_reg r_0x10f998; + struct ramfuc_reg r_0x10f9b0; + struct ramfuc_reg r_0x10f9b4; + struct ramfuc_reg r_0x10fb04; + struct ramfuc_reg r_0x10fb08; + struct ramfuc_reg r_0x137300; + struct ramfuc_reg r_0x137310; + struct ramfuc_reg r_0x137360; + struct ramfuc_reg r_0x1373ec; + struct ramfuc_reg r_0x1373f0; + struct ramfuc_reg r_0x1373f8; + + struct ramfuc_reg r_0x61c140; + struct ramfuc_reg r_0x611200; + + struct ramfuc_reg r_0x13d8f4; +}; + +struct gf100_ram { + struct nvkm_ram base; + struct gf100_ramfuc fuc; + struct nvbios_pll refpll; + struct nvbios_pll mempll; +}; + +static void +gf100_ram_train(struct gf100_ramfuc *fuc, u32 magic) +{ + struct gf100_ram *ram = container_of(fuc, typeof(*ram), fuc); + struct nvkm_fb *pfb = nvkm_fb(ram); + u32 part = nv_rd32(pfb, 0x022438), i; + u32 mask = nv_rd32(pfb, 0x022554); + u32 addr = 0x110974; + + ram_wr32(fuc, 0x10f910, magic); + ram_wr32(fuc, 0x10f914, magic); + + for (i = 0; (magic & 0x80000000) && i < part; addr += 0x1000, i++) { + if (mask & (1 << i)) + continue; + ram_wait(fuc, addr, 0x0000000f, 0x00000000, 500000); + } +} + +static int +gf100_ram_calc(struct nvkm_fb *pfb, u32 freq) +{ + struct nvkm_clk *clk = nvkm_clk(pfb); + struct nvkm_bios *bios = nvkm_bios(pfb); + struct gf100_ram *ram = (void *)pfb->ram; + struct gf100_ramfuc *fuc = &ram->fuc; + struct nvbios_ramcfg cfg; + u8 ver, cnt, len, strap; + struct { + u32 data; + u8 size; + } rammap, ramcfg, timing; + int ref, div, out; + int from, mode; + int N1, M1, P; + int ret; + + /* lookup memory config data relevant to the target frequency */ + rammap.data = nvbios_rammapEm(bios, freq / 1000, &ver, &rammap.size, + &cnt, &ramcfg.size, &cfg); + if (!rammap.data || ver != 0x10 || rammap.size < 0x0e) { + nv_error(pfb, "invalid/missing rammap entry\n"); + return -EINVAL; + } + + /* locate specific data set for the attached memory */ + strap = nvbios_ramcfg_index(nv_subdev(pfb)); + if (strap >= cnt) { + nv_error(pfb, "invalid ramcfg strap\n"); + return -EINVAL; + } + + ramcfg.data = rammap.data + rammap.size + (strap * ramcfg.size); + if (!ramcfg.data || ver != 0x10 || ramcfg.size < 0x0e) { + nv_error(pfb, "invalid/missing ramcfg entry\n"); + return -EINVAL; + } + + /* lookup memory timings, if bios says they're present */ + strap = nv_ro08(bios, ramcfg.data + 0x01); + if (strap != 0xff) { + timing.data = nvbios_timingEe(bios, strap, &ver, &timing.size, + &cnt, &len); + if (!timing.data || ver != 0x10 || timing.size < 0x19) { + nv_error(pfb, "invalid/missing timing entry\n"); + return -EINVAL; + } + } else { + timing.data = 0; + } + + ret = ram_init(fuc, pfb); + if (ret) + return ret; + + /* determine current mclk configuration */ + from = !!(ram_rd32(fuc, 0x1373f0) & 0x00000002); /*XXX: ok? */ + + /* determine target mclk configuration */ + if (!(ram_rd32(fuc, 0x137300) & 0x00000100)) + ref = clk->read(clk, nv_clk_src_sppll0); + else + ref = clk->read(clk, nv_clk_src_sppll1); + div = max(min((ref * 2) / freq, (u32)65), (u32)2) - 2; + out = (ref * 2) / (div + 2); + mode = freq != out; + + ram_mask(fuc, 0x137360, 0x00000002, 0x00000000); + + if ((ram_rd32(fuc, 0x132000) & 0x00000002) || 0 /*XXX*/) { + ram_nuke(fuc, 0x132000); + ram_mask(fuc, 0x132000, 0x00000002, 0x00000002); + ram_mask(fuc, 0x132000, 0x00000002, 0x00000000); + } + + if (mode == 1) { + ram_nuke(fuc, 0x10fe20); + ram_mask(fuc, 0x10fe20, 0x00000002, 0x00000002); + ram_mask(fuc, 0x10fe20, 0x00000002, 0x00000000); + } + +// 0x00020034 // 0x0000000a + ram_wr32(fuc, 0x132100, 0x00000001); + + if (mode == 1 && from == 0) { + /* calculate refpll */ + ret = gt215_pll_calc(nv_subdev(pfb), &ram->refpll, + ram->mempll.refclk, &N1, NULL, &M1, &P); + if (ret <= 0) { + nv_error(pfb, "unable to calc refpll\n"); + return ret ? ret : -ERANGE; + } + + ram_wr32(fuc, 0x10fe20, 0x20010000); + ram_wr32(fuc, 0x137320, 0x00000003); + ram_wr32(fuc, 0x137330, 0x81200006); + ram_wr32(fuc, 0x10fe24, (P << 16) | (N1 << 8) | M1); + ram_wr32(fuc, 0x10fe20, 0x20010001); + ram_wait(fuc, 0x137390, 0x00020000, 0x00020000, 64000); + + /* calculate mempll */ + ret = gt215_pll_calc(nv_subdev(pfb), &ram->mempll, freq, + &N1, NULL, &M1, &P); + if (ret <= 0) { + nv_error(pfb, "unable to calc refpll\n"); + return ret ? ret : -ERANGE; + } + + ram_wr32(fuc, 0x10fe20, 0x20010005); + ram_wr32(fuc, 0x132004, (P << 16) | (N1 << 8) | M1); + ram_wr32(fuc, 0x132000, 0x18010101); + ram_wait(fuc, 0x137390, 0x00000002, 0x00000002, 64000); + } else + if (mode == 0) { + ram_wr32(fuc, 0x137300, 0x00000003); + } + + if (from == 0) { + ram_nuke(fuc, 0x10fb04); + ram_mask(fuc, 0x10fb04, 0x0000ffff, 0x00000000); + ram_nuke(fuc, 0x10fb08); + ram_mask(fuc, 0x10fb08, 0x0000ffff, 0x00000000); + ram_wr32(fuc, 0x10f988, 0x2004ff00); + ram_wr32(fuc, 0x10f98c, 0x003fc040); + ram_wr32(fuc, 0x10f990, 0x20012001); + ram_wr32(fuc, 0x10f998, 0x00011a00); + ram_wr32(fuc, 0x13d8f4, 0x00000000); + } else { + ram_wr32(fuc, 0x10f988, 0x20010000); + ram_wr32(fuc, 0x10f98c, 0x00000000); + ram_wr32(fuc, 0x10f990, 0x20012001); + ram_wr32(fuc, 0x10f998, 0x00010a00); + } + + if (from == 0) { +// 0x00020039 // 0x000000ba + } + +// 0x0002003a // 0x00000002 + ram_wr32(fuc, 0x100b0c, 0x00080012); +// 0x00030014 // 0x00000000 // 0x02b5f070 +// 0x00030014 // 0x00010000 // 0x02b5f070 + ram_wr32(fuc, 0x611200, 0x00003300); +// 0x00020034 // 0x0000000a +// 0x00030020 // 0x00000001 // 0x00000000 + + ram_mask(fuc, 0x10f200, 0x00000800, 0x00000000); + ram_wr32(fuc, 0x10f210, 0x00000000); + ram_nsec(fuc, 1000); + if (mode == 0) + gf100_ram_train(fuc, 0x000c1001); + ram_wr32(fuc, 0x10f310, 0x00000001); + ram_nsec(fuc, 1000); + ram_wr32(fuc, 0x10f090, 0x00000061); + ram_wr32(fuc, 0x10f090, 0xc000007f); + ram_nsec(fuc, 1000); + + if (from == 0) { + ram_wr32(fuc, 0x10f824, 0x00007fd4); + } else { + ram_wr32(fuc, 0x1373ec, 0x00020404); + } + + if (mode == 0) { + ram_mask(fuc, 0x10f808, 0x00080000, 0x00000000); + ram_mask(fuc, 0x10f200, 0x00008000, 0x00008000); + ram_wr32(fuc, 0x10f830, 0x41500010); + ram_mask(fuc, 0x10f830, 0x01000000, 0x00000000); + ram_mask(fuc, 0x132100, 0x00000100, 0x00000100); + ram_wr32(fuc, 0x10f050, 0xff000090); + ram_wr32(fuc, 0x1373ec, 0x00020f0f); + ram_wr32(fuc, 0x1373f0, 0x00000003); + ram_wr32(fuc, 0x137310, 0x81201616); + ram_wr32(fuc, 0x132100, 0x00000001); +// 0x00020039 // 0x000000ba + ram_wr32(fuc, 0x10f830, 0x00300017); + ram_wr32(fuc, 0x1373f0, 0x00000001); + ram_wr32(fuc, 0x10f824, 0x00007e77); + ram_wr32(fuc, 0x132000, 0x18030001); + ram_wr32(fuc, 0x10f090, 0x4000007e); + ram_nsec(fuc, 2000); + ram_wr32(fuc, 0x10f314, 0x00000001); + ram_wr32(fuc, 0x10f210, 0x80000000); + ram_wr32(fuc, 0x10f338, 0x00300220); + ram_wr32(fuc, 0x10f300, 0x0000011d); + ram_nsec(fuc, 1000); + ram_wr32(fuc, 0x10f290, 0x02060505); + ram_wr32(fuc, 0x10f294, 0x34208288); + ram_wr32(fuc, 0x10f298, 0x44050411); + ram_wr32(fuc, 0x10f29c, 0x0000114c); + ram_wr32(fuc, 0x10f2a0, 0x42e10069); + ram_wr32(fuc, 0x10f614, 0x40044f77); + ram_wr32(fuc, 0x10f610, 0x40044f77); + ram_wr32(fuc, 0x10f344, 0x00600009); + ram_nsec(fuc, 1000); + ram_wr32(fuc, 0x10f348, 0x00700008); + ram_wr32(fuc, 0x61c140, 0x19240000); + ram_wr32(fuc, 0x10f830, 0x00300017); + gf100_ram_train(fuc, 0x80021001); + gf100_ram_train(fuc, 0x80081001); + ram_wr32(fuc, 0x10f340, 0x00500004); + ram_nsec(fuc, 1000); + ram_wr32(fuc, 0x10f830, 0x01300017); + ram_wr32(fuc, 0x10f830, 0x00300017); +// 0x00030020 // 0x00000000 // 0x00000000 +// 0x00020034 // 0x0000000b + ram_wr32(fuc, 0x100b0c, 0x00080028); + ram_wr32(fuc, 0x611200, 0x00003330); + } else { + ram_wr32(fuc, 0x10f800, 0x00001800); + ram_wr32(fuc, 0x13d8f4, 0x00000000); + ram_wr32(fuc, 0x1373ec, 0x00020404); + ram_wr32(fuc, 0x1373f0, 0x00000003); + ram_wr32(fuc, 0x10f830, 0x40700010); + ram_wr32(fuc, 0x10f830, 0x40500010); + ram_wr32(fuc, 0x13d8f4, 0x00000000); + ram_wr32(fuc, 0x1373f8, 0x00000000); + ram_wr32(fuc, 0x132100, 0x00000101); + ram_wr32(fuc, 0x137310, 0x89201616); + ram_wr32(fuc, 0x10f050, 0xff000090); + ram_wr32(fuc, 0x1373ec, 0x00030404); + ram_wr32(fuc, 0x1373f0, 0x00000002); + // 0x00020039 // 0x00000011 + ram_wr32(fuc, 0x132100, 0x00000001); + ram_wr32(fuc, 0x1373f8, 0x00002000); + ram_nsec(fuc, 2000); + ram_wr32(fuc, 0x10f808, 0x7aaa0050); + ram_wr32(fuc, 0x10f830, 0x00500010); + ram_wr32(fuc, 0x10f200, 0x00ce1000); + ram_wr32(fuc, 0x10f090, 0x4000007e); + ram_nsec(fuc, 2000); + ram_wr32(fuc, 0x10f314, 0x00000001); + ram_wr32(fuc, 0x10f210, 0x80000000); + ram_wr32(fuc, 0x10f338, 0x00300200); + ram_wr32(fuc, 0x10f300, 0x0000084d); + ram_nsec(fuc, 1000); + ram_wr32(fuc, 0x10f290, 0x0b343825); + ram_wr32(fuc, 0x10f294, 0x3483028e); + ram_wr32(fuc, 0x10f298, 0x440c0600); + ram_wr32(fuc, 0x10f29c, 0x0000214c); + ram_wr32(fuc, 0x10f2a0, 0x42e20069); + ram_wr32(fuc, 0x10f200, 0x00ce0000); + ram_wr32(fuc, 0x10f614, 0x60044e77); + ram_wr32(fuc, 0x10f610, 0x60044e77); + ram_wr32(fuc, 0x10f340, 0x00500000); + ram_nsec(fuc, 1000); + ram_wr32(fuc, 0x10f344, 0x00600228); + ram_nsec(fuc, 1000); + ram_wr32(fuc, 0x10f348, 0x00700000); + ram_wr32(fuc, 0x13d8f4, 0x00000000); + ram_wr32(fuc, 0x61c140, 0x09a40000); + + gf100_ram_train(fuc, 0x800e1008); + + ram_nsec(fuc, 1000); + ram_wr32(fuc, 0x10f800, 0x00001804); + // 0x00030020 // 0x00000000 // 0x00000000 + // 0x00020034 // 0x0000000b + ram_wr32(fuc, 0x13d8f4, 0x00000000); + ram_wr32(fuc, 0x100b0c, 0x00080028); + ram_wr32(fuc, 0x611200, 0x00003330); + ram_nsec(fuc, 100000); + ram_wr32(fuc, 0x10f9b0, 0x05313f41); + ram_wr32(fuc, 0x10f9b4, 0x00002f50); + + gf100_ram_train(fuc, 0x010c1001); + } + + ram_mask(fuc, 0x10f200, 0x00000800, 0x00000800); +// 0x00020016 // 0x00000000 + + if (mode == 0) + ram_mask(fuc, 0x132000, 0x00000001, 0x00000000); + + return 0; +} + +static int +gf100_ram_prog(struct nvkm_fb *pfb) +{ + struct nvkm_device *device = nv_device(pfb); + struct gf100_ram *ram = (void *)pfb->ram; + struct gf100_ramfuc *fuc = &ram->fuc; + ram_exec(fuc, nvkm_boolopt(device->cfgopt, "NvMemExec", true)); + return 0; +} + +static void +gf100_ram_tidy(struct nvkm_fb *pfb) +{ + struct gf100_ram *ram = (void *)pfb->ram; + struct gf100_ramfuc *fuc = &ram->fuc; + ram_exec(fuc, false); +} + +extern const u8 gf100_pte_storage_type_map[256]; + +void +gf100_ram_put(struct nvkm_fb *pfb, struct nvkm_mem **pmem) +{ + struct nvkm_ltc *ltc = nvkm_ltc(pfb); + struct nvkm_mem *mem = *pmem; + + *pmem = NULL; + if (unlikely(mem == NULL)) + return; + + mutex_lock(&pfb->base.mutex); + if (mem->tag) + ltc->tags_free(ltc, &mem->tag); + __nv50_ram_put(pfb, mem); + mutex_unlock(&pfb->base.mutex); + + kfree(mem); +} + +int +gf100_ram_get(struct nvkm_fb *pfb, u64 size, u32 align, u32 ncmin, + u32 memtype, struct nvkm_mem **pmem) +{ + struct nvkm_mm *mm = &pfb->vram; + struct nvkm_mm_node *r; + struct nvkm_mem *mem; + int type = (memtype & 0x0ff); + int back = (memtype & 0x800); + const bool comp = gf100_pte_storage_type_map[type] != type; + int ret; + + size >>= 12; + align >>= 12; + ncmin >>= 12; + if (!ncmin) + ncmin = size; + + mem = kzalloc(sizeof(*mem), GFP_KERNEL); + if (!mem) + return -ENOMEM; + + INIT_LIST_HEAD(&mem->regions); + mem->size = size; + + mutex_lock(&pfb->base.mutex); + if (comp) { + struct nvkm_ltc *ltc = nvkm_ltc(pfb); + + /* compression only works with lpages */ + if (align == (1 << (17 - 12))) { + int n = size >> 5; + ltc->tags_alloc(ltc, n, &mem->tag); + } + + if (unlikely(!mem->tag)) + type = gf100_pte_storage_type_map[type]; + } + mem->memtype = type; + + do { + if (back) + ret = nvkm_mm_tail(mm, 0, 1, size, ncmin, align, &r); + else + ret = nvkm_mm_head(mm, 0, 1, size, ncmin, align, &r); + if (ret) { + mutex_unlock(&pfb->base.mutex); + pfb->ram->put(pfb, &mem); + return ret; + } + + list_add_tail(&r->rl_entry, &mem->regions); + size -= r->length; + } while (size); + mutex_unlock(&pfb->base.mutex); + + r = list_first_entry(&mem->regions, struct nvkm_mm_node, rl_entry); + mem->offset = (u64)r->offset << 12; + *pmem = mem; + return 0; +} + +int +gf100_ram_create_(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, u32 maskaddr, int size, + void **pobject) +{ + struct nvkm_fb *pfb = nvkm_fb(parent); + struct nvkm_bios *bios = nvkm_bios(pfb); + struct nvkm_ram *ram; + const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */ + const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */ + u32 parts = nv_rd32(pfb, 0x022438); + u32 pmask = nv_rd32(pfb, maskaddr); + u32 bsize = nv_rd32(pfb, 0x10f20c); + u32 offset, length; + bool uniform = true; + int ret, part; + + ret = nvkm_ram_create_(parent, engine, oclass, size, pobject); + ram = *pobject; + if (ret) + return ret; + + nv_debug(pfb, "0x100800: 0x%08x\n", nv_rd32(pfb, 0x100800)); + nv_debug(pfb, "parts 0x%08x mask 0x%08x\n", parts, pmask); + + ram->type = nvkm_fb_bios_memtype(bios); + ram->ranks = (nv_rd32(pfb, 0x10f200) & 0x00000004) ? 2 : 1; + + /* read amount of vram attached to each memory controller */ + for (part = 0; part < parts; part++) { + if (!(pmask & (1 << part))) { + u32 psize = nv_rd32(pfb, 0x11020c + (part * 0x1000)); + if (psize != bsize) { + if (psize < bsize) + bsize = psize; + uniform = false; + } + + nv_debug(pfb, "%d: mem_amount 0x%08x\n", part, psize); + ram->size += (u64)psize << 20; + } + } + + /* if all controllers have the same amount attached, there's no holes */ + if (uniform) { + offset = rsvd_head; + length = (ram->size >> 12) - rsvd_head - rsvd_tail; + ret = nvkm_mm_init(&pfb->vram, offset, length, 1); + } else { + /* otherwise, address lowest common amount from 0GiB */ + ret = nvkm_mm_init(&pfb->vram, rsvd_head, + (bsize << 8) * parts - rsvd_head, 1); + if (ret) + return ret; + + /* and the rest starting from (8GiB + common_size) */ + offset = (0x0200000000ULL >> 12) + (bsize << 8); + length = (ram->size >> 12) - ((bsize * parts) << 8) - rsvd_tail; + + ret = nvkm_mm_init(&pfb->vram, offset, length, 1); + if (ret) + nvkm_mm_fini(&pfb->vram); + } + + if (ret) + return ret; + + ram->get = gf100_ram_get; + ram->put = gf100_ram_put; + return 0; +} + +static int +gf100_ram_init(struct nvkm_object *object) +{ + struct nvkm_fb *pfb = (void *)object->parent; + struct gf100_ram *ram = (void *)object; + int ret, i; + + ret = nvkm_ram_init(&ram->base); + if (ret) + return ret; + + /* prepare for ddr link training, and load training patterns */ + switch (ram->base.type) { + case NV_MEM_TYPE_GDDR5: { + static const u8 train0[] = { + 0x00, 0xff, 0x55, 0xaa, 0x33, 0xcc, + 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, + }; + static const u32 train1[] = { + 0x00000000, 0xffffffff, + 0x55555555, 0xaaaaaaaa, + 0x33333333, 0xcccccccc, + 0xf0f0f0f0, 0x0f0f0f0f, + 0x00ff00ff, 0xff00ff00, + 0x0000ffff, 0xffff0000, + }; + + for (i = 0; i < 0x30; i++) { + nv_wr32(pfb, 0x10f968, 0x00000000 | (i << 8)); + nv_wr32(pfb, 0x10f96c, 0x00000000 | (i << 8)); + nv_wr32(pfb, 0x10f920, 0x00000100 | train0[i % 12]); + nv_wr32(pfb, 0x10f924, 0x00000100 | train0[i % 12]); + nv_wr32(pfb, 0x10f918, train1[i % 12]); + nv_wr32(pfb, 0x10f91c, train1[i % 12]); + nv_wr32(pfb, 0x10f920, 0x00000000 | train0[i % 12]); + nv_wr32(pfb, 0x10f924, 0x00000000 | train0[i % 12]); + nv_wr32(pfb, 0x10f918, train1[i % 12]); + nv_wr32(pfb, 0x10f91c, train1[i % 12]); + } + } break; + default: + break; + } + + return 0; +} + +static int +gf100_ram_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_bios *bios = nvkm_bios(parent); + struct gf100_ram *ram; + int ret; + + ret = gf100_ram_create(parent, engine, oclass, 0x022554, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + ret = nvbios_pll_parse(bios, 0x0c, &ram->refpll); + if (ret) { + nv_error(ram, "mclk refpll data not found\n"); + return ret; + } + + ret = nvbios_pll_parse(bios, 0x04, &ram->mempll); + if (ret) { + nv_error(ram, "mclk pll data not found\n"); + return ret; + } + + switch (ram->base.type) { + case NV_MEM_TYPE_GDDR5: + ram->base.calc = gf100_ram_calc; + ram->base.prog = gf100_ram_prog; + ram->base.tidy = gf100_ram_tidy; + break; + default: + nv_warn(ram, "reclocking of this ram type unsupported\n"); + return 0; + } + + ram->fuc.r_0x10fe20 = ramfuc_reg(0x10fe20); + ram->fuc.r_0x10fe24 = ramfuc_reg(0x10fe24); + ram->fuc.r_0x137320 = ramfuc_reg(0x137320); + ram->fuc.r_0x137330 = ramfuc_reg(0x137330); + + ram->fuc.r_0x132000 = ramfuc_reg(0x132000); + ram->fuc.r_0x132004 = ramfuc_reg(0x132004); + ram->fuc.r_0x132100 = ramfuc_reg(0x132100); + + ram->fuc.r_0x137390 = ramfuc_reg(0x137390); + + ram->fuc.r_0x10f290 = ramfuc_reg(0x10f290); + ram->fuc.r_0x10f294 = ramfuc_reg(0x10f294); + ram->fuc.r_0x10f298 = ramfuc_reg(0x10f298); + ram->fuc.r_0x10f29c = ramfuc_reg(0x10f29c); + ram->fuc.r_0x10f2a0 = ramfuc_reg(0x10f2a0); + + ram->fuc.r_0x10f300 = ramfuc_reg(0x10f300); + ram->fuc.r_0x10f338 = ramfuc_reg(0x10f338); + ram->fuc.r_0x10f340 = ramfuc_reg(0x10f340); + ram->fuc.r_0x10f344 = ramfuc_reg(0x10f344); + ram->fuc.r_0x10f348 = ramfuc_reg(0x10f348); + + ram->fuc.r_0x10f910 = ramfuc_reg(0x10f910); + ram->fuc.r_0x10f914 = ramfuc_reg(0x10f914); + + ram->fuc.r_0x100b0c = ramfuc_reg(0x100b0c); + ram->fuc.r_0x10f050 = ramfuc_reg(0x10f050); + ram->fuc.r_0x10f090 = ramfuc_reg(0x10f090); + ram->fuc.r_0x10f200 = ramfuc_reg(0x10f200); + ram->fuc.r_0x10f210 = ramfuc_reg(0x10f210); + ram->fuc.r_0x10f310 = ramfuc_reg(0x10f310); + ram->fuc.r_0x10f314 = ramfuc_reg(0x10f314); + ram->fuc.r_0x10f610 = ramfuc_reg(0x10f610); + ram->fuc.r_0x10f614 = ramfuc_reg(0x10f614); + ram->fuc.r_0x10f800 = ramfuc_reg(0x10f800); + ram->fuc.r_0x10f808 = ramfuc_reg(0x10f808); + ram->fuc.r_0x10f824 = ramfuc_reg(0x10f824); + ram->fuc.r_0x10f830 = ramfuc_reg(0x10f830); + ram->fuc.r_0x10f988 = ramfuc_reg(0x10f988); + ram->fuc.r_0x10f98c = ramfuc_reg(0x10f98c); + ram->fuc.r_0x10f990 = ramfuc_reg(0x10f990); + ram->fuc.r_0x10f998 = ramfuc_reg(0x10f998); + ram->fuc.r_0x10f9b0 = ramfuc_reg(0x10f9b0); + ram->fuc.r_0x10f9b4 = ramfuc_reg(0x10f9b4); + ram->fuc.r_0x10fb04 = ramfuc_reg(0x10fb04); + ram->fuc.r_0x10fb08 = ramfuc_reg(0x10fb08); + ram->fuc.r_0x137310 = ramfuc_reg(0x137300); + ram->fuc.r_0x137310 = ramfuc_reg(0x137310); + ram->fuc.r_0x137360 = ramfuc_reg(0x137360); + ram->fuc.r_0x1373ec = ramfuc_reg(0x1373ec); + ram->fuc.r_0x1373f0 = ramfuc_reg(0x1373f0); + ram->fuc.r_0x1373f8 = ramfuc_reg(0x1373f8); + + ram->fuc.r_0x61c140 = ramfuc_reg(0x61c140); + ram->fuc.r_0x611200 = ramfuc_reg(0x611200); + + ram->fuc.r_0x13d8f4 = ramfuc_reg(0x13d8f4); + return 0; +} + +struct nvkm_oclass +gf100_ram_oclass = { + .handle = 0, + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_ram_ctor, + .dtor = _nvkm_ram_dtor, + .init = gf100_ram_init, + .fini = _nvkm_ram_fini, + } +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgk104.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgk104.c new file mode 100644 index 000000000..1ef15c3e6 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgk104.c @@ -0,0 +1,1639 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "ramfuc.h" +#include "gf100.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct gk104_ramfuc { + struct ramfuc base; + + struct nvbios_pll refpll; + struct nvbios_pll mempll; + + struct ramfuc_reg r_gpioMV; + u32 r_funcMV[2]; + struct ramfuc_reg r_gpio2E; + u32 r_func2E[2]; + struct ramfuc_reg r_gpiotrig; + + struct ramfuc_reg r_0x132020; + struct ramfuc_reg r_0x132028; + struct ramfuc_reg r_0x132024; + struct ramfuc_reg r_0x132030; + struct ramfuc_reg r_0x132034; + struct ramfuc_reg r_0x132000; + struct ramfuc_reg r_0x132004; + struct ramfuc_reg r_0x132040; + + struct ramfuc_reg r_0x10f248; + struct ramfuc_reg r_0x10f290; + struct ramfuc_reg r_0x10f294; + struct ramfuc_reg r_0x10f298; + struct ramfuc_reg r_0x10f29c; + struct ramfuc_reg r_0x10f2a0; + struct ramfuc_reg r_0x10f2a4; + struct ramfuc_reg r_0x10f2a8; + struct ramfuc_reg r_0x10f2ac; + struct ramfuc_reg r_0x10f2cc; + struct ramfuc_reg r_0x10f2e8; + struct ramfuc_reg r_0x10f250; + struct ramfuc_reg r_0x10f24c; + struct ramfuc_reg r_0x10fec4; + struct ramfuc_reg r_0x10fec8; + struct ramfuc_reg r_0x10f604; + struct ramfuc_reg r_0x10f614; + struct ramfuc_reg r_0x10f610; + struct ramfuc_reg r_0x100770; + struct ramfuc_reg r_0x100778; + struct ramfuc_reg r_0x10f224; + + struct ramfuc_reg r_0x10f870; + struct ramfuc_reg r_0x10f698; + struct ramfuc_reg r_0x10f694; + struct ramfuc_reg r_0x10f6b8; + struct ramfuc_reg r_0x10f808; + struct ramfuc_reg r_0x10f670; + struct ramfuc_reg r_0x10f60c; + struct ramfuc_reg r_0x10f830; + struct ramfuc_reg r_0x1373ec; + struct ramfuc_reg r_0x10f800; + struct ramfuc_reg r_0x10f82c; + + struct ramfuc_reg r_0x10f978; + struct ramfuc_reg r_0x10f910; + struct ramfuc_reg r_0x10f914; + + struct ramfuc_reg r_mr[16]; /* MR0 - MR8, MR15 */ + + struct ramfuc_reg r_0x62c000; + + struct ramfuc_reg r_0x10f200; + + struct ramfuc_reg r_0x10f210; + struct ramfuc_reg r_0x10f310; + struct ramfuc_reg r_0x10f314; + struct ramfuc_reg r_0x10f318; + struct ramfuc_reg r_0x10f090; + struct ramfuc_reg r_0x10f69c; + struct ramfuc_reg r_0x10f824; + struct ramfuc_reg r_0x1373f0; + struct ramfuc_reg r_0x1373f4; + struct ramfuc_reg r_0x137320; + struct ramfuc_reg r_0x10f65c; + struct ramfuc_reg r_0x10f6bc; + struct ramfuc_reg r_0x100710; + struct ramfuc_reg r_0x100750; +}; + +struct gk104_ram { + struct nvkm_ram base; + struct gk104_ramfuc fuc; + + struct list_head cfg; + u32 parts; + u32 pmask; + u32 pnuts; + + struct nvbios_ramcfg diff; + int from; + int mode; + int N1, fN1, M1, P1; + int N2, M2, P2; +}; + +/******************************************************************************* + * GDDR5 + ******************************************************************************/ +static void +gk104_ram_train(struct gk104_ramfuc *fuc, u32 mask, u32 data) +{ + struct gk104_ram *ram = container_of(fuc, typeof(*ram), fuc); + u32 addr = 0x110974, i; + + ram_mask(fuc, 0x10f910, mask, data); + ram_mask(fuc, 0x10f914, mask, data); + + for (i = 0; (data & 0x80000000) && i < ram->parts; addr += 0x1000, i++) { + if (ram->pmask & (1 << i)) + continue; + ram_wait(fuc, addr, 0x0000000f, 0x00000000, 500000); + } +} + +static void +r1373f4_init(struct gk104_ramfuc *fuc) +{ + struct gk104_ram *ram = container_of(fuc, typeof(*ram), fuc); + const u32 mcoef = ((--ram->P2 << 28) | (ram->N2 << 8) | ram->M2); + const u32 rcoef = (( ram->P1 << 16) | (ram->N1 << 8) | ram->M1); + const u32 runk0 = ram->fN1 << 16; + const u32 runk1 = ram->fN1; + + if (ram->from == 2) { + ram_mask(fuc, 0x1373f4, 0x00000000, 0x00001100); + ram_mask(fuc, 0x1373f4, 0x00000000, 0x00000010); + } else { + ram_mask(fuc, 0x1373f4, 0x00000000, 0x00010010); + } + + ram_mask(fuc, 0x1373f4, 0x00000003, 0x00000000); + ram_mask(fuc, 0x1373f4, 0x00000010, 0x00000000); + + /* (re)program refpll, if required */ + if ((ram_rd32(fuc, 0x132024) & 0xffffffff) != rcoef || + (ram_rd32(fuc, 0x132034) & 0x0000ffff) != runk1) { + ram_mask(fuc, 0x132000, 0x00000001, 0x00000000); + ram_mask(fuc, 0x132020, 0x00000001, 0x00000000); + ram_wr32(fuc, 0x137320, 0x00000000); + ram_mask(fuc, 0x132030, 0xffff0000, runk0); + ram_mask(fuc, 0x132034, 0x0000ffff, runk1); + ram_wr32(fuc, 0x132024, rcoef); + ram_mask(fuc, 0x132028, 0x00080000, 0x00080000); + ram_mask(fuc, 0x132020, 0x00000001, 0x00000001); + ram_wait(fuc, 0x137390, 0x00020000, 0x00020000, 64000); + ram_mask(fuc, 0x132028, 0x00080000, 0x00000000); + } + + /* (re)program mempll, if required */ + if (ram->mode == 2) { + ram_mask(fuc, 0x1373f4, 0x00010000, 0x00000000); + ram_mask(fuc, 0x132000, 0x80000000, 0x80000000); + ram_mask(fuc, 0x132000, 0x00000001, 0x00000000); + ram_mask(fuc, 0x132004, 0x103fffff, mcoef); + ram_mask(fuc, 0x132000, 0x00000001, 0x00000001); + ram_wait(fuc, 0x137390, 0x00000002, 0x00000002, 64000); + ram_mask(fuc, 0x1373f4, 0x00000000, 0x00001100); + } else { + ram_mask(fuc, 0x1373f4, 0x00000000, 0x00010100); + } + + ram_mask(fuc, 0x1373f4, 0x00000000, 0x00000010); +} + +static void +r1373f4_fini(struct gk104_ramfuc *fuc) +{ + struct gk104_ram *ram = container_of(fuc, typeof(*ram), fuc); + struct nvkm_ram_data *next = ram->base.next; + u8 v0 = next->bios.ramcfg_11_03_c0; + u8 v1 = next->bios.ramcfg_11_03_30; + u32 tmp; + + tmp = ram_rd32(fuc, 0x1373ec) & ~0x00030000; + ram_wr32(fuc, 0x1373ec, tmp | (v1 << 16)); + ram_mask(fuc, 0x1373f0, (~ram->mode & 3), 0x00000000); + if (ram->mode == 2) { + ram_mask(fuc, 0x1373f4, 0x00000003, 0x000000002); + ram_mask(fuc, 0x1373f4, 0x00001100, 0x000000000); + } else { + ram_mask(fuc, 0x1373f4, 0x00000003, 0x000000001); + ram_mask(fuc, 0x1373f4, 0x00010000, 0x000000000); + } + ram_mask(fuc, 0x10f800, 0x00000030, (v0 ^ v1) << 4); +} + +static void +gk104_ram_nuts(struct gk104_ram *ram, struct ramfuc_reg *reg, + u32 _mask, u32 _data, u32 _copy) +{ + struct gk104_fb_priv *priv = (void *)nvkm_fb(ram); + struct ramfuc *fuc = &ram->fuc.base; + u32 addr = 0x110000 + (reg->addr & 0xfff); + u32 mask = _mask | _copy; + u32 data = (_data & _mask) | (reg->data & _copy); + u32 i; + + for (i = 0; i < 16; i++, addr += 0x1000) { + if (ram->pnuts & (1 << i)) { + u32 prev = nv_rd32(priv, addr); + u32 next = (prev & ~mask) | data; + nvkm_memx_wr32(fuc->memx, addr, next); + } + } +} +#define ram_nuts(s,r,m,d,c) \ + gk104_ram_nuts((s), &(s)->fuc.r_##r, (m), (d), (c)) + +static int +gk104_ram_calc_gddr5(struct nvkm_fb *pfb, u32 freq) +{ + struct gk104_ram *ram = (void *)pfb->ram; + struct gk104_ramfuc *fuc = &ram->fuc; + struct nvkm_ram_data *next = ram->base.next; + int vc = !next->bios.ramcfg_11_02_08; + int mv = !next->bios.ramcfg_11_02_04; + u32 mask, data; + + ram_mask(fuc, 0x10f808, 0x40000000, 0x40000000); + ram_block(fuc); + ram_wr32(fuc, 0x62c000, 0x0f0f0000); + + /* MR1: turn termination on early, for some reason.. */ + if ((ram->base.mr[1] & 0x03c) != 0x030) { + ram_mask(fuc, mr[1], 0x03c, ram->base.mr[1] & 0x03c); + ram_nuts(ram, mr[1], 0x03c, ram->base.mr1_nuts & 0x03c, 0x000); + } + + if (vc == 1 && ram_have(fuc, gpio2E)) { + u32 temp = ram_mask(fuc, gpio2E, 0x3000, fuc->r_func2E[1]); + if (temp != ram_rd32(fuc, gpio2E)) { + ram_wr32(fuc, gpiotrig, 1); + ram_nsec(fuc, 20000); + } + } + + ram_mask(fuc, 0x10f200, 0x00000800, 0x00000000); + + gk104_ram_train(fuc, 0x01020000, 0x000c0000); + + ram_wr32(fuc, 0x10f210, 0x00000000); /* REFRESH_AUTO = 0 */ + ram_nsec(fuc, 1000); + ram_wr32(fuc, 0x10f310, 0x00000001); /* REFRESH */ + ram_nsec(fuc, 1000); + + ram_mask(fuc, 0x10f200, 0x80000000, 0x80000000); + ram_wr32(fuc, 0x10f314, 0x00000001); /* PRECHARGE */ + ram_mask(fuc, 0x10f200, 0x80000000, 0x00000000); + ram_wr32(fuc, 0x10f090, 0x00000061); + ram_wr32(fuc, 0x10f090, 0xc000007f); + ram_nsec(fuc, 1000); + + ram_wr32(fuc, 0x10f698, 0x00000000); + ram_wr32(fuc, 0x10f69c, 0x00000000); + + /*XXX: there does appear to be some kind of condition here, simply + * modifying these bits in the vbios from the default pl0 + * entries shows no change. however, the data does appear to + * be correct and may be required for the transition back + */ + mask = 0x800f07e0; + data = 0x00030000; + if (ram_rd32(fuc, 0x10f978) & 0x00800000) + data |= 0x00040000; + + if (1) { + data |= 0x800807e0; + switch (next->bios.ramcfg_11_03_c0) { + case 3: data &= ~0x00000040; break; + case 2: data &= ~0x00000100; break; + case 1: data &= ~0x80000000; break; + case 0: data &= ~0x00000400; break; + } + + switch (next->bios.ramcfg_11_03_30) { + case 3: data &= ~0x00000020; break; + case 2: data &= ~0x00000080; break; + case 1: data &= ~0x00080000; break; + case 0: data &= ~0x00000200; break; + } + } + + if (next->bios.ramcfg_11_02_80) + mask |= 0x03000000; + if (next->bios.ramcfg_11_02_40) + mask |= 0x00002000; + if (next->bios.ramcfg_11_07_10) + mask |= 0x00004000; + if (next->bios.ramcfg_11_07_08) + mask |= 0x00000003; + else { + mask |= 0x34000000; + if (ram_rd32(fuc, 0x10f978) & 0x00800000) + mask |= 0x40000000; + } + ram_mask(fuc, 0x10f824, mask, data); + + ram_mask(fuc, 0x132040, 0x00010000, 0x00000000); + + if (ram->from == 2 && ram->mode != 2) { + ram_mask(fuc, 0x10f808, 0x00080000, 0x00000000); + ram_mask(fuc, 0x10f200, 0x18008000, 0x00008000); + ram_mask(fuc, 0x10f800, 0x00000000, 0x00000004); + ram_mask(fuc, 0x10f830, 0x00008000, 0x01040010); + ram_mask(fuc, 0x10f830, 0x01000000, 0x00000000); + r1373f4_init(fuc); + ram_mask(fuc, 0x1373f0, 0x00000002, 0x00000001); + r1373f4_fini(fuc); + ram_mask(fuc, 0x10f830, 0x00c00000, 0x00240001); + } else + if (ram->from != 2 && ram->mode != 2) { + r1373f4_init(fuc); + r1373f4_fini(fuc); + } + + if (ram_have(fuc, gpioMV)) { + u32 temp = ram_mask(fuc, gpioMV, 0x3000, fuc->r_funcMV[mv]); + if (temp != ram_rd32(fuc, gpioMV)) { + ram_wr32(fuc, gpiotrig, 1); + ram_nsec(fuc, 64000); + } + } + + if (next->bios.ramcfg_11_02_40 || + next->bios.ramcfg_11_07_10) { + ram_mask(fuc, 0x132040, 0x00010000, 0x00010000); + ram_nsec(fuc, 20000); + } + + if (ram->from != 2 && ram->mode == 2) { + if (0 /*XXX: Titan */) + ram_mask(fuc, 0x10f200, 0x18000000, 0x18000000); + ram_mask(fuc, 0x10f800, 0x00000004, 0x00000000); + ram_mask(fuc, 0x1373f0, 0x00000000, 0x00000002); + ram_mask(fuc, 0x10f830, 0x00800001, 0x00408010); + r1373f4_init(fuc); + r1373f4_fini(fuc); + ram_mask(fuc, 0x10f808, 0x00000000, 0x00080000); + ram_mask(fuc, 0x10f200, 0x00808000, 0x00800000); + } else + if (ram->from == 2 && ram->mode == 2) { + ram_mask(fuc, 0x10f800, 0x00000004, 0x00000000); + r1373f4_init(fuc); + r1373f4_fini(fuc); + } + + if (ram->mode != 2) /*XXX*/ { + if (next->bios.ramcfg_11_07_40) + ram_mask(fuc, 0x10f670, 0x80000000, 0x80000000); + } + + ram_wr32(fuc, 0x10f65c, 0x00000011 * next->bios.rammap_11_11_0c); + ram_wr32(fuc, 0x10f6b8, 0x01010101 * next->bios.ramcfg_11_09); + ram_wr32(fuc, 0x10f6bc, 0x01010101 * next->bios.ramcfg_11_09); + + if (!next->bios.ramcfg_11_07_08 && !next->bios.ramcfg_11_07_04) { + ram_wr32(fuc, 0x10f698, 0x01010101 * next->bios.ramcfg_11_04); + ram_wr32(fuc, 0x10f69c, 0x01010101 * next->bios.ramcfg_11_04); + } else + if (!next->bios.ramcfg_11_07_08) { + ram_wr32(fuc, 0x10f698, 0x00000000); + ram_wr32(fuc, 0x10f69c, 0x00000000); + } + + if (ram->mode != 2) { + u32 data = 0x01000100 * next->bios.ramcfg_11_04; + ram_nuke(fuc, 0x10f694); + ram_mask(fuc, 0x10f694, 0xff00ff00, data); + } + + if (ram->mode == 2 && next->bios.ramcfg_11_08_10) + data = 0x00000080; + else + data = 0x00000000; + ram_mask(fuc, 0x10f60c, 0x00000080, data); + + mask = 0x00070000; + data = 0x00000000; + if (!next->bios.ramcfg_11_02_80) + data |= 0x03000000; + if (!next->bios.ramcfg_11_02_40) + data |= 0x00002000; + if (!next->bios.ramcfg_11_07_10) + data |= 0x00004000; + if (!next->bios.ramcfg_11_07_08) + data |= 0x00000003; + else + data |= 0x74000000; + ram_mask(fuc, 0x10f824, mask, data); + + if (next->bios.ramcfg_11_01_08) + data = 0x00000000; + else + data = 0x00001000; + ram_mask(fuc, 0x10f200, 0x00001000, data); + + if (ram_rd32(fuc, 0x10f670) & 0x80000000) { + ram_nsec(fuc, 10000); + ram_mask(fuc, 0x10f670, 0x80000000, 0x00000000); + } + + if (next->bios.ramcfg_11_08_01) + data = 0x00100000; + else + data = 0x00000000; + ram_mask(fuc, 0x10f82c, 0x00100000, data); + + data = 0x00000000; + if (next->bios.ramcfg_11_08_08) + data |= 0x00002000; + if (next->bios.ramcfg_11_08_04) + data |= 0x00001000; + if (next->bios.ramcfg_11_08_02) + data |= 0x00004000; + ram_mask(fuc, 0x10f830, 0x00007000, data); + + /* PFB timing */ + ram_mask(fuc, 0x10f248, 0xffffffff, next->bios.timing[10]); + ram_mask(fuc, 0x10f290, 0xffffffff, next->bios.timing[0]); + ram_mask(fuc, 0x10f294, 0xffffffff, next->bios.timing[1]); + ram_mask(fuc, 0x10f298, 0xffffffff, next->bios.timing[2]); + ram_mask(fuc, 0x10f29c, 0xffffffff, next->bios.timing[3]); + ram_mask(fuc, 0x10f2a0, 0xffffffff, next->bios.timing[4]); + ram_mask(fuc, 0x10f2a4, 0xffffffff, next->bios.timing[5]); + ram_mask(fuc, 0x10f2a8, 0xffffffff, next->bios.timing[6]); + ram_mask(fuc, 0x10f2ac, 0xffffffff, next->bios.timing[7]); + ram_mask(fuc, 0x10f2cc, 0xffffffff, next->bios.timing[8]); + ram_mask(fuc, 0x10f2e8, 0xffffffff, next->bios.timing[9]); + + data = mask = 0x00000000; + if (ram->diff.ramcfg_11_08_20) { + if (next->bios.ramcfg_11_08_20) + data |= 0x01000000; + mask |= 0x01000000; + } + ram_mask(fuc, 0x10f200, mask, data); + + data = mask = 0x00000000; + if (ram->diff.ramcfg_11_02_03) { + data |= next->bios.ramcfg_11_02_03 << 8; + mask |= 0x00000300; + } + if (ram->diff.ramcfg_11_01_10) { + if (next->bios.ramcfg_11_01_10) + data |= 0x70000000; + mask |= 0x70000000; + } + ram_mask(fuc, 0x10f604, mask, data); + + data = mask = 0x00000000; + if (ram->diff.timing_20_30_07) { + data |= next->bios.timing_20_30_07 << 28; + mask |= 0x70000000; + } + if (ram->diff.ramcfg_11_01_01) { + if (next->bios.ramcfg_11_01_01) + data |= 0x00000100; + mask |= 0x00000100; + } + ram_mask(fuc, 0x10f614, mask, data); + + data = mask = 0x00000000; + if (ram->diff.timing_20_30_07) { + data |= next->bios.timing_20_30_07 << 28; + mask |= 0x70000000; + } + if (ram->diff.ramcfg_11_01_02) { + if (next->bios.ramcfg_11_01_02) + data |= 0x00000100; + mask |= 0x00000100; + } + ram_mask(fuc, 0x10f610, mask, data); + + mask = 0x33f00000; + data = 0x00000000; + if (!next->bios.ramcfg_11_01_04) + data |= 0x20200000; + if (!next->bios.ramcfg_11_07_80) + data |= 0x12800000; + /*XXX: see note above about there probably being some condition + * for the 10f824 stuff that uses ramcfg 3... + */ + if (next->bios.ramcfg_11_03_f0) { + if (next->bios.rammap_11_08_0c) { + if (!next->bios.ramcfg_11_07_80) + mask |= 0x00000020; + else + data |= 0x00000020; + mask |= 0x00000004; + } + } else { + mask |= 0x40000020; + data |= 0x00000004; + } + + ram_mask(fuc, 0x10f808, mask, data); + + ram_wr32(fuc, 0x10f870, 0x11111111 * next->bios.ramcfg_11_03_0f); + + data = mask = 0x00000000; + if (ram->diff.ramcfg_11_02_03) { + data |= next->bios.ramcfg_11_02_03; + mask |= 0x00000003; + } + if (ram->diff.ramcfg_11_01_10) { + if (next->bios.ramcfg_11_01_10) + data |= 0x00000004; + mask |= 0x00000004; + } + + if ((ram_mask(fuc, 0x100770, mask, data) & mask & 4) != (data & 4)) { + ram_mask(fuc, 0x100750, 0x00000008, 0x00000008); + ram_wr32(fuc, 0x100710, 0x00000000); + ram_wait(fuc, 0x100710, 0x80000000, 0x80000000, 200000); + } + + data = next->bios.timing_20_30_07 << 8; + if (next->bios.ramcfg_11_01_01) + data |= 0x80000000; + ram_mask(fuc, 0x100778, 0x00000700, data); + + ram_mask(fuc, 0x10f250, 0x000003f0, next->bios.timing_20_2c_003f << 4); + data = (next->bios.timing[10] & 0x7f000000) >> 24; + if (data < next->bios.timing_20_2c_1fc0) + data = next->bios.timing_20_2c_1fc0; + ram_mask(fuc, 0x10f24c, 0x7f000000, data << 24); + ram_mask(fuc, 0x10f224, 0x001f0000, next->bios.timing_20_30_f8 << 16); + + ram_mask(fuc, 0x10fec4, 0x041e0f07, next->bios.timing_20_31_0800 << 26 | + next->bios.timing_20_31_0780 << 17 | + next->bios.timing_20_31_0078 << 8 | + next->bios.timing_20_31_0007); + ram_mask(fuc, 0x10fec8, 0x00000027, next->bios.timing_20_31_8000 << 5 | + next->bios.timing_20_31_7000); + + ram_wr32(fuc, 0x10f090, 0x4000007e); + ram_nsec(fuc, 2000); + ram_wr32(fuc, 0x10f314, 0x00000001); /* PRECHARGE */ + ram_wr32(fuc, 0x10f310, 0x00000001); /* REFRESH */ + ram_wr32(fuc, 0x10f210, 0x80000000); /* REFRESH_AUTO = 1 */ + + if (next->bios.ramcfg_11_08_10 && (ram->mode == 2) /*XXX*/) { + u32 temp = ram_mask(fuc, 0x10f294, 0xff000000, 0x24000000); + gk104_ram_train(fuc, 0xbc0e0000, 0xa4010000); /*XXX*/ + ram_nsec(fuc, 1000); + ram_wr32(fuc, 0x10f294, temp); + } + + ram_mask(fuc, mr[3], 0xfff, ram->base.mr[3]); + ram_wr32(fuc, mr[0], ram->base.mr[0]); + ram_mask(fuc, mr[8], 0xfff, ram->base.mr[8]); + ram_nsec(fuc, 1000); + ram_mask(fuc, mr[1], 0xfff, ram->base.mr[1]); + ram_mask(fuc, mr[5], 0xfff, ram->base.mr[5] & ~0x004); /* LP3 later */ + ram_mask(fuc, mr[6], 0xfff, ram->base.mr[6]); + ram_mask(fuc, mr[7], 0xfff, ram->base.mr[7]); + + if (vc == 0 && ram_have(fuc, gpio2E)) { + u32 temp = ram_mask(fuc, gpio2E, 0x3000, fuc->r_func2E[0]); + if (temp != ram_rd32(fuc, gpio2E)) { + ram_wr32(fuc, gpiotrig, 1); + ram_nsec(fuc, 20000); + } + } + + ram_mask(fuc, 0x10f200, 0x80000000, 0x80000000); + ram_wr32(fuc, 0x10f318, 0x00000001); /* NOP? */ + ram_mask(fuc, 0x10f200, 0x80000000, 0x00000000); + ram_nsec(fuc, 1000); + ram_nuts(ram, 0x10f200, 0x18808800, 0x00000000, 0x18808800); + + data = ram_rd32(fuc, 0x10f978); + data &= ~0x00046144; + data |= 0x0000000b; + if (!next->bios.ramcfg_11_07_08) { + if (!next->bios.ramcfg_11_07_04) + data |= 0x0000200c; + else + data |= 0x00000000; + } else { + data |= 0x00040044; + } + ram_wr32(fuc, 0x10f978, data); + + if (ram->mode == 1) { + data = ram_rd32(fuc, 0x10f830) | 0x00000001; + ram_wr32(fuc, 0x10f830, data); + } + + if (!next->bios.ramcfg_11_07_08) { + data = 0x88020000; + if ( next->bios.ramcfg_11_07_04) + data |= 0x10000000; + if (!next->bios.rammap_11_08_10) + data |= 0x00080000; + } else { + data = 0xa40e0000; + } + gk104_ram_train(fuc, 0xbc0f0000, data); + if (1) /* XXX: not always? */ + ram_nsec(fuc, 1000); + + if (ram->mode == 2) { /*XXX*/ + ram_mask(fuc, 0x10f800, 0x00000004, 0x00000004); + } + + /* LP3 */ + if (ram_mask(fuc, mr[5], 0x004, ram->base.mr[5]) != ram->base.mr[5]) + ram_nsec(fuc, 1000); + + if (ram->mode != 2) { + ram_mask(fuc, 0x10f830, 0x01000000, 0x01000000); + ram_mask(fuc, 0x10f830, 0x01000000, 0x00000000); + } + + if (next->bios.ramcfg_11_07_02) + gk104_ram_train(fuc, 0x80020000, 0x01000000); + + ram_unblock(fuc); + ram_wr32(fuc, 0x62c000, 0x0f0f0f00); + + if (next->bios.rammap_11_08_01) + data = 0x00000800; + else + data = 0x00000000; + ram_mask(fuc, 0x10f200, 0x00000800, data); + ram_nuts(ram, 0x10f200, 0x18808800, data, 0x18808800); + return 0; +} + +/******************************************************************************* + * DDR3 + ******************************************************************************/ + +static int +gk104_ram_calc_sddr3(struct nvkm_fb *pfb, u32 freq) +{ + struct gk104_ram *ram = (void *)pfb->ram; + struct gk104_ramfuc *fuc = &ram->fuc; + const u32 rcoef = (( ram->P1 << 16) | (ram->N1 << 8) | ram->M1); + const u32 runk0 = ram->fN1 << 16; + const u32 runk1 = ram->fN1; + struct nvkm_ram_data *next = ram->base.next; + int vc = !next->bios.ramcfg_11_02_08; + int mv = !next->bios.ramcfg_11_02_04; + u32 mask, data; + + ram_mask(fuc, 0x10f808, 0x40000000, 0x40000000); + ram_block(fuc); + ram_wr32(fuc, 0x62c000, 0x0f0f0000); + + if (vc == 1 && ram_have(fuc, gpio2E)) { + u32 temp = ram_mask(fuc, gpio2E, 0x3000, fuc->r_func2E[1]); + if (temp != ram_rd32(fuc, gpio2E)) { + ram_wr32(fuc, gpiotrig, 1); + ram_nsec(fuc, 20000); + } + } + + ram_mask(fuc, 0x10f200, 0x00000800, 0x00000000); + if (next->bios.ramcfg_11_03_f0) + ram_mask(fuc, 0x10f808, 0x04000000, 0x04000000); + + ram_wr32(fuc, 0x10f314, 0x00000001); /* PRECHARGE */ + ram_wr32(fuc, 0x10f210, 0x00000000); /* REFRESH_AUTO = 0 */ + ram_wr32(fuc, 0x10f310, 0x00000001); /* REFRESH */ + ram_mask(fuc, 0x10f200, 0x80000000, 0x80000000); + ram_wr32(fuc, 0x10f310, 0x00000001); /* REFRESH */ + ram_mask(fuc, 0x10f200, 0x80000000, 0x00000000); + ram_nsec(fuc, 1000); + + ram_wr32(fuc, 0x10f090, 0x00000060); + ram_wr32(fuc, 0x10f090, 0xc000007e); + + /*XXX: there does appear to be some kind of condition here, simply + * modifying these bits in the vbios from the default pl0 + * entries shows no change. however, the data does appear to + * be correct and may be required for the transition back + */ + mask = 0x00010000; + data = 0x00010000; + + if (1) { + mask |= 0x800807e0; + data |= 0x800807e0; + switch (next->bios.ramcfg_11_03_c0) { + case 3: data &= ~0x00000040; break; + case 2: data &= ~0x00000100; break; + case 1: data &= ~0x80000000; break; + case 0: data &= ~0x00000400; break; + } + + switch (next->bios.ramcfg_11_03_30) { + case 3: data &= ~0x00000020; break; + case 2: data &= ~0x00000080; break; + case 1: data &= ~0x00080000; break; + case 0: data &= ~0x00000200; break; + } + } + + if (next->bios.ramcfg_11_02_80) + mask |= 0x03000000; + if (next->bios.ramcfg_11_02_40) + mask |= 0x00002000; + if (next->bios.ramcfg_11_07_10) + mask |= 0x00004000; + if (next->bios.ramcfg_11_07_08) + mask |= 0x00000003; + else + mask |= 0x14000000; + ram_mask(fuc, 0x10f824, mask, data); + + ram_mask(fuc, 0x132040, 0x00010000, 0x00000000); + + ram_mask(fuc, 0x1373f4, 0x00000000, 0x00010010); + data = ram_rd32(fuc, 0x1373ec) & ~0x00030000; + data |= next->bios.ramcfg_11_03_30 << 16; + ram_wr32(fuc, 0x1373ec, data); + ram_mask(fuc, 0x1373f4, 0x00000003, 0x00000000); + ram_mask(fuc, 0x1373f4, 0x00000010, 0x00000000); + + /* (re)program refpll, if required */ + if ((ram_rd32(fuc, 0x132024) & 0xffffffff) != rcoef || + (ram_rd32(fuc, 0x132034) & 0x0000ffff) != runk1) { + ram_mask(fuc, 0x132000, 0x00000001, 0x00000000); + ram_mask(fuc, 0x132020, 0x00000001, 0x00000000); + ram_wr32(fuc, 0x137320, 0x00000000); + ram_mask(fuc, 0x132030, 0xffff0000, runk0); + ram_mask(fuc, 0x132034, 0x0000ffff, runk1); + ram_wr32(fuc, 0x132024, rcoef); + ram_mask(fuc, 0x132028, 0x00080000, 0x00080000); + ram_mask(fuc, 0x132020, 0x00000001, 0x00000001); + ram_wait(fuc, 0x137390, 0x00020000, 0x00020000, 64000); + ram_mask(fuc, 0x132028, 0x00080000, 0x00000000); + } + + ram_mask(fuc, 0x1373f4, 0x00000010, 0x00000010); + ram_mask(fuc, 0x1373f4, 0x00000003, 0x00000001); + ram_mask(fuc, 0x1373f4, 0x00010000, 0x00000000); + + if (ram_have(fuc, gpioMV)) { + u32 temp = ram_mask(fuc, gpioMV, 0x3000, fuc->r_funcMV[mv]); + if (temp != ram_rd32(fuc, gpioMV)) { + ram_wr32(fuc, gpiotrig, 1); + ram_nsec(fuc, 64000); + } + } + + if (next->bios.ramcfg_11_02_40 || + next->bios.ramcfg_11_07_10) { + ram_mask(fuc, 0x132040, 0x00010000, 0x00010000); + ram_nsec(fuc, 20000); + } + + if (ram->mode != 2) /*XXX*/ { + if (next->bios.ramcfg_11_07_40) + ram_mask(fuc, 0x10f670, 0x80000000, 0x80000000); + } + + ram_wr32(fuc, 0x10f65c, 0x00000011 * next->bios.rammap_11_11_0c); + ram_wr32(fuc, 0x10f6b8, 0x01010101 * next->bios.ramcfg_11_09); + ram_wr32(fuc, 0x10f6bc, 0x01010101 * next->bios.ramcfg_11_09); + + mask = 0x00010000; + data = 0x00000000; + if (!next->bios.ramcfg_11_02_80) + data |= 0x03000000; + if (!next->bios.ramcfg_11_02_40) + data |= 0x00002000; + if (!next->bios.ramcfg_11_07_10) + data |= 0x00004000; + if (!next->bios.ramcfg_11_07_08) + data |= 0x00000003; + else + data |= 0x14000000; + ram_mask(fuc, 0x10f824, mask, data); + ram_nsec(fuc, 1000); + + if (next->bios.ramcfg_11_08_01) + data = 0x00100000; + else + data = 0x00000000; + ram_mask(fuc, 0x10f82c, 0x00100000, data); + + /* PFB timing */ + ram_mask(fuc, 0x10f248, 0xffffffff, next->bios.timing[10]); + ram_mask(fuc, 0x10f290, 0xffffffff, next->bios.timing[0]); + ram_mask(fuc, 0x10f294, 0xffffffff, next->bios.timing[1]); + ram_mask(fuc, 0x10f298, 0xffffffff, next->bios.timing[2]); + ram_mask(fuc, 0x10f29c, 0xffffffff, next->bios.timing[3]); + ram_mask(fuc, 0x10f2a0, 0xffffffff, next->bios.timing[4]); + ram_mask(fuc, 0x10f2a4, 0xffffffff, next->bios.timing[5]); + ram_mask(fuc, 0x10f2a8, 0xffffffff, next->bios.timing[6]); + ram_mask(fuc, 0x10f2ac, 0xffffffff, next->bios.timing[7]); + ram_mask(fuc, 0x10f2cc, 0xffffffff, next->bios.timing[8]); + ram_mask(fuc, 0x10f2e8, 0xffffffff, next->bios.timing[9]); + + mask = 0x33f00000; + data = 0x00000000; + if (!next->bios.ramcfg_11_01_04) + data |= 0x20200000; + if (!next->bios.ramcfg_11_07_80) + data |= 0x12800000; + /*XXX: see note above about there probably being some condition + * for the 10f824 stuff that uses ramcfg 3... + */ + if (next->bios.ramcfg_11_03_f0) { + if (next->bios.rammap_11_08_0c) { + if (!next->bios.ramcfg_11_07_80) + mask |= 0x00000020; + else + data |= 0x00000020; + mask |= 0x08000004; + } + data |= 0x04000000; + } else { + mask |= 0x44000020; + data |= 0x08000004; + } + + ram_mask(fuc, 0x10f808, mask, data); + + ram_wr32(fuc, 0x10f870, 0x11111111 * next->bios.ramcfg_11_03_0f); + + ram_mask(fuc, 0x10f250, 0x000003f0, next->bios.timing_20_2c_003f << 4); + + data = (next->bios.timing[10] & 0x7f000000) >> 24; + if (data < next->bios.timing_20_2c_1fc0) + data = next->bios.timing_20_2c_1fc0; + ram_mask(fuc, 0x10f24c, 0x7f000000, data << 24); + + ram_mask(fuc, 0x10f224, 0x001f0000, next->bios.timing_20_30_f8 << 16); + + ram_wr32(fuc, 0x10f090, 0x4000007f); + ram_nsec(fuc, 1000); + + ram_wr32(fuc, 0x10f314, 0x00000001); /* PRECHARGE */ + ram_wr32(fuc, 0x10f310, 0x00000001); /* REFRESH */ + ram_wr32(fuc, 0x10f210, 0x80000000); /* REFRESH_AUTO = 1 */ + ram_nsec(fuc, 1000); + + ram_nuke(fuc, mr[0]); + ram_mask(fuc, mr[0], 0x100, 0x100); + ram_mask(fuc, mr[0], 0x100, 0x000); + + ram_mask(fuc, mr[2], 0xfff, ram->base.mr[2]); + ram_wr32(fuc, mr[0], ram->base.mr[0]); + ram_nsec(fuc, 1000); + + ram_nuke(fuc, mr[0]); + ram_mask(fuc, mr[0], 0x100, 0x100); + ram_mask(fuc, mr[0], 0x100, 0x000); + + if (vc == 0 && ram_have(fuc, gpio2E)) { + u32 temp = ram_mask(fuc, gpio2E, 0x3000, fuc->r_func2E[0]); + if (temp != ram_rd32(fuc, gpio2E)) { + ram_wr32(fuc, gpiotrig, 1); + ram_nsec(fuc, 20000); + } + } + + if (ram->mode != 2) { + ram_mask(fuc, 0x10f830, 0x01000000, 0x01000000); + ram_mask(fuc, 0x10f830, 0x01000000, 0x00000000); + } + + ram_mask(fuc, 0x10f200, 0x80000000, 0x80000000); + ram_wr32(fuc, 0x10f318, 0x00000001); /* NOP? */ + ram_mask(fuc, 0x10f200, 0x80000000, 0x00000000); + ram_nsec(fuc, 1000); + + ram_unblock(fuc); + ram_wr32(fuc, 0x62c000, 0x0f0f0f00); + + if (next->bios.rammap_11_08_01) + data = 0x00000800; + else + data = 0x00000000; + ram_mask(fuc, 0x10f200, 0x00000800, data); + return 0; +} + +/******************************************************************************* + * main hooks + ******************************************************************************/ + +static int +gk104_ram_calc_data(struct nvkm_fb *pfb, u32 khz, struct nvkm_ram_data *data) +{ + struct gk104_ram *ram = (void *)pfb->ram; + struct nvkm_ram_data *cfg; + u32 mhz = khz / 1000; + + list_for_each_entry(cfg, &ram->cfg, head) { + if (mhz >= cfg->bios.rammap_min && + mhz <= cfg->bios.rammap_max) { + *data = *cfg; + data->freq = khz; + return 0; + } + } + + nv_error(ram, "ramcfg data for %dMHz not found\n", mhz); + return -EINVAL; +} + +static int +gk104_ram_calc_xits(struct nvkm_fb *pfb, struct nvkm_ram_data *next) +{ + struct gk104_ram *ram = (void *)pfb->ram; + struct gk104_ramfuc *fuc = &ram->fuc; + int refclk, i; + int ret; + + ret = ram_init(fuc, pfb); + if (ret) + return ret; + + ram->mode = (next->freq > fuc->refpll.vco1.max_freq) ? 2 : 1; + ram->from = ram_rd32(fuc, 0x1373f4) & 0x0000000f; + + /* XXX: this is *not* what nvidia do. on fermi nvidia generally + * select, based on some unknown condition, one of the two possible + * reference frequencies listed in the vbios table for mempll and + * program refpll to that frequency. + * + * so far, i've seen very weird values being chosen by nvidia on + * kepler boards, no idea how/why they're chosen. + */ + refclk = next->freq; + if (ram->mode == 2) + refclk = fuc->mempll.refclk; + + /* calculate refpll coefficients */ + ret = gt215_pll_calc(nv_subdev(pfb), &fuc->refpll, refclk, &ram->N1, + &ram->fN1, &ram->M1, &ram->P1); + fuc->mempll.refclk = ret; + if (ret <= 0) { + nv_error(pfb, "unable to calc refpll\n"); + return -EINVAL; + } + + /* calculate mempll coefficients, if we're using it */ + if (ram->mode == 2) { + /* post-divider doesn't work... the reg takes the values but + * appears to completely ignore it. there *is* a bit at + * bit 28 that appears to divide the clock by 2 if set. + */ + fuc->mempll.min_p = 1; + fuc->mempll.max_p = 2; + + ret = gt215_pll_calc(nv_subdev(pfb), &fuc->mempll, next->freq, + &ram->N2, NULL, &ram->M2, &ram->P2); + if (ret <= 0) { + nv_error(pfb, "unable to calc mempll\n"); + return -EINVAL; + } + } + + for (i = 0; i < ARRAY_SIZE(fuc->r_mr); i++) { + if (ram_have(fuc, mr[i])) + ram->base.mr[i] = ram_rd32(fuc, mr[i]); + } + ram->base.freq = next->freq; + + switch (ram->base.type) { + case NV_MEM_TYPE_DDR3: + ret = nvkm_sddr3_calc(&ram->base); + if (ret == 0) + ret = gk104_ram_calc_sddr3(pfb, next->freq); + break; + case NV_MEM_TYPE_GDDR5: + ret = nvkm_gddr5_calc(&ram->base, ram->pnuts != 0); + if (ret == 0) + ret = gk104_ram_calc_gddr5(pfb, next->freq); + break; + default: + ret = -ENOSYS; + break; + } + + return ret; +} + +static int +gk104_ram_calc(struct nvkm_fb *pfb, u32 freq) +{ + struct nvkm_clk *clk = nvkm_clk(pfb); + struct gk104_ram *ram = (void *)pfb->ram; + struct nvkm_ram_data *xits = &ram->base.xition; + struct nvkm_ram_data *copy; + int ret; + + if (ram->base.next == NULL) { + ret = gk104_ram_calc_data(pfb, clk->read(clk, nv_clk_src_mem), + &ram->base.former); + if (ret) + return ret; + + ret = gk104_ram_calc_data(pfb, freq, &ram->base.target); + if (ret) + return ret; + + if (ram->base.target.freq < ram->base.former.freq) { + *xits = ram->base.target; + copy = &ram->base.former; + } else { + *xits = ram->base.former; + copy = &ram->base.target; + } + + xits->bios.ramcfg_11_02_04 = copy->bios.ramcfg_11_02_04; + xits->bios.ramcfg_11_02_03 = copy->bios.ramcfg_11_02_03; + xits->bios.timing_20_30_07 = copy->bios.timing_20_30_07; + + ram->base.next = &ram->base.target; + if (memcmp(xits, &ram->base.former, sizeof(xits->bios))) + ram->base.next = &ram->base.xition; + } else { + BUG_ON(ram->base.next != &ram->base.xition); + ram->base.next = &ram->base.target; + } + + return gk104_ram_calc_xits(pfb, ram->base.next); +} + +static void +gk104_ram_prog_0(struct nvkm_fb *pfb, u32 freq) +{ + struct gk104_ram *ram = (void *)pfb->ram; + struct nvkm_ram_data *cfg; + u32 mhz = freq / 1000; + u32 mask, data; + + list_for_each_entry(cfg, &ram->cfg, head) { + if (mhz >= cfg->bios.rammap_min && + mhz <= cfg->bios.rammap_max) + break; + } + + if (&cfg->head == &ram->cfg) + return; + + if (mask = 0, data = 0, ram->diff.rammap_11_0a_03fe) { + data |= cfg->bios.rammap_11_0a_03fe << 12; + mask |= 0x001ff000; + } + if (ram->diff.rammap_11_09_01ff) { + data |= cfg->bios.rammap_11_09_01ff; + mask |= 0x000001ff; + } + nv_mask(pfb, 0x10f468, mask, data); + + if (mask = 0, data = 0, ram->diff.rammap_11_0a_0400) { + data |= cfg->bios.rammap_11_0a_0400; + mask |= 0x00000001; + } + nv_mask(pfb, 0x10f420, mask, data); + + if (mask = 0, data = 0, ram->diff.rammap_11_0a_0800) { + data |= cfg->bios.rammap_11_0a_0800; + mask |= 0x00000001; + } + nv_mask(pfb, 0x10f430, mask, data); + + if (mask = 0, data = 0, ram->diff.rammap_11_0b_01f0) { + data |= cfg->bios.rammap_11_0b_01f0; + mask |= 0x0000001f; + } + nv_mask(pfb, 0x10f400, mask, data); + + if (mask = 0, data = 0, ram->diff.rammap_11_0b_0200) { + data |= cfg->bios.rammap_11_0b_0200 << 9; + mask |= 0x00000200; + } + nv_mask(pfb, 0x10f410, mask, data); + + if (mask = 0, data = 0, ram->diff.rammap_11_0d) { + data |= cfg->bios.rammap_11_0d << 16; + mask |= 0x00ff0000; + } + if (ram->diff.rammap_11_0f) { + data |= cfg->bios.rammap_11_0f << 8; + mask |= 0x0000ff00; + } + nv_mask(pfb, 0x10f440, mask, data); + + if (mask = 0, data = 0, ram->diff.rammap_11_0e) { + data |= cfg->bios.rammap_11_0e << 8; + mask |= 0x0000ff00; + } + if (ram->diff.rammap_11_0b_0800) { + data |= cfg->bios.rammap_11_0b_0800 << 7; + mask |= 0x00000080; + } + if (ram->diff.rammap_11_0b_0400) { + data |= cfg->bios.rammap_11_0b_0400 << 5; + mask |= 0x00000020; + } + nv_mask(pfb, 0x10f444, mask, data); +} + +static int +gk104_ram_prog(struct nvkm_fb *pfb) +{ + struct nvkm_device *device = nv_device(pfb); + struct gk104_ram *ram = (void *)pfb->ram; + struct gk104_ramfuc *fuc = &ram->fuc; + struct nvkm_ram_data *next = ram->base.next; + + if (!nvkm_boolopt(device->cfgopt, "NvMemExec", true)) { + ram_exec(fuc, false); + return (ram->base.next == &ram->base.xition); + } + + gk104_ram_prog_0(pfb, 1000); + ram_exec(fuc, true); + gk104_ram_prog_0(pfb, next->freq); + + return (ram->base.next == &ram->base.xition); +} + +static void +gk104_ram_tidy(struct nvkm_fb *pfb) +{ + struct gk104_ram *ram = (void *)pfb->ram; + struct gk104_ramfuc *fuc = &ram->fuc; + ram->base.next = NULL; + ram_exec(fuc, false); +} + +struct gk104_ram_train { + u16 mask; + struct nvbios_M0209S remap; + struct nvbios_M0209S type00; + struct nvbios_M0209S type01; + struct nvbios_M0209S type04; + struct nvbios_M0209S type06; + struct nvbios_M0209S type07; + struct nvbios_M0209S type08; + struct nvbios_M0209S type09; +}; + +static int +gk104_ram_train_type(struct nvkm_fb *pfb, int i, u8 ramcfg, + struct gk104_ram_train *train) +{ + struct nvkm_bios *bios = nvkm_bios(pfb); + struct nvbios_M0205E M0205E; + struct nvbios_M0205S M0205S; + struct nvbios_M0209E M0209E; + struct nvbios_M0209S *remap = &train->remap; + struct nvbios_M0209S *value; + u8 ver, hdr, cnt, len; + u32 data; + + /* determine type of data for this index */ + if (!(data = nvbios_M0205Ep(bios, i, &ver, &hdr, &cnt, &len, &M0205E))) + return -ENOENT; + + switch (M0205E.type) { + case 0x00: value = &train->type00; break; + case 0x01: value = &train->type01; break; + case 0x04: value = &train->type04; break; + case 0x06: value = &train->type06; break; + case 0x07: value = &train->type07; break; + case 0x08: value = &train->type08; break; + case 0x09: value = &train->type09; break; + default: + return 0; + } + + /* training data index determined by ramcfg strap */ + if (!(data = nvbios_M0205Sp(bios, i, ramcfg, &ver, &hdr, &M0205S))) + return -EINVAL; + i = M0205S.data; + + /* training data format information */ + if (!(data = nvbios_M0209Ep(bios, i, &ver, &hdr, &cnt, &len, &M0209E))) + return -EINVAL; + + /* ... and the raw data */ + if (!(data = nvbios_M0209Sp(bios, i, 0, &ver, &hdr, value))) + return -EINVAL; + + if (M0209E.v02_07 == 2) { + /* of course! why wouldn't we have a pointer to another entry + * in the same table, and use the first one as an array of + * remap indices... + */ + if (!(data = nvbios_M0209Sp(bios, M0209E.v03, 0, &ver, &hdr, + remap))) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(value->data); i++) + value->data[i] = remap->data[value->data[i]]; + } else + if (M0209E.v02_07 != 1) + return -EINVAL; + + train->mask |= 1 << M0205E.type; + return 0; +} + +static int +gk104_ram_train_init_0(struct nvkm_fb *pfb, struct gk104_ram_train *train) +{ + int i, j; + + if ((train->mask & 0x03d3) != 0x03d3) { + nv_warn(pfb, "missing link training data\n"); + return -EINVAL; + } + + for (i = 0; i < 0x30; i++) { + for (j = 0; j < 8; j += 4) { + nv_wr32(pfb, 0x10f968 + j, 0x00000000 | (i << 8)); + nv_wr32(pfb, 0x10f920 + j, 0x00000000 | + train->type08.data[i] << 4 | + train->type06.data[i]); + nv_wr32(pfb, 0x10f918 + j, train->type00.data[i]); + nv_wr32(pfb, 0x10f920 + j, 0x00000100 | + train->type09.data[i] << 4 | + train->type07.data[i]); + nv_wr32(pfb, 0x10f918 + j, train->type01.data[i]); + } + } + + for (j = 0; j < 8; j += 4) { + for (i = 0; i < 0x100; i++) { + nv_wr32(pfb, 0x10f968 + j, i); + nv_wr32(pfb, 0x10f900 + j, train->type04.data[i]); + } + } + + return 0; +} + +static int +gk104_ram_train_init(struct nvkm_fb *pfb) +{ + u8 ramcfg = nvbios_ramcfg_index(nv_subdev(pfb)); + struct gk104_ram_train *train; + int ret = -ENOMEM, i; + + if ((train = kzalloc(sizeof(*train), GFP_KERNEL))) { + for (i = 0; i < 0x100; i++) { + ret = gk104_ram_train_type(pfb, i, ramcfg, train); + if (ret && ret != -ENOENT) + break; + } + } + + switch (pfb->ram->type) { + case NV_MEM_TYPE_GDDR5: + ret = gk104_ram_train_init_0(pfb, train); + break; + default: + ret = 0; + break; + } + + kfree(train); + return ret; +} + +int +gk104_ram_init(struct nvkm_object *object) +{ + struct nvkm_fb *pfb = (void *)object->parent; + struct gk104_ram *ram = (void *)object; + struct nvkm_bios *bios = nvkm_bios(pfb); + u8 ver, hdr, cnt, len, snr, ssz; + u32 data, save; + int ret, i; + + ret = nvkm_ram_init(&ram->base); + if (ret) + return ret; + + /* run a bunch of tables from rammap table. there's actually + * individual pointers for each rammap entry too, but, nvidia + * seem to just run the last two entries' scripts early on in + * their init, and never again.. we'll just run 'em all once + * for now. + * + * i strongly suspect that each script is for a separate mode + * (likely selected by 0x10f65c's lower bits?), and the + * binary driver skips the one that's already been setup by + * the init tables. + */ + data = nvbios_rammapTe(bios, &ver, &hdr, &cnt, &len, &snr, &ssz); + if (!data || hdr < 0x15) + return -EINVAL; + + cnt = nv_ro08(bios, data + 0x14); /* guess at count */ + data = nv_ro32(bios, data + 0x10); /* guess u32... */ + save = nv_rd32(pfb, 0x10f65c) & 0x000000f0; + for (i = 0; i < cnt; i++, data += 4) { + if (i != save >> 4) { + nv_mask(pfb, 0x10f65c, 0x000000f0, i << 4); + nvbios_exec(&(struct nvbios_init) { + .subdev = nv_subdev(pfb), + .bios = bios, + .offset = nv_ro32(bios, data), + .execute = 1, + }); + } + } + nv_mask(pfb, 0x10f65c, 0x000000f0, save); + nv_mask(pfb, 0x10f584, 0x11000000, 0x00000000); + nv_wr32(pfb, 0x10ecc0, 0xffffffff); + nv_mask(pfb, 0x10f160, 0x00000010, 0x00000010); + + return gk104_ram_train_init(pfb); +} + +static int +gk104_ram_ctor_data(struct gk104_ram *ram, u8 ramcfg, int i) +{ + struct nvkm_fb *pfb = (void *)nv_object(ram)->parent; + struct nvkm_bios *bios = nvkm_bios(pfb); + struct nvkm_ram_data *cfg; + struct nvbios_ramcfg *d = &ram->diff; + struct nvbios_ramcfg *p, *n; + u8 ver, hdr, cnt, len; + u32 data; + int ret; + + if (!(cfg = kmalloc(sizeof(*cfg), GFP_KERNEL))) + return -ENOMEM; + p = &list_last_entry(&ram->cfg, typeof(*cfg), head)->bios; + n = &cfg->bios; + + /* memory config data for a range of target frequencies */ + data = nvbios_rammapEp(bios, i, &ver, &hdr, &cnt, &len, &cfg->bios); + if (ret = -ENOENT, !data) + goto done; + if (ret = -ENOSYS, ver != 0x11 || hdr < 0x12) + goto done; + + /* ... and a portion specific to the attached memory */ + data = nvbios_rammapSp(bios, data, ver, hdr, cnt, len, ramcfg, + &ver, &hdr, &cfg->bios); + if (ret = -EINVAL, !data) + goto done; + if (ret = -ENOSYS, ver != 0x11 || hdr < 0x0a) + goto done; + + /* lookup memory timings, if bios says they're present */ + if (cfg->bios.ramcfg_timing != 0xff) { + data = nvbios_timingEp(bios, cfg->bios.ramcfg_timing, + &ver, &hdr, &cnt, &len, + &cfg->bios); + if (ret = -EINVAL, !data) + goto done; + if (ret = -ENOSYS, ver != 0x20 || hdr < 0x33) + goto done; + } + + list_add_tail(&cfg->head, &ram->cfg); + if (ret = 0, i == 0) + goto done; + + d->rammap_11_0a_03fe |= p->rammap_11_0a_03fe != n->rammap_11_0a_03fe; + d->rammap_11_09_01ff |= p->rammap_11_09_01ff != n->rammap_11_09_01ff; + d->rammap_11_0a_0400 |= p->rammap_11_0a_0400 != n->rammap_11_0a_0400; + d->rammap_11_0a_0800 |= p->rammap_11_0a_0800 != n->rammap_11_0a_0800; + d->rammap_11_0b_01f0 |= p->rammap_11_0b_01f0 != n->rammap_11_0b_01f0; + d->rammap_11_0b_0200 |= p->rammap_11_0b_0200 != n->rammap_11_0b_0200; + d->rammap_11_0d |= p->rammap_11_0d != n->rammap_11_0d; + d->rammap_11_0f |= p->rammap_11_0f != n->rammap_11_0f; + d->rammap_11_0e |= p->rammap_11_0e != n->rammap_11_0e; + d->rammap_11_0b_0800 |= p->rammap_11_0b_0800 != n->rammap_11_0b_0800; + d->rammap_11_0b_0400 |= p->rammap_11_0b_0400 != n->rammap_11_0b_0400; + d->ramcfg_11_01_01 |= p->ramcfg_11_01_01 != n->ramcfg_11_01_01; + d->ramcfg_11_01_02 |= p->ramcfg_11_01_02 != n->ramcfg_11_01_02; + d->ramcfg_11_01_10 |= p->ramcfg_11_01_10 != n->ramcfg_11_01_10; + d->ramcfg_11_02_03 |= p->ramcfg_11_02_03 != n->ramcfg_11_02_03; + d->ramcfg_11_08_20 |= p->ramcfg_11_08_20 != n->ramcfg_11_08_20; + d->timing_20_30_07 |= p->timing_20_30_07 != n->timing_20_30_07; +done: + if (ret) + kfree(cfg); + return ret; +} + +static void +gk104_ram_dtor(struct nvkm_object *object) +{ + struct gk104_ram *ram = (void *)object; + struct nvkm_ram_data *cfg, *tmp; + + list_for_each_entry_safe(cfg, tmp, &ram->cfg, head) { + kfree(cfg); + } + + nvkm_ram_destroy(&ram->base); +} + +static int +gk104_ram_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_fb *pfb = nvkm_fb(parent); + struct nvkm_bios *bios = nvkm_bios(pfb); + struct nvkm_gpio *gpio = nvkm_gpio(pfb); + struct dcb_gpio_func func; + struct gk104_ram *ram; + int ret, i; + u8 ramcfg = nvbios_ramcfg_index(nv_subdev(pfb)); + u32 tmp; + + ret = gf100_ram_create(parent, engine, oclass, 0x022554, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + INIT_LIST_HEAD(&ram->cfg); + + switch (ram->base.type) { + case NV_MEM_TYPE_DDR3: + case NV_MEM_TYPE_GDDR5: + ram->base.calc = gk104_ram_calc; + ram->base.prog = gk104_ram_prog; + ram->base.tidy = gk104_ram_tidy; + break; + default: + nv_warn(pfb, "reclocking of this RAM type is unsupported\n"); + break; + } + + /* calculate a mask of differently configured memory partitions, + * because, of course reclocking wasn't complicated enough + * already without having to treat some of them differently to + * the others.... + */ + ram->parts = nv_rd32(pfb, 0x022438); + ram->pmask = nv_rd32(pfb, 0x022554); + ram->pnuts = 0; + for (i = 0, tmp = 0; i < ram->parts; i++) { + if (!(ram->pmask & (1 << i))) { + u32 cfg1 = nv_rd32(pfb, 0x110204 + (i * 0x1000)); + if (tmp && tmp != cfg1) { + ram->pnuts |= (1 << i); + continue; + } + tmp = cfg1; + } + } + + /* parse bios data for all rammap table entries up-front, and + * build information on whether certain fields differ between + * any of the entries. + * + * the binary driver appears to completely ignore some fields + * when all entries contain the same value. at first, it was + * hoped that these were mere optimisations and the bios init + * tables had configured as per the values here, but there is + * evidence now to suggest that this isn't the case and we do + * need to treat this condition as a "don't touch" indicator. + */ + for (i = 0; !ret; i++) { + ret = gk104_ram_ctor_data(ram, ramcfg, i); + if (ret && ret != -ENOENT) { + nv_error(pfb, "failed to parse ramcfg data\n"); + return ret; + } + } + + /* parse bios data for both pll's */ + ret = nvbios_pll_parse(bios, 0x0c, &ram->fuc.refpll); + if (ret) { + nv_error(pfb, "mclk refpll data not found\n"); + return ret; + } + + ret = nvbios_pll_parse(bios, 0x04, &ram->fuc.mempll); + if (ret) { + nv_error(pfb, "mclk pll data not found\n"); + return ret; + } + + /* lookup memory voltage gpios */ + ret = gpio->find(gpio, 0, 0x18, DCB_GPIO_UNUSED, &func); + if (ret == 0) { + ram->fuc.r_gpioMV = ramfuc_reg(0x00d610 + (func.line * 0x04)); + ram->fuc.r_funcMV[0] = (func.log[0] ^ 2) << 12; + ram->fuc.r_funcMV[1] = (func.log[1] ^ 2) << 12; + } + + ret = gpio->find(gpio, 0, 0x2e, DCB_GPIO_UNUSED, &func); + if (ret == 0) { + ram->fuc.r_gpio2E = ramfuc_reg(0x00d610 + (func.line * 0x04)); + ram->fuc.r_func2E[0] = (func.log[0] ^ 2) << 12; + ram->fuc.r_func2E[1] = (func.log[1] ^ 2) << 12; + } + + ram->fuc.r_gpiotrig = ramfuc_reg(0x00d604); + + ram->fuc.r_0x132020 = ramfuc_reg(0x132020); + ram->fuc.r_0x132028 = ramfuc_reg(0x132028); + ram->fuc.r_0x132024 = ramfuc_reg(0x132024); + ram->fuc.r_0x132030 = ramfuc_reg(0x132030); + ram->fuc.r_0x132034 = ramfuc_reg(0x132034); + ram->fuc.r_0x132000 = ramfuc_reg(0x132000); + ram->fuc.r_0x132004 = ramfuc_reg(0x132004); + ram->fuc.r_0x132040 = ramfuc_reg(0x132040); + + ram->fuc.r_0x10f248 = ramfuc_reg(0x10f248); + ram->fuc.r_0x10f290 = ramfuc_reg(0x10f290); + ram->fuc.r_0x10f294 = ramfuc_reg(0x10f294); + ram->fuc.r_0x10f298 = ramfuc_reg(0x10f298); + ram->fuc.r_0x10f29c = ramfuc_reg(0x10f29c); + ram->fuc.r_0x10f2a0 = ramfuc_reg(0x10f2a0); + ram->fuc.r_0x10f2a4 = ramfuc_reg(0x10f2a4); + ram->fuc.r_0x10f2a8 = ramfuc_reg(0x10f2a8); + ram->fuc.r_0x10f2ac = ramfuc_reg(0x10f2ac); + ram->fuc.r_0x10f2cc = ramfuc_reg(0x10f2cc); + ram->fuc.r_0x10f2e8 = ramfuc_reg(0x10f2e8); + ram->fuc.r_0x10f250 = ramfuc_reg(0x10f250); + ram->fuc.r_0x10f24c = ramfuc_reg(0x10f24c); + ram->fuc.r_0x10fec4 = ramfuc_reg(0x10fec4); + ram->fuc.r_0x10fec8 = ramfuc_reg(0x10fec8); + ram->fuc.r_0x10f604 = ramfuc_reg(0x10f604); + ram->fuc.r_0x10f614 = ramfuc_reg(0x10f614); + ram->fuc.r_0x10f610 = ramfuc_reg(0x10f610); + ram->fuc.r_0x100770 = ramfuc_reg(0x100770); + ram->fuc.r_0x100778 = ramfuc_reg(0x100778); + ram->fuc.r_0x10f224 = ramfuc_reg(0x10f224); + + ram->fuc.r_0x10f870 = ramfuc_reg(0x10f870); + ram->fuc.r_0x10f698 = ramfuc_reg(0x10f698); + ram->fuc.r_0x10f694 = ramfuc_reg(0x10f694); + ram->fuc.r_0x10f6b8 = ramfuc_reg(0x10f6b8); + ram->fuc.r_0x10f808 = ramfuc_reg(0x10f808); + ram->fuc.r_0x10f670 = ramfuc_reg(0x10f670); + ram->fuc.r_0x10f60c = ramfuc_reg(0x10f60c); + ram->fuc.r_0x10f830 = ramfuc_reg(0x10f830); + ram->fuc.r_0x1373ec = ramfuc_reg(0x1373ec); + ram->fuc.r_0x10f800 = ramfuc_reg(0x10f800); + ram->fuc.r_0x10f82c = ramfuc_reg(0x10f82c); + + ram->fuc.r_0x10f978 = ramfuc_reg(0x10f978); + ram->fuc.r_0x10f910 = ramfuc_reg(0x10f910); + ram->fuc.r_0x10f914 = ramfuc_reg(0x10f914); + + switch (ram->base.type) { + case NV_MEM_TYPE_GDDR5: + ram->fuc.r_mr[0] = ramfuc_reg(0x10f300); + ram->fuc.r_mr[1] = ramfuc_reg(0x10f330); + ram->fuc.r_mr[2] = ramfuc_reg(0x10f334); + ram->fuc.r_mr[3] = ramfuc_reg(0x10f338); + ram->fuc.r_mr[4] = ramfuc_reg(0x10f33c); + ram->fuc.r_mr[5] = ramfuc_reg(0x10f340); + ram->fuc.r_mr[6] = ramfuc_reg(0x10f344); + ram->fuc.r_mr[7] = ramfuc_reg(0x10f348); + ram->fuc.r_mr[8] = ramfuc_reg(0x10f354); + ram->fuc.r_mr[15] = ramfuc_reg(0x10f34c); + break; + case NV_MEM_TYPE_DDR3: + ram->fuc.r_mr[0] = ramfuc_reg(0x10f300); + ram->fuc.r_mr[2] = ramfuc_reg(0x10f320); + break; + default: + break; + } + + ram->fuc.r_0x62c000 = ramfuc_reg(0x62c000); + ram->fuc.r_0x10f200 = ramfuc_reg(0x10f200); + ram->fuc.r_0x10f210 = ramfuc_reg(0x10f210); + ram->fuc.r_0x10f310 = ramfuc_reg(0x10f310); + ram->fuc.r_0x10f314 = ramfuc_reg(0x10f314); + ram->fuc.r_0x10f318 = ramfuc_reg(0x10f318); + ram->fuc.r_0x10f090 = ramfuc_reg(0x10f090); + ram->fuc.r_0x10f69c = ramfuc_reg(0x10f69c); + ram->fuc.r_0x10f824 = ramfuc_reg(0x10f824); + ram->fuc.r_0x1373f0 = ramfuc_reg(0x1373f0); + ram->fuc.r_0x1373f4 = ramfuc_reg(0x1373f4); + ram->fuc.r_0x137320 = ramfuc_reg(0x137320); + ram->fuc.r_0x10f65c = ramfuc_reg(0x10f65c); + ram->fuc.r_0x10f6bc = ramfuc_reg(0x10f6bc); + ram->fuc.r_0x100710 = ramfuc_reg(0x100710); + ram->fuc.r_0x100750 = ramfuc_reg(0x100750); + return 0; +} + +struct nvkm_oclass +gk104_ram_oclass = { + .handle = 0, + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gk104_ram_ctor, + .dtor = gk104_ram_dtor, + .init = gk104_ram_init, + .fini = _nvkm_ram_fini, + } +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgm107.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgm107.c new file mode 100644 index 000000000..a298b39f5 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgm107.c @@ -0,0 +1,55 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "gf100.h" + +struct gm107_ram { + struct nvkm_ram base; +}; + +static int +gm107_ram_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gm107_ram *ram; + int ret; + + ret = gf100_ram_create(parent, engine, oclass, 0x021c14, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + return 0; +} + +struct nvkm_oclass +gm107_ram_oclass = { + .handle = 0, + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gm107_ram_ctor, + .dtor = _nvkm_ram_dtor, + .init = gk104_ram_init, + .fini = _nvkm_ram_fini, + } +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgt215.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgt215.c new file mode 100644 index 000000000..24176401b --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgt215.c @@ -0,0 +1,1012 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + * Roy Spliet + */ + +#include "ramfuc.h" +#include "nv50.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +/* XXX: Remove when memx gains GPIO support */ +extern int nv50_gpio_location(int line, u32 *reg, u32 *shift); + +struct gt215_ramfuc { + struct ramfuc base; + struct ramfuc_reg r_0x001610; + struct ramfuc_reg r_0x001700; + struct ramfuc_reg r_0x002504; + struct ramfuc_reg r_0x004000; + struct ramfuc_reg r_0x004004; + struct ramfuc_reg r_0x004018; + struct ramfuc_reg r_0x004128; + struct ramfuc_reg r_0x004168; + struct ramfuc_reg r_0x100080; + struct ramfuc_reg r_0x100200; + struct ramfuc_reg r_0x100210; + struct ramfuc_reg r_0x100220[9]; + struct ramfuc_reg r_0x100264; + struct ramfuc_reg r_0x1002d0; + struct ramfuc_reg r_0x1002d4; + struct ramfuc_reg r_0x1002dc; + struct ramfuc_reg r_0x10053c; + struct ramfuc_reg r_0x1005a0; + struct ramfuc_reg r_0x1005a4; + struct ramfuc_reg r_0x100700; + struct ramfuc_reg r_0x100714; + struct ramfuc_reg r_0x100718; + struct ramfuc_reg r_0x10071c; + struct ramfuc_reg r_0x100720; + struct ramfuc_reg r_0x100760; + struct ramfuc_reg r_0x1007a0; + struct ramfuc_reg r_0x1007e0; + struct ramfuc_reg r_0x100da0; + struct ramfuc_reg r_0x10f804; + struct ramfuc_reg r_0x1110e0; + struct ramfuc_reg r_0x111100; + struct ramfuc_reg r_0x111104; + struct ramfuc_reg r_0x1111e0; + struct ramfuc_reg r_0x111400; + struct ramfuc_reg r_0x611200; + struct ramfuc_reg r_mr[4]; + struct ramfuc_reg r_gpioFBVREF; +}; + +struct gt215_ltrain { + enum { + NVA3_TRAIN_UNKNOWN, + NVA3_TRAIN_UNSUPPORTED, + NVA3_TRAIN_ONCE, + NVA3_TRAIN_EXEC, + NVA3_TRAIN_DONE + } state; + u32 r_100720; + u32 r_1111e0; + u32 r_111400; + struct nvkm_mem *mem; +}; + +struct gt215_ram { + struct nvkm_ram base; + struct gt215_ramfuc fuc; + struct gt215_ltrain ltrain; +}; + +void +gt215_link_train_calc(u32 *vals, struct gt215_ltrain *train) +{ + int i, lo, hi; + u8 median[8], bins[4] = {0, 0, 0, 0}, bin = 0, qty = 0; + + for (i = 0; i < 8; i++) { + for (lo = 0; lo < 0x40; lo++) { + if (!(vals[lo] & 0x80000000)) + continue; + if (vals[lo] & (0x101 << i)) + break; + } + + if (lo == 0x40) + return; + + for (hi = lo + 1; hi < 0x40; hi++) { + if (!(vals[lo] & 0x80000000)) + continue; + if (!(vals[hi] & (0x101 << i))) { + hi--; + break; + } + } + + median[i] = ((hi - lo) >> 1) + lo; + bins[(median[i] & 0xf0) >> 4]++; + median[i] += 0x30; + } + + /* Find the best value for 0x1111e0 */ + for (i = 0; i < 4; i++) { + if (bins[i] > qty) { + bin = i + 3; + qty = bins[i]; + } + } + + train->r_100720 = 0; + for (i = 0; i < 8; i++) { + median[i] = max(median[i], (u8) (bin << 4)); + median[i] = min(median[i], (u8) ((bin << 4) | 0xf)); + + train->r_100720 |= ((median[i] & 0x0f) << (i << 2)); + } + + train->r_1111e0 = 0x02000000 | (bin * 0x101); + train->r_111400 = 0x0; +} + +/* + * Link training for (at least) DDR3 + */ +int +gt215_link_train(struct nvkm_fb *pfb) +{ + struct nvkm_bios *bios = nvkm_bios(pfb); + struct gt215_ram *ram = (void *)pfb->ram; + struct nvkm_clk *clk = nvkm_clk(pfb); + struct gt215_ltrain *train = &ram->ltrain; + struct nvkm_device *device = nv_device(pfb); + struct gt215_ramfuc *fuc = &ram->fuc; + u32 *result, r1700; + int ret, i; + struct nvbios_M0205T M0205T = { 0 }; + u8 ver, hdr, cnt, len, snr, ssz; + unsigned int clk_current; + unsigned long flags; + unsigned long *f = &flags; + + if (nvkm_boolopt(device->cfgopt, "NvMemExec", true) != true) + return -ENOSYS; + + /* XXX: Multiple partitions? */ + result = kmalloc(64 * sizeof(u32), GFP_KERNEL); + if (!result) + return -ENOMEM; + + train->state = NVA3_TRAIN_EXEC; + + /* Clock speeds for training and back */ + nvbios_M0205Tp(bios, &ver, &hdr, &cnt, &len, &snr, &ssz, &M0205T); + if (M0205T.freq == 0) + return -ENOENT; + + clk_current = clk->read(clk, nv_clk_src_mem); + + ret = gt215_clk_pre(clk, f); + if (ret) + goto out; + + /* First: clock up/down */ + ret = ram->base.calc(pfb, (u32) M0205T.freq * 1000); + if (ret) + goto out; + + /* Do this *after* calc, eliminates write in script */ + nv_wr32(pfb, 0x111400, 0x00000000); + /* XXX: Magic writes that improve train reliability? */ + nv_mask(pfb, 0x100674, 0x0000ffff, 0x00000000); + nv_mask(pfb, 0x1005e4, 0x0000ffff, 0x00000000); + nv_mask(pfb, 0x100b0c, 0x000000ff, 0x00000000); + nv_wr32(pfb, 0x100c04, 0x00000400); + + /* Now the training script */ + r1700 = ram_rd32(fuc, 0x001700); + + ram_mask(fuc, 0x100200, 0x00000800, 0x00000000); + ram_wr32(fuc, 0x611200, 0x3300); + ram_wait_vblank(fuc); + ram_wait(fuc, 0x611200, 0x00000003, 0x00000000, 500000); + ram_mask(fuc, 0x001610, 0x00000083, 0x00000003); + ram_mask(fuc, 0x100080, 0x00000020, 0x00000000); + ram_mask(fuc, 0x10f804, 0x80000000, 0x00000000); + ram_wr32(fuc, 0x001700, 0x00000000); + + ram_train(fuc); + + /* Reset */ + ram_mask(fuc, 0x10f804, 0x80000000, 0x80000000); + ram_wr32(fuc, 0x10053c, 0x0); + ram_wr32(fuc, 0x100720, train->r_100720); + ram_wr32(fuc, 0x1111e0, train->r_1111e0); + ram_wr32(fuc, 0x111400, train->r_111400); + ram_nuke(fuc, 0x100080); + ram_mask(fuc, 0x100080, 0x00000020, 0x00000020); + ram_nsec(fuc, 1000); + + ram_wr32(fuc, 0x001700, r1700); + ram_mask(fuc, 0x001610, 0x00000083, 0x00000080); + ram_wr32(fuc, 0x611200, 0x3330); + ram_mask(fuc, 0x100200, 0x00000800, 0x00000800); + + ram_exec(fuc, true); + + ram->base.calc(pfb, clk_current); + ram_exec(fuc, true); + + /* Post-processing, avoids flicker */ + nv_mask(pfb, 0x616308, 0x10, 0x10); + nv_mask(pfb, 0x616b08, 0x10, 0x10); + + gt215_clk_post(clk, f); + + ram_train_result(pfb, result, 64); + for (i = 0; i < 64; i++) + nv_debug(pfb, "Train: %08x", result[i]); + gt215_link_train_calc(result, train); + + nv_debug(pfb, "Train: %08x %08x %08x", train->r_100720, + train->r_1111e0, train->r_111400); + + kfree(result); + + train->state = NVA3_TRAIN_DONE; + + return ret; + +out: + if(ret == -EBUSY) + f = NULL; + + train->state = NVA3_TRAIN_UNSUPPORTED; + + gt215_clk_post(clk, f); + return ret; +} + +int +gt215_link_train_init(struct nvkm_fb *pfb) +{ + static const u32 pattern[16] = { + 0xaaaaaaaa, 0xcccccccc, 0xdddddddd, 0xeeeeeeee, + 0x00000000, 0x11111111, 0x44444444, 0xdddddddd, + 0x33333333, 0x55555555, 0x77777777, 0x66666666, + 0x99999999, 0x88888888, 0xeeeeeeee, 0xbbbbbbbb, + }; + struct nvkm_bios *bios = nvkm_bios(pfb); + struct gt215_ram *ram = (void *)pfb->ram; + struct gt215_ltrain *train = &ram->ltrain; + struct nvkm_mem *mem; + struct nvbios_M0205E M0205E; + u8 ver, hdr, cnt, len; + u32 r001700; + int ret, i = 0; + + train->state = NVA3_TRAIN_UNSUPPORTED; + + /* We support type "5" + * XXX: training pattern table appears to be unused for this routine */ + if (!nvbios_M0205Ep(bios, i, &ver, &hdr, &cnt, &len, &M0205E)) + return -ENOENT; + + if (M0205E.type != 5) + return 0; + + train->state = NVA3_TRAIN_ONCE; + + ret = pfb->ram->get(pfb, 0x8000, 0x10000, 0, 0x800, &ram->ltrain.mem); + if (ret) + return ret; + + mem = ram->ltrain.mem; + + nv_wr32(pfb, 0x100538, 0x10000000 | (mem->offset >> 16)); + nv_wr32(pfb, 0x1005a8, 0x0000ffff); + nv_mask(pfb, 0x10f800, 0x00000001, 0x00000001); + + for (i = 0; i < 0x30; i++) { + nv_wr32(pfb, 0x10f8c0, (i << 8) | i); + nv_wr32(pfb, 0x10f900, pattern[i % 16]); + } + + for (i = 0; i < 0x30; i++) { + nv_wr32(pfb, 0x10f8e0, (i << 8) | i); + nv_wr32(pfb, 0x10f920, pattern[i % 16]); + } + + /* And upload the pattern */ + r001700 = nv_rd32(pfb, 0x1700); + nv_wr32(pfb, 0x1700, mem->offset >> 16); + for (i = 0; i < 16; i++) + nv_wr32(pfb, 0x700000 + (i << 2), pattern[i]); + for (i = 0; i < 16; i++) + nv_wr32(pfb, 0x700100 + (i << 2), pattern[i]); + nv_wr32(pfb, 0x1700, r001700); + + train->r_100720 = nv_rd32(pfb, 0x100720); + train->r_1111e0 = nv_rd32(pfb, 0x1111e0); + train->r_111400 = nv_rd32(pfb, 0x111400); + return 0; +} + +void +gt215_link_train_fini(struct nvkm_fb *pfb) +{ + struct gt215_ram *ram = (void *)pfb->ram; + + if (ram->ltrain.mem) + pfb->ram->put(pfb, &ram->ltrain.mem); +} + +/* + * RAM reclocking + */ +#define T(t) cfg->timing_10_##t +static int +gt215_ram_timing_calc(struct nvkm_fb *pfb, u32 *timing) +{ + struct gt215_ram *ram = (void *)pfb->ram; + struct nvbios_ramcfg *cfg = &ram->base.target.bios; + int tUNK_base, tUNK_40_0, prevCL; + u32 cur2, cur3, cur7, cur8; + + cur2 = nv_rd32(pfb, 0x100228); + cur3 = nv_rd32(pfb, 0x10022c); + cur7 = nv_rd32(pfb, 0x10023c); + cur8 = nv_rd32(pfb, 0x100240); + + + switch ((!T(CWL)) * ram->base.type) { + case NV_MEM_TYPE_DDR2: + T(CWL) = T(CL) - 1; + break; + case NV_MEM_TYPE_GDDR3: + T(CWL) = ((cur2 & 0xff000000) >> 24) + 1; + break; + } + + prevCL = (cur3 & 0x000000ff) + 1; + tUNK_base = ((cur7 & 0x00ff0000) >> 16) - prevCL; + + timing[0] = (T(RP) << 24 | T(RAS) << 16 | T(RFC) << 8 | T(RC)); + timing[1] = (T(WR) + 1 + T(CWL)) << 24 | + max_t(u8,T(18), 1) << 16 | + (T(WTR) + 1 + T(CWL)) << 8 | + (5 + T(CL) - T(CWL)); + timing[2] = (T(CWL) - 1) << 24 | + (T(RRD) << 16) | + (T(RCDWR) << 8) | + T(RCDRD); + timing[3] = (cur3 & 0x00ff0000) | + (0x30 + T(CL)) << 24 | + (0xb + T(CL)) << 8 | + (T(CL) - 1); + timing[4] = T(20) << 24 | + T(21) << 16 | + T(13) << 8 | + T(13); + timing[5] = T(RFC) << 24 | + max_t(u8,T(RCDRD), T(RCDWR)) << 16 | + max_t(u8, (T(CWL) + 6), (T(CL) + 2)) << 8 | + T(RP); + timing[6] = (0x5a + T(CL)) << 16 | + max_t(u8, 1, (6 - T(CL) + T(CWL))) << 8 | + (0x50 + T(CL) - T(CWL)); + timing[7] = (cur7 & 0xff000000) | + ((tUNK_base + T(CL)) << 16) | + 0x202; + timing[8] = cur8 & 0xffffff00; + + switch (ram->base.type) { + case NV_MEM_TYPE_DDR2: + case NV_MEM_TYPE_GDDR3: + tUNK_40_0 = prevCL - (cur8 & 0xff); + if (tUNK_40_0 > 0) + timing[8] |= T(CL); + break; + default: + break; + } + + nv_debug(pfb, "Entry: 220: %08x %08x %08x %08x\n", + timing[0], timing[1], timing[2], timing[3]); + nv_debug(pfb, " 230: %08x %08x %08x %08x\n", + timing[4], timing[5], timing[6], timing[7]); + nv_debug(pfb, " 240: %08x\n", timing[8]); + return 0; +} +#undef T + +static void +nvkm_sddr2_dll_reset(struct gt215_ramfuc *fuc) +{ + ram_mask(fuc, mr[0], 0x100, 0x100); + ram_nsec(fuc, 1000); + ram_mask(fuc, mr[0], 0x100, 0x000); + ram_nsec(fuc, 1000); +} + +static void +nvkm_sddr3_dll_disable(struct gt215_ramfuc *fuc, u32 *mr) +{ + u32 mr1_old = ram_rd32(fuc, mr[1]); + + if (!(mr1_old & 0x1)) { + ram_wr32(fuc, 0x1002d4, 0x00000001); + ram_wr32(fuc, mr[1], mr[1]); + ram_nsec(fuc, 1000); + } +} + +static void +nvkm_gddr3_dll_disable(struct gt215_ramfuc *fuc, u32 *mr) +{ + u32 mr1_old = ram_rd32(fuc, mr[1]); + + if (!(mr1_old & 0x40)) { + ram_wr32(fuc, mr[1], mr[1]); + ram_nsec(fuc, 1000); + } +} + +static void +gt215_ram_lock_pll(struct gt215_ramfuc *fuc, struct gt215_clk_info *mclk) +{ + ram_wr32(fuc, 0x004004, mclk->pll); + ram_mask(fuc, 0x004000, 0x00000001, 0x00000001); + ram_mask(fuc, 0x004000, 0x00000010, 0x00000000); + ram_wait(fuc, 0x004000, 0x00020000, 0x00020000, 64000); + ram_mask(fuc, 0x004000, 0x00000010, 0x00000010); +} + +static void +gt215_ram_fbvref(struct gt215_ramfuc *fuc, u32 val) +{ + struct nvkm_gpio *gpio = nvkm_gpio(fuc->base.pfb); + struct dcb_gpio_func func; + u32 reg, sh, gpio_val; + int ret; + + if (gpio->get(gpio, 0, 0x2e, DCB_GPIO_UNUSED) != val) { + ret = gpio->find(gpio, 0, 0x2e, DCB_GPIO_UNUSED, &func); + if (ret) + return; + + nv50_gpio_location(func.line, ®, &sh); + gpio_val = ram_rd32(fuc, gpioFBVREF); + if (gpio_val & (8 << sh)) + val = !val; + + ram_mask(fuc, gpioFBVREF, (0x3 << sh), ((val | 0x2) << sh)); + ram_nsec(fuc, 20000); + } +} + +static int +gt215_ram_calc(struct nvkm_fb *pfb, u32 freq) +{ + struct nvkm_bios *bios = nvkm_bios(pfb); + struct gt215_ram *ram = (void *)pfb->ram; + struct gt215_ramfuc *fuc = &ram->fuc; + struct gt215_ltrain *train = &ram->ltrain; + struct gt215_clk_info mclk; + struct nvkm_ram_data *next; + u8 ver, hdr, cnt, len, strap; + u32 data; + u32 r004018, r100760, r100da0, r111100, ctrl; + u32 unk714, unk718, unk71c; + int ret, i; + u32 timing[9]; + bool pll2pll; + + next = &ram->base.target; + next->freq = freq; + ram->base.next = next; + + if (ram->ltrain.state == NVA3_TRAIN_ONCE) + gt215_link_train(pfb); + + /* lookup memory config data relevant to the target frequency */ + i = 0; + data = nvbios_rammapEm(bios, freq / 1000, &ver, &hdr, &cnt, &len, + &next->bios); + if (!data || ver != 0x10 || hdr < 0x05) { + nv_error(pfb, "invalid/missing rammap entry\n"); + return -EINVAL; + } + + /* locate specific data set for the attached memory */ + strap = nvbios_ramcfg_index(nv_subdev(pfb)); + if (strap >= cnt) { + nv_error(pfb, "invalid ramcfg strap\n"); + return -EINVAL; + } + + data = nvbios_rammapSp(bios, data, ver, hdr, cnt, len, strap, + &ver, &hdr, &next->bios); + if (!data || ver != 0x10 || hdr < 0x09) { + nv_error(pfb, "invalid/missing ramcfg entry\n"); + return -EINVAL; + } + + /* lookup memory timings, if bios says they're present */ + if (next->bios.ramcfg_timing != 0xff) { + data = nvbios_timingEp(bios, next->bios.ramcfg_timing, + &ver, &hdr, &cnt, &len, + &next->bios); + if (!data || ver != 0x10 || hdr < 0x17) { + nv_error(pfb, "invalid/missing timing entry\n"); + return -EINVAL; + } + } + + ret = gt215_pll_info(nvkm_clk(pfb), 0x12, 0x4000, freq, &mclk); + if (ret < 0) { + nv_error(pfb, "failed mclk calculation\n"); + return ret; + } + + gt215_ram_timing_calc(pfb, timing); + + ret = ram_init(fuc, pfb); + if (ret) + return ret; + + /* Determine ram-specific MR values */ + ram->base.mr[0] = ram_rd32(fuc, mr[0]); + ram->base.mr[1] = ram_rd32(fuc, mr[1]); + ram->base.mr[2] = ram_rd32(fuc, mr[2]); + + switch (ram->base.type) { + case NV_MEM_TYPE_DDR2: + ret = nvkm_sddr2_calc(&ram->base); + break; + case NV_MEM_TYPE_DDR3: + ret = nvkm_sddr3_calc(&ram->base); + break; + case NV_MEM_TYPE_GDDR3: + ret = nvkm_gddr3_calc(&ram->base); + break; + default: + ret = -ENOSYS; + break; + } + + if (ret) + return ret; + + /* XXX: where the fuck does 750MHz come from? */ + if (freq <= 750000) { + r004018 = 0x10000000; + r100760 = 0x22222222; + r100da0 = 0x00000010; + } else { + r004018 = 0x00000000; + r100760 = 0x00000000; + r100da0 = 0x00000000; + } + + if (!next->bios.ramcfg_10_DLLoff) + r004018 |= 0x00004000; + + /* pll2pll requires to switch to a safe clock first */ + ctrl = ram_rd32(fuc, 0x004000); + pll2pll = (!(ctrl & 0x00000008)) && mclk.pll; + + /* Pre, NVIDIA does this outside the script */ + if (next->bios.ramcfg_10_02_10) { + ram_mask(fuc, 0x111104, 0x00000600, 0x00000000); + } else { + ram_mask(fuc, 0x111100, 0x40000000, 0x40000000); + ram_mask(fuc, 0x111104, 0x00000180, 0x00000000); + } + /* Always disable this bit during reclock */ + ram_mask(fuc, 0x100200, 0x00000800, 0x00000000); + + /* If switching from non-pll to pll, lock before disabling FB */ + if (mclk.pll && !pll2pll) { + ram_mask(fuc, 0x004128, 0x003f3141, mclk.clk | 0x00000101); + gt215_ram_lock_pll(fuc, &mclk); + } + + /* Start with disabling some CRTCs and PFIFO? */ + ram_wait_vblank(fuc); + ram_wr32(fuc, 0x611200, 0x3300); + ram_mask(fuc, 0x002504, 0x1, 0x1); + ram_nsec(fuc, 10000); + ram_wait(fuc, 0x002504, 0x10, 0x10, 20000); /* XXX: or longer? */ + ram_block(fuc); + ram_nsec(fuc, 2000); + + if (!next->bios.ramcfg_10_02_10) { + if (ram->base.type == NV_MEM_TYPE_GDDR3) + ram_mask(fuc, 0x111100, 0x04020000, 0x00020000); + else + ram_mask(fuc, 0x111100, 0x04020000, 0x04020000); + } + + /* If we're disabling the DLL, do it now */ + switch (next->bios.ramcfg_10_DLLoff * ram->base.type) { + case NV_MEM_TYPE_DDR3: + nvkm_sddr3_dll_disable(fuc, ram->base.mr); + break; + case NV_MEM_TYPE_GDDR3: + nvkm_gddr3_dll_disable(fuc, ram->base.mr); + break; + } + + if (fuc->r_gpioFBVREF.addr && next->bios.timing_10_ODT) + gt215_ram_fbvref(fuc, 0); + + /* Brace RAM for impact */ + ram_wr32(fuc, 0x1002d4, 0x00000001); + ram_wr32(fuc, 0x1002d0, 0x00000001); + ram_wr32(fuc, 0x1002d0, 0x00000001); + ram_wr32(fuc, 0x100210, 0x00000000); + ram_wr32(fuc, 0x1002dc, 0x00000001); + ram_nsec(fuc, 2000); + + if (nv_device(pfb)->chipset == 0xa3 && freq <= 500000) + ram_mask(fuc, 0x100700, 0x00000006, 0x00000006); + + /* Fiddle with clocks */ + /* There's 4 scenario's + * pll->pll: first switch to a 324MHz clock, set up new PLL, switch + * clk->pll: Set up new PLL, switch + * pll->clk: Set up clock, switch + * clk->clk: Overwrite ctrl and other bits, switch */ + + /* Switch to regular clock - 324MHz */ + if (pll2pll) { + ram_mask(fuc, 0x004000, 0x00000004, 0x00000004); + ram_mask(fuc, 0x004168, 0x003f3141, 0x00083101); + ram_mask(fuc, 0x004000, 0x00000008, 0x00000008); + ram_mask(fuc, 0x1110e0, 0x00088000, 0x00088000); + ram_wr32(fuc, 0x004018, 0x00001000); + gt215_ram_lock_pll(fuc, &mclk); + } + + if (mclk.pll) { + ram_mask(fuc, 0x004000, 0x00000105, 0x00000105); + ram_wr32(fuc, 0x004018, 0x00001000 | r004018); + ram_wr32(fuc, 0x100da0, r100da0); + } else { + ram_mask(fuc, 0x004168, 0x003f3141, mclk.clk | 0x00000101); + ram_mask(fuc, 0x004000, 0x00000108, 0x00000008); + ram_mask(fuc, 0x1110e0, 0x00088000, 0x00088000); + ram_wr32(fuc, 0x004018, 0x00009000 | r004018); + ram_wr32(fuc, 0x100da0, r100da0); + } + ram_nsec(fuc, 20000); + + if (next->bios.rammap_10_04_08) { + ram_wr32(fuc, 0x1005a0, next->bios.ramcfg_10_06 << 16 | + next->bios.ramcfg_10_05 << 8 | + next->bios.ramcfg_10_05); + ram_wr32(fuc, 0x1005a4, next->bios.ramcfg_10_08 << 8 | + next->bios.ramcfg_10_07); + ram_wr32(fuc, 0x10f804, next->bios.ramcfg_10_09_f0 << 20 | + next->bios.ramcfg_10_03_0f << 16 | + next->bios.ramcfg_10_09_0f | + 0x80000000); + ram_mask(fuc, 0x10053c, 0x00001000, 0x00000000); + } else { + if (train->state == NVA3_TRAIN_DONE) { + ram_wr32(fuc, 0x100080, 0x1020); + ram_mask(fuc, 0x111400, 0xffffffff, train->r_111400); + ram_mask(fuc, 0x1111e0, 0xffffffff, train->r_1111e0); + ram_mask(fuc, 0x100720, 0xffffffff, train->r_100720); + } + ram_mask(fuc, 0x10053c, 0x00001000, 0x00001000); + ram_mask(fuc, 0x10f804, 0x80000000, 0x00000000); + ram_mask(fuc, 0x100760, 0x22222222, r100760); + ram_mask(fuc, 0x1007a0, 0x22222222, r100760); + ram_mask(fuc, 0x1007e0, 0x22222222, r100760); + } + + if (nv_device(pfb)->chipset == 0xa3 && freq > 500000) { + ram_mask(fuc, 0x100700, 0x00000006, 0x00000000); + } + + /* Final switch */ + if (mclk.pll) { + ram_mask(fuc, 0x1110e0, 0x00088000, 0x00011000); + ram_mask(fuc, 0x004000, 0x00000008, 0x00000000); + } + + ram_wr32(fuc, 0x1002dc, 0x00000000); + ram_wr32(fuc, 0x1002d4, 0x00000001); + ram_wr32(fuc, 0x100210, 0x80000000); + ram_nsec(fuc, 2000); + + /* Set RAM MR parameters and timings */ + for (i = 2; i >= 0; i--) { + if (ram_rd32(fuc, mr[i]) != ram->base.mr[i]) { + ram_wr32(fuc, mr[i], ram->base.mr[i]); + ram_nsec(fuc, 1000); + } + } + + ram_wr32(fuc, 0x100220[3], timing[3]); + ram_wr32(fuc, 0x100220[1], timing[1]); + ram_wr32(fuc, 0x100220[6], timing[6]); + ram_wr32(fuc, 0x100220[7], timing[7]); + ram_wr32(fuc, 0x100220[2], timing[2]); + ram_wr32(fuc, 0x100220[4], timing[4]); + ram_wr32(fuc, 0x100220[5], timing[5]); + ram_wr32(fuc, 0x100220[0], timing[0]); + ram_wr32(fuc, 0x100220[8], timing[8]); + + /* Misc */ + ram_mask(fuc, 0x100200, 0x00001000, !next->bios.ramcfg_10_02_08 << 12); + + /* XXX: A lot of "chipset"/"ram type" specific stuff...? */ + unk714 = ram_rd32(fuc, 0x100714) & ~0xf0000130; + unk718 = ram_rd32(fuc, 0x100718) & ~0x00000100; + unk71c = ram_rd32(fuc, 0x10071c) & ~0x00000100; + r111100 = ram_rd32(fuc, 0x111100) & ~0x3a800000; + + if (next->bios.ramcfg_10_02_04) { + switch (ram->base.type) { + case NV_MEM_TYPE_DDR3: + if (nv_device(pfb)->chipset != 0xa8) + r111100 |= 0x00000004; + /* no break */ + case NV_MEM_TYPE_DDR2: + r111100 |= 0x08000000; + break; + default: + break; + } + } else { + switch (ram->base.type) { + case NV_MEM_TYPE_DDR2: + r111100 |= 0x1a800000; + unk714 |= 0x00000010; + break; + case NV_MEM_TYPE_DDR3: + if (nv_device(pfb)->chipset == 0xa8) { + r111100 |= 0x08000000; + } else { + r111100 &= ~0x00000004; + r111100 |= 0x12800000; + } + unk714 |= 0x00000010; + break; + case NV_MEM_TYPE_GDDR3: + r111100 |= 0x30000000; + unk714 |= 0x00000020; + break; + default: + break; + } + } + + unk714 |= (next->bios.ramcfg_10_04_01) << 8; + + if (next->bios.ramcfg_10_02_20) + unk714 |= 0xf0000000; + if (next->bios.ramcfg_10_02_02) + unk718 |= 0x00000100; + if (next->bios.ramcfg_10_02_01) + unk71c |= 0x00000100; + if (next->bios.timing_10_24 != 0xff) { + unk718 &= ~0xf0000000; + unk718 |= next->bios.timing_10_24 << 28; + } + if (next->bios.ramcfg_10_02_10) + r111100 &= ~0x04020000; + + ram_mask(fuc, 0x100714, 0xffffffff, unk714); + ram_mask(fuc, 0x10071c, 0xffffffff, unk71c); + ram_mask(fuc, 0x100718, 0xffffffff, unk718); + ram_mask(fuc, 0x111100, 0xffffffff, r111100); + + if (fuc->r_gpioFBVREF.addr && !next->bios.timing_10_ODT) + gt215_ram_fbvref(fuc, 1); + + /* Reset DLL */ + if (!next->bios.ramcfg_10_DLLoff) + nvkm_sddr2_dll_reset(fuc); + + if (ram->base.type == NV_MEM_TYPE_GDDR3) { + ram_nsec(fuc, 31000); + } else { + ram_nsec(fuc, 14000); + } + + if (ram->base.type == NV_MEM_TYPE_DDR3) { + ram_wr32(fuc, 0x100264, 0x1); + ram_nsec(fuc, 2000); + } + + ram_nuke(fuc, 0x100700); + ram_mask(fuc, 0x100700, 0x01000000, 0x01000000); + ram_mask(fuc, 0x100700, 0x01000000, 0x00000000); + + /* Re-enable FB */ + ram_unblock(fuc); + ram_wr32(fuc, 0x611200, 0x3330); + + /* Post fiddlings */ + if (next->bios.rammap_10_04_02) + ram_mask(fuc, 0x100200, 0x00000800, 0x00000800); + if (next->bios.ramcfg_10_02_10) { + ram_mask(fuc, 0x111104, 0x00000180, 0x00000180); + ram_mask(fuc, 0x111100, 0x40000000, 0x00000000); + } else { + ram_mask(fuc, 0x111104, 0x00000600, 0x00000600); + } + + if (mclk.pll) { + ram_mask(fuc, 0x004168, 0x00000001, 0x00000000); + ram_mask(fuc, 0x004168, 0x00000100, 0x00000000); + } else { + ram_mask(fuc, 0x004000, 0x00000001, 0x00000000); + ram_mask(fuc, 0x004128, 0x00000001, 0x00000000); + ram_mask(fuc, 0x004128, 0x00000100, 0x00000000); + } + + return 0; +} + +static int +gt215_ram_prog(struct nvkm_fb *pfb) +{ + struct nvkm_device *device = nv_device(pfb); + struct gt215_ram *ram = (void *)pfb->ram; + struct gt215_ramfuc *fuc = &ram->fuc; + bool exec = nvkm_boolopt(device->cfgopt, "NvMemExec", true); + + if (exec) { + nv_mask(pfb, 0x001534, 0x2, 0x2); + + ram_exec(fuc, true); + + /* Post-processing, avoids flicker */ + nv_mask(pfb, 0x002504, 0x1, 0x0); + nv_mask(pfb, 0x001534, 0x2, 0x0); + + nv_mask(pfb, 0x616308, 0x10, 0x10); + nv_mask(pfb, 0x616b08, 0x10, 0x10); + } else { + ram_exec(fuc, false); + } + return 0; +} + +static void +gt215_ram_tidy(struct nvkm_fb *pfb) +{ + struct gt215_ram *ram = (void *)pfb->ram; + struct gt215_ramfuc *fuc = &ram->fuc; + ram_exec(fuc, false); +} + +static int +gt215_ram_init(struct nvkm_object *object) +{ + struct nvkm_fb *pfb = (void *)object->parent; + struct gt215_ram *ram = (void *)object; + int ret; + + ret = nvkm_ram_init(&ram->base); + if (ret) + return ret; + + gt215_link_train_init(pfb); + return 0; +} + +static int +gt215_ram_fini(struct nvkm_object *object, bool suspend) +{ + struct nvkm_fb *pfb = (void *)object->parent; + + if (!suspend) + gt215_link_train_fini(pfb); + + return 0; +} + +static int +gt215_ram_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 datasize, + struct nvkm_object **pobject) +{ + struct nvkm_fb *pfb = nvkm_fb(parent); + struct nvkm_gpio *gpio = nvkm_gpio(pfb); + struct dcb_gpio_func func; + struct gt215_ram *ram; + int ret, i; + u32 reg, shift; + + ret = nv50_ram_create(parent, engine, oclass, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + switch (ram->base.type) { + case NV_MEM_TYPE_DDR2: + case NV_MEM_TYPE_DDR3: + case NV_MEM_TYPE_GDDR3: + ram->base.calc = gt215_ram_calc; + ram->base.prog = gt215_ram_prog; + ram->base.tidy = gt215_ram_tidy; + break; + default: + nv_warn(ram, "reclocking of this ram type unsupported\n"); + return 0; + } + + ram->fuc.r_0x001610 = ramfuc_reg(0x001610); + ram->fuc.r_0x001700 = ramfuc_reg(0x001700); + ram->fuc.r_0x002504 = ramfuc_reg(0x002504); + ram->fuc.r_0x004000 = ramfuc_reg(0x004000); + ram->fuc.r_0x004004 = ramfuc_reg(0x004004); + ram->fuc.r_0x004018 = ramfuc_reg(0x004018); + ram->fuc.r_0x004128 = ramfuc_reg(0x004128); + ram->fuc.r_0x004168 = ramfuc_reg(0x004168); + ram->fuc.r_0x100080 = ramfuc_reg(0x100080); + ram->fuc.r_0x100200 = ramfuc_reg(0x100200); + ram->fuc.r_0x100210 = ramfuc_reg(0x100210); + for (i = 0; i < 9; i++) + ram->fuc.r_0x100220[i] = ramfuc_reg(0x100220 + (i * 4)); + ram->fuc.r_0x100264 = ramfuc_reg(0x100264); + ram->fuc.r_0x1002d0 = ramfuc_reg(0x1002d0); + ram->fuc.r_0x1002d4 = ramfuc_reg(0x1002d4); + ram->fuc.r_0x1002dc = ramfuc_reg(0x1002dc); + ram->fuc.r_0x10053c = ramfuc_reg(0x10053c); + ram->fuc.r_0x1005a0 = ramfuc_reg(0x1005a0); + ram->fuc.r_0x1005a4 = ramfuc_reg(0x1005a4); + ram->fuc.r_0x100700 = ramfuc_reg(0x100700); + ram->fuc.r_0x100714 = ramfuc_reg(0x100714); + ram->fuc.r_0x100718 = ramfuc_reg(0x100718); + ram->fuc.r_0x10071c = ramfuc_reg(0x10071c); + ram->fuc.r_0x100720 = ramfuc_reg(0x100720); + ram->fuc.r_0x100760 = ramfuc_stride(0x100760, 4, ram->base.part_mask); + ram->fuc.r_0x1007a0 = ramfuc_stride(0x1007a0, 4, ram->base.part_mask); + ram->fuc.r_0x1007e0 = ramfuc_stride(0x1007e0, 4, ram->base.part_mask); + ram->fuc.r_0x100da0 = ramfuc_stride(0x100da0, 4, ram->base.part_mask); + ram->fuc.r_0x10f804 = ramfuc_reg(0x10f804); + ram->fuc.r_0x1110e0 = ramfuc_stride(0x1110e0, 4, ram->base.part_mask); + ram->fuc.r_0x111100 = ramfuc_reg(0x111100); + ram->fuc.r_0x111104 = ramfuc_reg(0x111104); + ram->fuc.r_0x1111e0 = ramfuc_reg(0x1111e0); + ram->fuc.r_0x111400 = ramfuc_reg(0x111400); + ram->fuc.r_0x611200 = ramfuc_reg(0x611200); + + if (ram->base.ranks > 1) { + ram->fuc.r_mr[0] = ramfuc_reg2(0x1002c0, 0x1002c8); + ram->fuc.r_mr[1] = ramfuc_reg2(0x1002c4, 0x1002cc); + ram->fuc.r_mr[2] = ramfuc_reg2(0x1002e0, 0x1002e8); + ram->fuc.r_mr[3] = ramfuc_reg2(0x1002e4, 0x1002ec); + } else { + ram->fuc.r_mr[0] = ramfuc_reg(0x1002c0); + ram->fuc.r_mr[1] = ramfuc_reg(0x1002c4); + ram->fuc.r_mr[2] = ramfuc_reg(0x1002e0); + ram->fuc.r_mr[3] = ramfuc_reg(0x1002e4); + } + + ret = gpio->find(gpio, 0, 0x2e, DCB_GPIO_UNUSED, &func); + if (ret == 0) { + nv50_gpio_location(func.line, ®, &shift); + ram->fuc.r_gpioFBVREF = ramfuc_reg(reg); + } + + return 0; +} + +struct nvkm_oclass +gt215_ram_oclass = { + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gt215_ram_ctor, + .dtor = _nvkm_ram_dtor, + .init = gt215_ram_init, + .fini = gt215_ram_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/rammcp77.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/rammcp77.c new file mode 100644 index 000000000..abc18e89a --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/rammcp77.c @@ -0,0 +1,101 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv50.h" + +struct mcp77_ram_priv { + struct nvkm_ram base; + u64 poller_base; +}; + +static int +mcp77_ram_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 datasize, + struct nvkm_object **pobject) +{ + u32 rsvd_head = ( 256 * 1024); /* vga memory */ + u32 rsvd_tail = (1024 * 1024); /* vbios etc */ + struct nvkm_fb *pfb = nvkm_fb(parent); + struct mcp77_ram_priv *priv; + int ret; + + ret = nvkm_ram_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.type = NV_MEM_TYPE_STOLEN; + priv->base.stolen = (u64)nv_rd32(pfb, 0x100e10) << 12; + priv->base.size = (u64)nv_rd32(pfb, 0x100e14) << 12; + + rsvd_tail += 0x1000; + priv->poller_base = priv->base.size - rsvd_tail; + + ret = nvkm_mm_init(&pfb->vram, rsvd_head >> 12, + (priv->base.size - (rsvd_head + rsvd_tail)) >> 12, + 1); + if (ret) + return ret; + + priv->base.get = nv50_ram_get; + priv->base.put = nv50_ram_put; + return 0; +} + +static int +mcp77_ram_init(struct nvkm_object *object) +{ + struct nvkm_fb *pfb = nvkm_fb(object); + struct mcp77_ram_priv *priv = (void *)object; + int ret; + u64 dniso, hostnb, flush; + + ret = nvkm_ram_init(&priv->base); + if (ret) + return ret; + + dniso = ((priv->base.size - (priv->poller_base + 0x00)) >> 5) - 1; + hostnb = ((priv->base.size - (priv->poller_base + 0x20)) >> 5) - 1; + flush = ((priv->base.size - (priv->poller_base + 0x40)) >> 5) - 1; + + /* Enable NISO poller for various clients and set their associated + * read address, only for MCP77/78 and MCP79/7A. (fd#25701) + */ + nv_wr32(pfb, 0x100c18, dniso); + nv_mask(pfb, 0x100c14, 0x00000000, 0x00000001); + nv_wr32(pfb, 0x100c1c, hostnb); + nv_mask(pfb, 0x100c14, 0x00000000, 0x00000002); + nv_wr32(pfb, 0x100c24, flush); + nv_mask(pfb, 0x100c14, 0x00000000, 0x00010000); + return 0; +} + +struct nvkm_oclass +mcp77_ram_oclass = { + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = mcp77_ram_ctor, + .dtor = _nvkm_ram_dtor, + .init = mcp77_ram_init, + .fini = _nvkm_ram_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv04.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv04.c new file mode 100644 index 000000000..855de1617 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv04.c @@ -0,0 +1,79 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "regsnv04.h" + +static int +nv04_ram_create(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_fb *pfb = nvkm_fb(parent); + struct nvkm_ram *ram; + u32 boot0 = nv_rd32(pfb, NV04_PFB_BOOT_0); + int ret; + + ret = nvkm_ram_create(parent, engine, oclass, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + if (boot0 & 0x00000100) { + ram->size = ((boot0 >> 12) & 0xf) * 2 + 2; + ram->size *= 1024 * 1024; + } else { + switch (boot0 & NV04_PFB_BOOT_0_RAM_AMOUNT) { + case NV04_PFB_BOOT_0_RAM_AMOUNT_32MB: + ram->size = 32 * 1024 * 1024; + break; + case NV04_PFB_BOOT_0_RAM_AMOUNT_16MB: + ram->size = 16 * 1024 * 1024; + break; + case NV04_PFB_BOOT_0_RAM_AMOUNT_8MB: + ram->size = 8 * 1024 * 1024; + break; + case NV04_PFB_BOOT_0_RAM_AMOUNT_4MB: + ram->size = 4 * 1024 * 1024; + break; + } + } + + if ((boot0 & 0x00000038) <= 0x10) + ram->type = NV_MEM_TYPE_SGRAM; + else + ram->type = NV_MEM_TYPE_SDRAM; + + return 0; +} + +struct nvkm_oclass +nv04_ram_oclass = { + .handle = 0, + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_ram_create, + .dtor = _nvkm_ram_dtor, + .init = _nvkm_ram_init, + .fini = _nvkm_ram_fini, + } +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv10.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv10.c new file mode 100644 index 000000000..3b8a1eda5 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv10.c @@ -0,0 +1,59 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +static int +nv10_ram_create(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_fb *pfb = nvkm_fb(parent); + struct nvkm_ram *ram; + u32 cfg0 = nv_rd32(pfb, 0x100200); + int ret; + + ret = nvkm_ram_create(parent, engine, oclass, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + if (cfg0 & 0x00000001) + ram->type = NV_MEM_TYPE_DDR1; + else + ram->type = NV_MEM_TYPE_SDRAM; + + ram->size = nv_rd32(pfb, 0x10020c) & 0xff000000; + return 0; +} + +struct nvkm_oclass +nv10_ram_oclass = { + .handle = 0, + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv10_ram_create, + .dtor = _nvkm_ram_dtor, + .init = _nvkm_ram_init, + .fini = _nvkm_ram_fini, + } +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv1a.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv1a.c new file mode 100644 index 000000000..fbae05db4 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv1a.c @@ -0,0 +1,72 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +#include + +static int +nv1a_ram_create(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_fb *pfb = nvkm_fb(parent); + struct nvkm_ram *ram; + struct pci_dev *bridge; + u32 mem, mib; + int ret; + + bridge = pci_get_bus_and_slot(0, PCI_DEVFN(0, 1)); + if (!bridge) { + nv_fatal(pfb, "no bridge device\n"); + return -ENODEV; + } + + ret = nvkm_ram_create(parent, engine, oclass, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + if (nv_device(pfb)->chipset == 0x1a) { + pci_read_config_dword(bridge, 0x7c, &mem); + mib = ((mem >> 6) & 31) + 1; + } else { + pci_read_config_dword(bridge, 0x84, &mem); + mib = ((mem >> 4) & 127) + 1; + } + + ram->type = NV_MEM_TYPE_STOLEN; + ram->size = mib * 1024 * 1024; + return 0; +} + +struct nvkm_oclass +nv1a_ram_oclass = { + .handle = 0, + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv1a_ram_create, + .dtor = _nvkm_ram_dtor, + .init = _nvkm_ram_init, + .fini = _nvkm_ram_fini, + } +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv20.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv20.c new file mode 100644 index 000000000..d9e7187bd --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv20.c @@ -0,0 +1,62 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +static int +nv20_ram_create(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_fb *pfb = nvkm_fb(parent); + struct nvkm_ram *ram; + u32 pbus1218 = nv_rd32(pfb, 0x001218); + int ret; + + ret = nvkm_ram_create(parent, engine, oclass, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + switch (pbus1218 & 0x00000300) { + case 0x00000000: ram->type = NV_MEM_TYPE_SDRAM; break; + case 0x00000100: ram->type = NV_MEM_TYPE_DDR1; break; + case 0x00000200: ram->type = NV_MEM_TYPE_GDDR3; break; + case 0x00000300: ram->type = NV_MEM_TYPE_GDDR2; break; + } + ram->size = (nv_rd32(pfb, 0x10020c) & 0xff000000); + ram->parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1; + ram->tags = nv_rd32(pfb, 0x100320); + return 0; +} + +struct nvkm_oclass +nv20_ram_oclass = { + .handle = 0, + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv20_ram_create, + .dtor = _nvkm_ram_dtor, + .init = _nvkm_ram_init, + .fini = _nvkm_ram_fini, + } +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv40.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv40.c new file mode 100644 index 000000000..3d31fa45c --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv40.c @@ -0,0 +1,212 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv40.h" + +#include +#include +#include +#include +#include +#include +#include + +int +nv40_ram_calc(struct nvkm_fb *pfb, u32 freq) +{ + struct nvkm_bios *bios = nvkm_bios(pfb); + struct nv40_ram *ram = (void *)pfb->ram; + struct nvbios_pll pll; + int N1, M1, N2, M2; + int log2P, ret; + + ret = nvbios_pll_parse(bios, 0x04, &pll); + if (ret) { + nv_error(pfb, "mclk pll data not found\n"); + return ret; + } + + ret = nv04_pll_calc(nv_subdev(pfb), &pll, freq, + &N1, &M1, &N2, &M2, &log2P); + if (ret < 0) + return ret; + + ram->ctrl = 0x80000000 | (log2P << 16); + ram->ctrl |= min(pll.bias_p + log2P, (int)pll.max_p) << 20; + if (N2 == M2) { + ram->ctrl |= 0x00000100; + ram->coef = (N1 << 8) | M1; + } else { + ram->ctrl |= 0x40000000; + ram->coef = (N2 << 24) | (M2 << 16) | (N1 << 8) | M1; + } + + return 0; +} + +int +nv40_ram_prog(struct nvkm_fb *pfb) +{ + struct nvkm_bios *bios = nvkm_bios(pfb); + struct nv40_ram *ram = (void *)pfb->ram; + struct bit_entry M; + u32 crtc_mask = 0; + u8 sr1[2]; + int i; + + /* determine which CRTCs are active, fetch VGA_SR1 for each */ + for (i = 0; i < 2; i++) { + u32 vbl = nv_rd32(pfb, 0x600808 + (i * 0x2000)); + u32 cnt = 0; + do { + if (vbl != nv_rd32(pfb, 0x600808 + (i * 0x2000))) { + nv_wr08(pfb, 0x0c03c4 + (i * 0x2000), 0x01); + sr1[i] = nv_rd08(pfb, 0x0c03c5 + (i * 0x2000)); + if (!(sr1[i] & 0x20)) + crtc_mask |= (1 << i); + break; + } + udelay(1); + } while (cnt++ < 32); + } + + /* wait for vblank start on active crtcs, disable memory access */ + for (i = 0; i < 2; i++) { + if (!(crtc_mask & (1 << i))) + continue; + nv_wait(pfb, 0x600808 + (i * 0x2000), 0x00010000, 0x00000000); + nv_wait(pfb, 0x600808 + (i * 0x2000), 0x00010000, 0x00010000); + nv_wr08(pfb, 0x0c03c4 + (i * 0x2000), 0x01); + nv_wr08(pfb, 0x0c03c5 + (i * 0x2000), sr1[i] | 0x20); + } + + /* prepare ram for reclocking */ + nv_wr32(pfb, 0x1002d4, 0x00000001); /* precharge */ + nv_wr32(pfb, 0x1002d0, 0x00000001); /* refresh */ + nv_wr32(pfb, 0x1002d0, 0x00000001); /* refresh */ + nv_mask(pfb, 0x100210, 0x80000000, 0x00000000); /* no auto refresh */ + nv_wr32(pfb, 0x1002dc, 0x00000001); /* enable self-refresh */ + + /* change the PLL of each memory partition */ + nv_mask(pfb, 0x00c040, 0x0000c000, 0x00000000); + switch (nv_device(pfb)->chipset) { + case 0x40: + case 0x45: + case 0x41: + case 0x42: + case 0x47: + nv_mask(pfb, 0x004044, 0xc0771100, ram->ctrl); + nv_mask(pfb, 0x00402c, 0xc0771100, ram->ctrl); + nv_wr32(pfb, 0x004048, ram->coef); + nv_wr32(pfb, 0x004030, ram->coef); + case 0x43: + case 0x49: + case 0x4b: + nv_mask(pfb, 0x004038, 0xc0771100, ram->ctrl); + nv_wr32(pfb, 0x00403c, ram->coef); + default: + nv_mask(pfb, 0x004020, 0xc0771100, ram->ctrl); + nv_wr32(pfb, 0x004024, ram->coef); + break; + } + udelay(100); + nv_mask(pfb, 0x00c040, 0x0000c000, 0x0000c000); + + /* re-enable normal operation of memory controller */ + nv_wr32(pfb, 0x1002dc, 0x00000000); + nv_mask(pfb, 0x100210, 0x80000000, 0x80000000); + udelay(100); + + /* execute memory reset script from vbios */ + if (!bit_entry(bios, 'M', &M)) { + struct nvbios_init init = { + .subdev = nv_subdev(pfb), + .bios = bios, + .offset = nv_ro16(bios, M.offset + 0x00), + .execute = 1, + }; + + nvbios_exec(&init); + } + + /* make sure we're in vblank (hopefully the same one as before), and + * then re-enable crtc memory access + */ + for (i = 0; i < 2; i++) { + if (!(crtc_mask & (1 << i))) + continue; + nv_wait(pfb, 0x600808 + (i * 0x2000), 0x00010000, 0x00010000); + nv_wr08(pfb, 0x0c03c4 + (i * 0x2000), 0x01); + nv_wr08(pfb, 0x0c03c5 + (i * 0x2000), sr1[i]); + } + + return 0; +} + +void +nv40_ram_tidy(struct nvkm_fb *pfb) +{ +} + +static int +nv40_ram_create(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_fb *pfb = nvkm_fb(parent); + struct nv40_ram *ram; + u32 pbus1218 = nv_rd32(pfb, 0x001218); + int ret; + + ret = nvkm_ram_create(parent, engine, oclass, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + switch (pbus1218 & 0x00000300) { + case 0x00000000: ram->base.type = NV_MEM_TYPE_SDRAM; break; + case 0x00000100: ram->base.type = NV_MEM_TYPE_DDR1; break; + case 0x00000200: ram->base.type = NV_MEM_TYPE_GDDR3; break; + case 0x00000300: ram->base.type = NV_MEM_TYPE_DDR2; break; + } + + ram->base.size = nv_rd32(pfb, 0x10020c) & 0xff000000; + ram->base.parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1; + ram->base.tags = nv_rd32(pfb, 0x100320); + ram->base.calc = nv40_ram_calc; + ram->base.prog = nv40_ram_prog; + ram->base.tidy = nv40_ram_tidy; + return 0; +} + + +struct nvkm_oclass +nv40_ram_oclass = { + .handle = 0, + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv40_ram_create, + .dtor = _nvkm_ram_dtor, + .init = _nvkm_ram_init, + .fini = _nvkm_ram_fini, + } +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv41.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv41.c new file mode 100644 index 000000000..33c612b13 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv41.c @@ -0,0 +1,66 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv40.h" + +static int +nv41_ram_create(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_fb *pfb = nvkm_fb(parent); + struct nv40_ram *ram; + u32 pfb474 = nv_rd32(pfb, 0x100474); + int ret; + + ret = nvkm_ram_create(parent, engine, oclass, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + if (pfb474 & 0x00000004) + ram->base.type = NV_MEM_TYPE_GDDR3; + if (pfb474 & 0x00000002) + ram->base.type = NV_MEM_TYPE_DDR2; + if (pfb474 & 0x00000001) + ram->base.type = NV_MEM_TYPE_DDR1; + + ram->base.size = nv_rd32(pfb, 0x10020c) & 0xff000000; + ram->base.parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1; + ram->base.tags = nv_rd32(pfb, 0x100320); + ram->base.calc = nv40_ram_calc; + ram->base.prog = nv40_ram_prog; + ram->base.tidy = nv40_ram_tidy; + return 0; +} + +struct nvkm_oclass +nv41_ram_oclass = { + .handle = 0, + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv41_ram_create, + .dtor = _nvkm_ram_dtor, + .init = _nvkm_ram_init, + .fini = _nvkm_ram_fini, + } +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv44.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv44.c new file mode 100644 index 000000000..f575a7246 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv44.c @@ -0,0 +1,64 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv40.h" + +static int +nv44_ram_create(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_fb *pfb = nvkm_fb(parent); + struct nv40_ram *ram; + u32 pfb474 = nv_rd32(pfb, 0x100474); + int ret; + + ret = nvkm_ram_create(parent, engine, oclass, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + if (pfb474 & 0x00000004) + ram->base.type = NV_MEM_TYPE_GDDR3; + if (pfb474 & 0x00000002) + ram->base.type = NV_MEM_TYPE_DDR2; + if (pfb474 & 0x00000001) + ram->base.type = NV_MEM_TYPE_DDR1; + + ram->base.size = nv_rd32(pfb, 0x10020c) & 0xff000000; + ram->base.calc = nv40_ram_calc; + ram->base.prog = nv40_ram_prog; + ram->base.tidy = nv40_ram_tidy; + return 0; +} + +struct nvkm_oclass +nv44_ram_oclass = { + .handle = 0, + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv44_ram_create, + .dtor = _nvkm_ram_dtor, + .init = _nvkm_ram_init, + .fini = _nvkm_ram_fini, + } +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv49.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv49.c new file mode 100644 index 000000000..51b44cdb2 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv49.c @@ -0,0 +1,66 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv40.h" + +static int +nv49_ram_create(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_fb *pfb = nvkm_fb(parent); + struct nv40_ram *ram; + u32 pfb914 = nv_rd32(pfb, 0x100914); + int ret; + + ret = nvkm_ram_create(parent, engine, oclass, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + switch (pfb914 & 0x00000003) { + case 0x00000000: ram->base.type = NV_MEM_TYPE_DDR1; break; + case 0x00000001: ram->base.type = NV_MEM_TYPE_DDR2; break; + case 0x00000002: ram->base.type = NV_MEM_TYPE_GDDR3; break; + case 0x00000003: break; + } + + ram->base.size = nv_rd32(pfb, 0x10020c) & 0xff000000; + ram->base.parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1; + ram->base.tags = nv_rd32(pfb, 0x100320); + ram->base.calc = nv40_ram_calc; + ram->base.prog = nv40_ram_prog; + ram->base.tidy = nv40_ram_tidy; + return 0; +} + +struct nvkm_oclass +nv49_ram_oclass = { + .handle = 0, + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv49_ram_create, + .dtor = _nvkm_ram_dtor, + .init = _nvkm_ram_init, + .fini = _nvkm_ram_fini, + } +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv4e.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv4e.c new file mode 100644 index 000000000..f3ed1c60d --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv4e.c @@ -0,0 +1,54 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +static int +nv4e_ram_create(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_fb *pfb = nvkm_fb(parent); + struct nvkm_ram *ram; + int ret; + + ret = nvkm_ram_create(parent, engine, oclass, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + ram->size = nv_rd32(pfb, 0x10020c) & 0xff000000; + ram->type = NV_MEM_TYPE_STOLEN; + return 0; +} + +struct nvkm_oclass +nv4e_ram_oclass = { + .handle = 0, + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv4e_ram_create, + .dtor = _nvkm_ram_dtor, + .init = _nvkm_ram_init, + .fini = _nvkm_ram_fini, + } +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv50.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv50.c new file mode 100644 index 000000000..d2c81dd63 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv50.c @@ -0,0 +1,465 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv50.h" +#include "ramseq.h" + +#include +#include +#include +#include +#include +#include +#include + +struct nv50_ramseq { + struct hwsq base; + struct hwsq_reg r_0x002504; + struct hwsq_reg r_0x004008; + struct hwsq_reg r_0x00400c; + struct hwsq_reg r_0x00c040; + struct hwsq_reg r_0x100210; + struct hwsq_reg r_0x1002d0; + struct hwsq_reg r_0x1002d4; + struct hwsq_reg r_0x1002dc; + struct hwsq_reg r_0x100da0[8]; + struct hwsq_reg r_0x100e20; + struct hwsq_reg r_0x100e24; + struct hwsq_reg r_0x611200; + struct hwsq_reg r_timing[9]; + struct hwsq_reg r_mr[4]; +}; + +struct nv50_ram { + struct nvkm_ram base; + struct nv50_ramseq hwsq; +}; + +#define QFX5800NVA0 1 + +static int +nv50_ram_calc(struct nvkm_fb *pfb, u32 freq) +{ + struct nvkm_bios *bios = nvkm_bios(pfb); + struct nv50_ram *ram = (void *)pfb->ram; + struct nv50_ramseq *hwsq = &ram->hwsq; + struct nvbios_perfE perfE; + struct nvbios_pll mpll; + struct { + u32 data; + u8 size; + } ramcfg, timing; + u8 ver, hdr, cnt, len, strap; + int N1, M1, N2, M2, P; + int ret, i; + + /* lookup closest matching performance table entry for frequency */ + i = 0; + do { + ramcfg.data = nvbios_perfEp(bios, i++, &ver, &hdr, &cnt, + &ramcfg.size, &perfE); + if (!ramcfg.data || (ver < 0x25 || ver >= 0x40) || + (ramcfg.size < 2)) { + nv_error(pfb, "invalid/missing perftab entry\n"); + return -EINVAL; + } + } while (perfE.memory < freq); + + /* locate specific data set for the attached memory */ + strap = nvbios_ramcfg_index(nv_subdev(pfb)); + if (strap >= cnt) { + nv_error(pfb, "invalid ramcfg strap\n"); + return -EINVAL; + } + + ramcfg.data += hdr + (strap * ramcfg.size); + + /* lookup memory timings, if bios says they're present */ + strap = nv_ro08(bios, ramcfg.data + 0x01); + if (strap != 0xff) { + timing.data = nvbios_timingEe(bios, strap, &ver, &hdr, + &cnt, &len); + if (!timing.data || ver != 0x10 || hdr < 0x12) { + nv_error(pfb, "invalid/missing timing entry " + "%02x %04x %02x %02x\n", + strap, timing.data, ver, hdr); + return -EINVAL; + } + } else { + timing.data = 0; + } + + ret = ram_init(hwsq, nv_subdev(pfb)); + if (ret) + return ret; + + ram_wait(hwsq, 0x01, 0x00); /* wait for !vblank */ + ram_wait(hwsq, 0x01, 0x01); /* wait for vblank */ + ram_wr32(hwsq, 0x611200, 0x00003300); + ram_wr32(hwsq, 0x002504, 0x00000001); /* block fifo */ + ram_nsec(hwsq, 8000); + ram_setf(hwsq, 0x10, 0x00); /* disable fb */ + ram_wait(hwsq, 0x00, 0x01); /* wait for fb disabled */ + + ram_wr32(hwsq, 0x1002d4, 0x00000001); /* precharge */ + ram_wr32(hwsq, 0x1002d0, 0x00000001); /* refresh */ + ram_wr32(hwsq, 0x1002d0, 0x00000001); /* refresh */ + ram_wr32(hwsq, 0x100210, 0x00000000); /* disable auto-refresh */ + ram_wr32(hwsq, 0x1002dc, 0x00000001); /* enable self-refresh */ + + ret = nvbios_pll_parse(bios, 0x004008, &mpll); + mpll.vco2.max_freq = 0; + if (ret == 0) { + ret = nv04_pll_calc(nv_subdev(pfb), &mpll, freq, + &N1, &M1, &N2, &M2, &P); + if (ret == 0) + ret = -EINVAL; + } + + if (ret < 0) + return ret; + + ram_mask(hwsq, 0x00c040, 0xc000c000, 0x0000c000); + ram_mask(hwsq, 0x004008, 0x00000200, 0x00000200); + ram_mask(hwsq, 0x00400c, 0x0000ffff, (N1 << 8) | M1); + ram_mask(hwsq, 0x004008, 0x81ff0000, 0x80000000 | (mpll.bias_p << 19) | + (P << 22) | (P << 16)); +#if QFX5800NVA0 + for (i = 0; i < 8; i++) + ram_mask(hwsq, 0x100da0[i], 0x00000000, 0x00000000); /*XXX*/ +#endif + ram_nsec(hwsq, 96000); /*XXX*/ + ram_mask(hwsq, 0x004008, 0x00002200, 0x00002000); + + ram_wr32(hwsq, 0x1002dc, 0x00000000); /* disable self-refresh */ + ram_wr32(hwsq, 0x100210, 0x80000000); /* enable auto-refresh */ + + ram_nsec(hwsq, 12000); + + switch (ram->base.type) { + case NV_MEM_TYPE_DDR2: + ram_nuke(hwsq, mr[0]); /* force update */ + ram_mask(hwsq, mr[0], 0x000, 0x000); + break; + case NV_MEM_TYPE_GDDR3: + ram_mask(hwsq, mr[2], 0x000, 0x000); + ram_nuke(hwsq, mr[0]); /* force update */ + ram_mask(hwsq, mr[0], 0x000, 0x000); + break; + default: + break; + } + + ram_mask(hwsq, timing[3], 0x00000000, 0x00000000); /*XXX*/ + ram_mask(hwsq, timing[1], 0x00000000, 0x00000000); /*XXX*/ + ram_mask(hwsq, timing[6], 0x00000000, 0x00000000); /*XXX*/ + ram_mask(hwsq, timing[7], 0x00000000, 0x00000000); /*XXX*/ + ram_mask(hwsq, timing[8], 0x00000000, 0x00000000); /*XXX*/ + ram_mask(hwsq, timing[0], 0x00000000, 0x00000000); /*XXX*/ + ram_mask(hwsq, timing[2], 0x00000000, 0x00000000); /*XXX*/ + ram_mask(hwsq, timing[4], 0x00000000, 0x00000000); /*XXX*/ + ram_mask(hwsq, timing[5], 0x00000000, 0x00000000); /*XXX*/ + + ram_mask(hwsq, timing[0], 0x00000000, 0x00000000); /*XXX*/ + +#if QFX5800NVA0 + ram_nuke(hwsq, 0x100e24); + ram_mask(hwsq, 0x100e24, 0x00000000, 0x00000000); + ram_nuke(hwsq, 0x100e20); + ram_mask(hwsq, 0x100e20, 0x00000000, 0x00000000); +#endif + + ram_mask(hwsq, mr[0], 0x100, 0x100); + ram_mask(hwsq, mr[0], 0x100, 0x000); + + ram_setf(hwsq, 0x10, 0x01); /* enable fb */ + ram_wait(hwsq, 0x00, 0x00); /* wait for fb enabled */ + ram_wr32(hwsq, 0x611200, 0x00003330); + ram_wr32(hwsq, 0x002504, 0x00000000); /* un-block fifo */ + return 0; +} + +static int +nv50_ram_prog(struct nvkm_fb *pfb) +{ + struct nvkm_device *device = nv_device(pfb); + struct nv50_ram *ram = (void *)pfb->ram; + struct nv50_ramseq *hwsq = &ram->hwsq; + + ram_exec(hwsq, nvkm_boolopt(device->cfgopt, "NvMemExec", true)); + return 0; +} + +static void +nv50_ram_tidy(struct nvkm_fb *pfb) +{ + struct nv50_ram *ram = (void *)pfb->ram; + struct nv50_ramseq *hwsq = &ram->hwsq; + ram_exec(hwsq, false); +} + +void +__nv50_ram_put(struct nvkm_fb *pfb, struct nvkm_mem *mem) +{ + struct nvkm_mm_node *this; + + while (!list_empty(&mem->regions)) { + this = list_first_entry(&mem->regions, typeof(*this), rl_entry); + + list_del(&this->rl_entry); + nvkm_mm_free(&pfb->vram, &this); + } + + nvkm_mm_free(&pfb->tags, &mem->tag); +} + +void +nv50_ram_put(struct nvkm_fb *pfb, struct nvkm_mem **pmem) +{ + struct nvkm_mem *mem = *pmem; + + *pmem = NULL; + if (unlikely(mem == NULL)) + return; + + mutex_lock(&pfb->base.mutex); + __nv50_ram_put(pfb, mem); + mutex_unlock(&pfb->base.mutex); + + kfree(mem); +} + +int +nv50_ram_get(struct nvkm_fb *pfb, u64 size, u32 align, u32 ncmin, + u32 memtype, struct nvkm_mem **pmem) +{ + struct nvkm_mm *heap = &pfb->vram; + struct nvkm_mm *tags = &pfb->tags; + struct nvkm_mm_node *r; + struct nvkm_mem *mem; + int comp = (memtype & 0x300) >> 8; + int type = (memtype & 0x07f); + int back = (memtype & 0x800); + int min, max, ret; + + max = (size >> 12); + min = ncmin ? (ncmin >> 12) : max; + align >>= 12; + + mem = kzalloc(sizeof(*mem), GFP_KERNEL); + if (!mem) + return -ENOMEM; + + mutex_lock(&pfb->base.mutex); + if (comp) { + if (align == 16) { + int n = (max >> 4) * comp; + + ret = nvkm_mm_head(tags, 0, 1, n, n, 1, &mem->tag); + if (ret) + mem->tag = NULL; + } + + if (unlikely(!mem->tag)) + comp = 0; + } + + INIT_LIST_HEAD(&mem->regions); + mem->memtype = (comp << 7) | type; + mem->size = max; + + type = nv50_fb_memtype[type]; + do { + if (back) + ret = nvkm_mm_tail(heap, 0, type, max, min, align, &r); + else + ret = nvkm_mm_head(heap, 0, type, max, min, align, &r); + if (ret) { + mutex_unlock(&pfb->base.mutex); + pfb->ram->put(pfb, &mem); + return ret; + } + + list_add_tail(&r->rl_entry, &mem->regions); + max -= r->length; + } while (max); + mutex_unlock(&pfb->base.mutex); + + r = list_first_entry(&mem->regions, struct nvkm_mm_node, rl_entry); + mem->offset = (u64)r->offset << 12; + *pmem = mem; + return 0; +} + +static u32 +nv50_fb_vram_rblock(struct nvkm_fb *pfb, struct nvkm_ram *ram) +{ + int colbits, rowbitsa, rowbitsb, banks; + u64 rowsize, predicted; + u32 r0, r4, rt, rblock_size; + + r0 = nv_rd32(pfb, 0x100200); + r4 = nv_rd32(pfb, 0x100204); + rt = nv_rd32(pfb, 0x100250); + nv_debug(pfb, "memcfg 0x%08x 0x%08x 0x%08x 0x%08x\n", + r0, r4, rt, nv_rd32(pfb, 0x001540)); + + colbits = (r4 & 0x0000f000) >> 12; + rowbitsa = ((r4 & 0x000f0000) >> 16) + 8; + rowbitsb = ((r4 & 0x00f00000) >> 20) + 8; + banks = 1 << (((r4 & 0x03000000) >> 24) + 2); + + rowsize = ram->parts * banks * (1 << colbits) * 8; + predicted = rowsize << rowbitsa; + if (r0 & 0x00000004) + predicted += rowsize << rowbitsb; + + if (predicted != ram->size) { + nv_warn(pfb, "memory controller reports %d MiB VRAM\n", + (u32)(ram->size >> 20)); + } + + rblock_size = rowsize; + if (rt & 1) + rblock_size *= 3; + + nv_debug(pfb, "rblock %d bytes\n", rblock_size); + return rblock_size; +} + +int +nv50_ram_create_(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, int length, void **pobject) +{ + const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */ + const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */ + struct nvkm_bios *bios = nvkm_bios(parent); + struct nvkm_fb *pfb = nvkm_fb(parent); + struct nvkm_ram *ram; + int ret; + + ret = nvkm_ram_create_(parent, engine, oclass, length, pobject); + ram = *pobject; + if (ret) + return ret; + + ram->size = nv_rd32(pfb, 0x10020c); + ram->size = (ram->size & 0xffffff00) | ((ram->size & 0x000000ff) << 32); + + ram->part_mask = (nv_rd32(pfb, 0x001540) & 0x00ff0000) >> 16; + ram->parts = hweight8(ram->part_mask); + + switch (nv_rd32(pfb, 0x100714) & 0x00000007) { + case 0: ram->type = NV_MEM_TYPE_DDR1; break; + case 1: + if (nvkm_fb_bios_memtype(bios) == NV_MEM_TYPE_DDR3) + ram->type = NV_MEM_TYPE_DDR3; + else + ram->type = NV_MEM_TYPE_DDR2; + break; + case 2: ram->type = NV_MEM_TYPE_GDDR3; break; + case 3: ram->type = NV_MEM_TYPE_GDDR4; break; + case 4: ram->type = NV_MEM_TYPE_GDDR5; break; + default: + break; + } + + ret = nvkm_mm_init(&pfb->vram, rsvd_head, (ram->size >> 12) - + (rsvd_head + rsvd_tail), + nv50_fb_vram_rblock(pfb, ram) >> 12); + if (ret) + return ret; + + ram->ranks = (nv_rd32(pfb, 0x100200) & 0x4) ? 2 : 1; + ram->tags = nv_rd32(pfb, 0x100320); + ram->get = nv50_ram_get; + ram->put = nv50_ram_put; + return 0; +} + +static int +nv50_ram_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 datasize, + struct nvkm_object **pobject) +{ + struct nv50_ram *ram; + int ret, i; + + ret = nv50_ram_create(parent, engine, oclass, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + switch (ram->base.type) { + case NV_MEM_TYPE_DDR2: + case NV_MEM_TYPE_GDDR3: + ram->base.calc = nv50_ram_calc; + ram->base.prog = nv50_ram_prog; + ram->base.tidy = nv50_ram_tidy; + break; + default: + nv_warn(ram, "reclocking of this ram type unsupported\n"); + return 0; + } + + ram->hwsq.r_0x002504 = hwsq_reg(0x002504); + ram->hwsq.r_0x00c040 = hwsq_reg(0x00c040); + ram->hwsq.r_0x004008 = hwsq_reg(0x004008); + ram->hwsq.r_0x00400c = hwsq_reg(0x00400c); + ram->hwsq.r_0x100210 = hwsq_reg(0x100210); + ram->hwsq.r_0x1002d0 = hwsq_reg(0x1002d0); + ram->hwsq.r_0x1002d4 = hwsq_reg(0x1002d4); + ram->hwsq.r_0x1002dc = hwsq_reg(0x1002dc); + for (i = 0; i < 8; i++) + ram->hwsq.r_0x100da0[i] = hwsq_reg(0x100da0 + (i * 0x04)); + ram->hwsq.r_0x100e20 = hwsq_reg(0x100e20); + ram->hwsq.r_0x100e24 = hwsq_reg(0x100e24); + ram->hwsq.r_0x611200 = hwsq_reg(0x611200); + + for (i = 0; i < 9; i++) + ram->hwsq.r_timing[i] = hwsq_reg(0x100220 + (i * 0x04)); + + if (ram->base.ranks > 1) { + ram->hwsq.r_mr[0] = hwsq_reg2(0x1002c0, 0x1002c8); + ram->hwsq.r_mr[1] = hwsq_reg2(0x1002c4, 0x1002cc); + ram->hwsq.r_mr[2] = hwsq_reg2(0x1002e0, 0x1002e8); + ram->hwsq.r_mr[3] = hwsq_reg2(0x1002e4, 0x1002ec); + } else { + ram->hwsq.r_mr[0] = hwsq_reg(0x1002c0); + ram->hwsq.r_mr[1] = hwsq_reg(0x1002c4); + ram->hwsq.r_mr[2] = hwsq_reg(0x1002e0); + ram->hwsq.r_mr[3] = hwsq_reg(0x1002e4); + } + + return 0; +} + +struct nvkm_oclass +nv50_ram_oclass = { + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_ram_ctor, + .dtor = _nvkm_ram_dtor, + .init = _nvkm_ram_init, + .fini = _nvkm_ram_fini, + } +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramseq.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramseq.h new file mode 100644 index 000000000..0f1f97ccd --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramseq.h @@ -0,0 +1,15 @@ +#ifndef __NVKM_FBRAM_SEQ_H__ +#define __NVKM_FBRAM_SEQ_H__ +#include + +#define ram_init(s,p) hwsq_init(&(s)->base, (p)) +#define ram_exec(s,e) hwsq_exec(&(s)->base, (e)) +#define ram_have(s,r) ((s)->r_##r.addr != 0x000000) +#define ram_rd32(s,r) hwsq_rd32(&(s)->base, &(s)->r_##r) +#define ram_wr32(s,r,d) hwsq_wr32(&(s)->base, &(s)->r_##r, (d)) +#define ram_nuke(s,r) hwsq_nuke(&(s)->base, &(s)->r_##r) +#define ram_mask(s,r,m,d) hwsq_mask(&(s)->base, &(s)->r_##r, (m), (d)) +#define ram_setf(s,f,d) hwsq_setf(&(s)->base, (f), (d)) +#define ram_wait(s,f,d) hwsq_wait(&(s)->base, (f), (d)) +#define ram_nsec(s,n) hwsq_nsec(&(s)->base, (n)) +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/regsnv04.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/regsnv04.h new file mode 100644 index 000000000..1f865f615 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/regsnv04.h @@ -0,0 +1,22 @@ +#ifndef __NVKM_FB_REGS_04_H__ +#define __NVKM_FB_REGS_04_H__ + +#define NV04_PFB_BOOT_0 0x00100000 +# define NV04_PFB_BOOT_0_RAM_AMOUNT 0x00000003 +# define NV04_PFB_BOOT_0_RAM_AMOUNT_32MB 0x00000000 +# define NV04_PFB_BOOT_0_RAM_AMOUNT_4MB 0x00000001 +# define NV04_PFB_BOOT_0_RAM_AMOUNT_8MB 0x00000002 +# define NV04_PFB_BOOT_0_RAM_AMOUNT_16MB 0x00000003 +# define NV04_PFB_BOOT_0_RAM_WIDTH_128 0x00000004 +# define NV04_PFB_BOOT_0_RAM_TYPE 0x00000028 +# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_8MBIT 0x00000000 +# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT 0x00000008 +# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT_4BANK 0x00000010 +# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_16MBIT 0x00000018 +# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBIT 0x00000020 +# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBITX16 0x00000028 +# define NV04_PFB_BOOT_0_UMA_ENABLE 0x00000100 +# define NV04_PFB_BOOT_0_UMA_SIZE 0x0000f000 +#define NV04_PFB_CFG0 0x00100200 + +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/sddr2.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/sddr2.c new file mode 100644 index 000000000..afab42df2 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/sddr2.c @@ -0,0 +1,93 @@ +/* + * Copyright 2014 Roy Spliet + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Roy Spliet + * Ben Skeggs + */ +#include "priv.h" + +struct ramxlat { + int id; + u8 enc; +}; + +static inline int +ramxlat(const struct ramxlat *xlat, int id) +{ + while (xlat->id >= 0) { + if (xlat->id == id) + return xlat->enc; + xlat++; + } + return -EINVAL; +} + +static const struct ramxlat +ramddr2_cl[] = { + { 2, 2 }, { 3, 3 }, { 4, 4 }, { 5, 5 }, { 6, 6 }, + /* The following are available in some, but not all DDR2 docs */ + { 7, 7 }, + { -1 } +}; + +static const struct ramxlat +ramddr2_wr[] = { + { 2, 1 }, { 3, 2 }, { 4, 3 }, { 5, 4 }, { 6, 5 }, + /* The following are available in some, but not all DDR2 docs */ + { 7, 6 }, + { -1 } +}; + +int +nvkm_sddr2_calc(struct nvkm_ram *ram) +{ + int CL, WR, DLL = 0, ODT = 0; + + switch (ram->next->bios.timing_ver) { + case 0x10: + CL = ram->next->bios.timing_10_CL; + WR = ram->next->bios.timing_10_WR; + DLL = !ram->next->bios.ramcfg_10_DLLoff; + ODT = ram->next->bios.timing_10_ODT & 3; + break; + case 0x20: + CL = (ram->next->bios.timing[1] & 0x0000001f); + WR = (ram->next->bios.timing[2] & 0x007f0000) >> 16; + break; + default: + return -ENOSYS; + } + + CL = ramxlat(ramddr2_cl, CL); + WR = ramxlat(ramddr2_wr, WR); + if (CL < 0 || WR < 0) + return -EINVAL; + + ram->mr[0] &= ~0xf70; + ram->mr[0] |= (WR & 0x07) << 9; + ram->mr[0] |= (CL & 0x07) << 4; + + ram->mr[1] &= ~0x045; + ram->mr[1] |= (ODT & 0x1) << 2; + ram->mr[1] |= (ODT & 0x2) << 5; + ram->mr[1] |= !DLL; + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/sddr3.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/sddr3.c new file mode 100644 index 000000000..10844355c --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/sddr3.c @@ -0,0 +1,119 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + * Roy Spliet + */ +#include "priv.h" + +struct ramxlat { + int id; + u8 enc; +}; + +static inline int +ramxlat(const struct ramxlat *xlat, int id) +{ + while (xlat->id >= 0) { + if (xlat->id == id) + return xlat->enc; + xlat++; + } + return -EINVAL; +} + +static const struct ramxlat +ramddr3_cl[] = { + { 5, 2 }, { 6, 4 }, { 7, 6 }, { 8, 8 }, { 9, 10 }, { 10, 12 }, + { 11, 14 }, + /* the below are mentioned in some, but not all, ddr3 docs */ + { 12, 1 }, { 13, 3 }, { 14, 5 }, + { -1 } +}; + +static const struct ramxlat +ramddr3_wr[] = { + { 5, 1 }, { 6, 2 }, { 7, 3 }, { 8, 4 }, { 10, 5 }, { 12, 6 }, + /* the below are mentioned in some, but not all, ddr3 docs */ + { 14, 7 }, { 16, 0 }, + { -1 } +}; + +static const struct ramxlat +ramddr3_cwl[] = { + { 5, 0 }, { 6, 1 }, { 7, 2 }, { 8, 3 }, + /* the below are mentioned in some, but not all, ddr3 docs */ + { 9, 4 }, + { -1 } +}; + +int +nvkm_sddr3_calc(struct nvkm_ram *ram) +{ + int CWL, CL, WR, DLL = 0, ODT = 0; + + switch (ram->next->bios.timing_ver) { + case 0x10: + if (ram->next->bios.timing_hdr < 0x17) { + /* XXX: NV50: Get CWL from the timing register */ + return -ENOSYS; + } + CWL = ram->next->bios.timing_10_CWL; + CL = ram->next->bios.timing_10_CL; + WR = ram->next->bios.timing_10_WR; + DLL = !ram->next->bios.ramcfg_10_DLLoff; + ODT = ram->next->bios.timing_10_ODT; + break; + case 0x20: + CWL = (ram->next->bios.timing[1] & 0x00000f80) >> 7; + CL = (ram->next->bios.timing[1] & 0x0000001f) >> 0; + WR = (ram->next->bios.timing[2] & 0x007f0000) >> 16; + /* XXX: Get these values from the VBIOS instead */ + DLL = !(ram->mr[1] & 0x1); + ODT = (ram->mr[1] & 0x004) >> 2 | + (ram->mr[1] & 0x040) >> 5 | + (ram->mr[1] & 0x200) >> 7; + break; + default: + return -ENOSYS; + } + + CWL = ramxlat(ramddr3_cwl, CWL); + CL = ramxlat(ramddr3_cl, CL); + WR = ramxlat(ramddr3_wr, WR); + if (CL < 0 || CWL < 0 || WR < 0) + return -EINVAL; + + ram->mr[0] &= ~0xf74; + ram->mr[0] |= (WR & 0x07) << 9; + ram->mr[0] |= (CL & 0x0e) << 3; + ram->mr[0] |= (CL & 0x01) << 2; + + ram->mr[1] &= ~0x245; + ram->mr[1] |= (ODT & 0x1) << 2; + ram->mr[1] |= (ODT & 0x2) << 5; + ram->mr[1] |= (ODT & 0x4) << 7; + ram->mr[1] |= !DLL; + + ram->mr[2] &= ~0x038; + ram->mr[2] |= (CWL & 0x07) << 3; + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/Kbuild new file mode 100644 index 000000000..f3d4e6e13 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/Kbuild @@ -0,0 +1,4 @@ +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/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/base.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/base.c new file mode 100644 index 000000000..b7b7193bb --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/base.c @@ -0,0 +1,51 @@ +/* + * 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 + +int +_nvkm_fuse_init(struct nvkm_object *object) +{ + struct nvkm_fuse *fuse = (void *)object; + return nvkm_subdev_init(&fuse->base); +} + +void +_nvkm_fuse_dtor(struct nvkm_object *object) +{ + struct nvkm_fuse *fuse = (void *)object; + nvkm_subdev_destroy(&fuse->base); +} + +int +nvkm_fuse_create_(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, int length, void **pobject) +{ + struct nvkm_fuse *fuse; + int ret; + + ret = nvkm_subdev_create_(parent, engine, oclass, 0, "FUSE", + "fuse", length, pobject); + fuse = *pobject; + return ret; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/gf100.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/gf100.c new file mode 100644 index 000000000..393ef3a0f --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/gf100.c @@ -0,0 +1,78 @@ +/* + * 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" + +struct gf100_fuse_priv { + struct nvkm_fuse base; + + spinlock_t fuse_enable_lock; +}; + +static u32 +gf100_fuse_rd32(struct nvkm_object *object, u64 addr) +{ + struct gf100_fuse_priv *priv = (void *)object; + unsigned long flags; + u32 fuse_enable, unk, val; + + /* racy if another part of nvkm start writing to these regs */ + spin_lock_irqsave(&priv->fuse_enable_lock, flags); + fuse_enable = nv_mask(priv, 0x22400, 0x800, 0x800); + unk = nv_mask(priv, 0x21000, 0x1, 0x1); + val = nv_rd32(priv, 0x21100 + addr); + nv_wr32(priv, 0x21000, unk); + nv_wr32(priv, 0x22400, fuse_enable); + spin_unlock_irqrestore(&priv->fuse_enable_lock, flags); + return val; +} + + +static int +gf100_fuse_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gf100_fuse_priv *priv; + int ret; + + ret = nvkm_fuse_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + spin_lock_init(&priv->fuse_enable_lock); + return 0; +} + +struct nvkm_oclass +gf100_fuse_oclass = { + .handle = NV_SUBDEV(FUSE, 0xC0), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_fuse_ctor, + .dtor = _nvkm_fuse_dtor, + .init = _nvkm_fuse_init, + .fini = _nvkm_fuse_fini, + .rd32 = gf100_fuse_rd32, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/gm107.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/gm107.c new file mode 100644 index 000000000..0b256aa49 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/gm107.c @@ -0,0 +1,62 @@ +/* + * 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" + +struct gm107_fuse_priv { + struct nvkm_fuse base; +}; + +static u32 +gm107_fuse_rd32(struct nvkm_object *object, u64 addr) +{ + struct gf100_fuse_priv *priv = (void *)object; + return nv_rd32(priv, 0x21100 + addr); +} + + +static int +gm107_fuse_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gm107_fuse_priv *priv; + int ret; + + ret = nvkm_fuse_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + + return ret; +} + +struct nvkm_oclass +gm107_fuse_oclass = { + .handle = NV_SUBDEV(FUSE, 0x117), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gm107_fuse_ctor, + .dtor = _nvkm_fuse_dtor, + .init = _nvkm_fuse_init, + .fini = _nvkm_fuse_fini, + .rd32 = gm107_fuse_rd32, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/nv50.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/nv50.c new file mode 100644 index 000000000..0d2afc426 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/nv50.c @@ -0,0 +1,76 @@ +/* + * 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" + +struct nv50_fuse_priv { + struct nvkm_fuse base; + + spinlock_t fuse_enable_lock; +}; + +static u32 +nv50_fuse_rd32(struct nvkm_object *object, u64 addr) +{ + struct nv50_fuse_priv *priv = (void *)object; + unsigned long flags; + u32 fuse_enable, val; + + /* racy if another part of nvkm start writing to this reg */ + spin_lock_irqsave(&priv->fuse_enable_lock, flags); + fuse_enable = nv_mask(priv, 0x1084, 0x800, 0x800); + val = nv_rd32(priv, 0x21000 + addr); + nv_wr32(priv, 0x1084, fuse_enable); + spin_unlock_irqrestore(&priv->fuse_enable_lock, flags); + return val; +} + + +static int +nv50_fuse_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv50_fuse_priv *priv; + int ret; + + ret = nvkm_fuse_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + spin_lock_init(&priv->fuse_enable_lock); + return 0; +} + +struct nvkm_oclass +nv50_fuse_oclass = { + .handle = NV_SUBDEV(FUSE, 0x50), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_fuse_ctor, + .dtor = _nvkm_fuse_dtor, + .init = _nvkm_fuse_init, + .fini = _nvkm_fuse_fini, + .rd32 = nv50_fuse_rd32, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/priv.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/priv.h new file mode 100644 index 000000000..7e050f789 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/priv.h @@ -0,0 +1,7 @@ +#ifndef __NVKM_FUSE_PRIV_H__ +#define __NVKM_FUSE_PRIV_H__ +#include + +int _nvkm_fuse_init(struct nvkm_object *object); +void _nvkm_fuse_dtor(struct nvkm_object *object); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/Kbuild new file mode 100644 index 000000000..ea42a9ed1 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/Kbuild @@ -0,0 +1,6 @@ +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/gf110.o +nvkm-y += nvkm/subdev/gpio/gk104.o diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/base.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/base.c new file mode 100644 index 000000000..dea58161b --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/base.c @@ -0,0 +1,251 @@ +/* + * 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) +{ + const struct nvkm_gpio_impl *impl = (void *)nv_object(gpio)->oclass; + return impl->drive ? impl->drive(gpio, line, dir, out) : -ENODEV; +} + +static int +nvkm_gpio_sense(struct nvkm_gpio *gpio, int idx, int line) +{ + const struct nvkm_gpio_impl *impl = (void *)nv_object(gpio)->oclass; + return impl->sense ? impl->sense(gpio, line) : -ENODEV; +} + +static int +nvkm_gpio_find(struct nvkm_gpio *gpio, int idx, u8 tag, u8 line, + struct dcb_gpio_func *func) +{ + struct nvkm_bios *bios = nvkm_bios(gpio); + 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 (nv_device_match(nv_object(gpio), 0x0189, 0x10de, 0x0010)) { + if (tag == DCB_GPIO_TVDAC0) { + *func = (struct dcb_gpio_func) { + .func = DCB_GPIO_TVDAC0, + .line = 4, + .log[0] = 0, + .log[1] = 1, + }; + return 0; + } + } + + return -ENOENT; +} + +static 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; +} + +static 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); + const struct nvkm_gpio_impl *impl = (void *)nv_object(gpio)->oclass; + impl->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); + const struct nvkm_gpio_impl *impl = (void *)nv_object(gpio)->oclass; + impl->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 void +nvkm_gpio_intr(struct nvkm_subdev *subdev) +{ + struct nvkm_gpio *gpio = nvkm_gpio(subdev); + const struct nvkm_gpio_impl *impl = (void *)nv_object(gpio)->oclass; + u32 hi, lo, i; + + impl->intr_stat(gpio, &hi, &lo); + + for (i = 0; (hi | lo) && i < impl->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 const struct nvkm_event_func +nvkm_gpio_intr_func = { + .ctor = nvkm_gpio_intr_ctor, + .init = nvkm_gpio_intr_init, + .fini = nvkm_gpio_intr_fini, +}; + +int +_nvkm_gpio_fini(struct nvkm_object *object, bool suspend) +{ + const struct nvkm_gpio_impl *impl = (void *)object->oclass; + struct nvkm_gpio *gpio = nvkm_gpio(object); + u32 mask = (1 << impl->lines) - 1; + + impl->intr_mask(gpio, NVKM_GPIO_TOGGLED, mask, 0); + impl->intr_stat(gpio, &mask, &mask); + + return nvkm_subdev_fini(&gpio->base, suspend); +} + +static 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"), + } + }, + { } +}; + +int +_nvkm_gpio_init(struct nvkm_object *object) +{ + struct nvkm_gpio *gpio = nvkm_gpio(object); + int ret; + + ret = nvkm_subdev_init(&gpio->base); + if (ret) + return ret; + + if (gpio->reset && dmi_check_system(gpio_reset_ids)) + gpio->reset(gpio, DCB_GPIO_UNUSED); + + return ret; +} + +void +_nvkm_gpio_dtor(struct nvkm_object *object) +{ + struct nvkm_gpio *gpio = (void *)object; + nvkm_event_fini(&gpio->event); + nvkm_subdev_destroy(&gpio->base); +} + +int +nvkm_gpio_create_(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, int length, void **pobject) +{ + const struct nvkm_gpio_impl *impl = (void *)oclass; + struct nvkm_gpio *gpio; + int ret; + + ret = nvkm_subdev_create_(parent, engine, oclass, 0, "GPIO", + "gpio", length, pobject); + gpio = *pobject; + if (ret) + return ret; + + gpio->find = nvkm_gpio_find; + gpio->set = nvkm_gpio_set; + gpio->get = nvkm_gpio_get; + gpio->reset = impl->reset; + + ret = nvkm_event_init(&nvkm_gpio_intr_func, 2, impl->lines, + &gpio->event); + if (ret) + return ret; + + nv_subdev(gpio)->intr = nvkm_gpio_intr; + return 0; +} + +int +_nvkm_gpio_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_gpio *gpio; + int ret; + + ret = nvkm_gpio_create(parent, engine, oclass, &gpio); + *pobject = nv_object(gpio); + if (ret) + return ret; + + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/g94.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/g94.c new file mode 100644 index 000000000..12b3e01fc --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/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" + +void +g94_gpio_intr_stat(struct nvkm_gpio *gpio, u32 *hi, u32 *lo) +{ + u32 intr0 = nv_rd32(gpio, 0x00e054); + u32 intr1 = nv_rd32(gpio, 0x00e074); + u32 stat0 = nv_rd32(gpio, 0x00e050) & intr0; + u32 stat1 = nv_rd32(gpio, 0x00e070) & intr1; + *lo = (stat1 & 0xffff0000) | (stat0 >> 16); + *hi = (stat1 << 16) | (stat0 & 0x0000ffff); + nv_wr32(gpio, 0x00e054, intr0); + nv_wr32(gpio, 0x00e074, intr1); +} + +void +g94_gpio_intr_mask(struct nvkm_gpio *gpio, u32 type, u32 mask, u32 data) +{ + u32 inte0 = nv_rd32(gpio, 0x00e050); + u32 inte1 = nv_rd32(gpio, 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; + nv_wr32(gpio, 0x00e050, inte0); + nv_wr32(gpio, 0x00e070, inte1); +} + +struct nvkm_oclass * +g94_gpio_oclass = &(struct nvkm_gpio_impl) { + .base.handle = NV_SUBDEV(GPIO, 0x94), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_gpio_ctor, + .dtor = _nvkm_gpio_dtor, + .init = _nvkm_gpio_init, + .fini = _nvkm_gpio_fini, + }, + .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, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/gf110.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/gf110.c new file mode 100644 index 000000000..2c3bb255d --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/gf110.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" + +void +gf110_gpio_reset(struct nvkm_gpio *gpio, u8 match) +{ + struct nvkm_bios *bios = nvkm_bios(gpio); + u8 ver, len; + u16 entry; + int ent = -1; + + while ((entry = dcb_gpio_entry(bios, 0, ++ent, &ver, &len))) { + u32 data = nv_ro32(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; + + gpio->set(gpio, 0, func, line, defs); + + nv_mask(gpio, 0x00d610 + (line * 4), 0xff, unk0); + if (unk1--) + nv_mask(gpio, 0x00d740 + (unk1 * 4), 0xff, line); + } +} + +int +gf110_gpio_drive(struct nvkm_gpio *gpio, int line, int dir, int out) +{ + u32 data = ((dir ^ 1) << 13) | (out << 12); + nv_mask(gpio, 0x00d610 + (line * 4), 0x00003000, data); + nv_mask(gpio, 0x00d604, 0x00000001, 0x00000001); /* update? */ + return 0; +} + +int +gf110_gpio_sense(struct nvkm_gpio *gpio, int line) +{ + return !!(nv_rd32(gpio, 0x00d610 + (line * 4)) & 0x00004000); +} + +struct nvkm_oclass * +gf110_gpio_oclass = &(struct nvkm_gpio_impl) { + .base.handle = NV_SUBDEV(GPIO, 0xd0), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_gpio_ctor, + .dtor = _nvkm_gpio_dtor, + .init = _nvkm_gpio_init, + .fini = _nvkm_gpio_fini, + }, + .lines = 32, + .intr_stat = g94_gpio_intr_stat, + .intr_mask = g94_gpio_intr_mask, + .drive = gf110_gpio_drive, + .sense = gf110_gpio_sense, + .reset = gf110_gpio_reset, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/gk104.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/gk104.c new file mode 100644 index 000000000..42fd2faaa --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/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" + +static void +gk104_gpio_intr_stat(struct nvkm_gpio *gpio, u32 *hi, u32 *lo) +{ + u32 intr0 = nv_rd32(gpio, 0x00dc00); + u32 intr1 = nv_rd32(gpio, 0x00dc80); + u32 stat0 = nv_rd32(gpio, 0x00dc08) & intr0; + u32 stat1 = nv_rd32(gpio, 0x00dc88) & intr1; + *lo = (stat1 & 0xffff0000) | (stat0 >> 16); + *hi = (stat1 << 16) | (stat0 & 0x0000ffff); + nv_wr32(gpio, 0x00dc00, intr0); + nv_wr32(gpio, 0x00dc80, intr1); +} + +void +gk104_gpio_intr_mask(struct nvkm_gpio *gpio, u32 type, u32 mask, u32 data) +{ + u32 inte0 = nv_rd32(gpio, 0x00dc08); + u32 inte1 = nv_rd32(gpio, 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; + nv_wr32(gpio, 0x00dc08, inte0); + nv_wr32(gpio, 0x00dc88, inte1); +} + +struct nvkm_oclass * +gk104_gpio_oclass = &(struct nvkm_gpio_impl) { + .base.handle = NV_SUBDEV(GPIO, 0xe0), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_gpio_ctor, + .dtor = _nvkm_gpio_dtor, + .init = _nvkm_gpio_init, + .fini = _nvkm_gpio_fini, + }, + .lines = 32, + .intr_stat = gk104_gpio_intr_stat, + .intr_mask = gk104_gpio_intr_mask, + .drive = gf110_gpio_drive, + .sense = gf110_gpio_sense, + .reset = gf110_gpio_reset, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/nv10.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/nv10.c new file mode 100644 index 000000000..2b2951542 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/nv10.c @@ -0,0 +1,115 @@ +/* + * 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) +{ + if (line < 2) { + line = line * 16; + line = nv_rd32(gpio, 0x600818) >> line; + return !!(line & 0x0100); + } else + if (line < 10) { + line = (line - 2) * 4; + line = nv_rd32(gpio, 0x60081c) >> line; + return !!(line & 0x04); + } else + if (line < 14) { + line = (line - 10) * 4; + line = nv_rd32(gpio, 0x600850) >> line; + return !!(line & 0x04); + } + + return -EINVAL; +} + +static int +nv10_gpio_drive(struct nvkm_gpio *gpio, int line, int dir, int out) +{ + 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; + } + + nv_mask(gpio, reg, mask << line, data << line); + return 0; +} + +static void +nv10_gpio_intr_stat(struct nvkm_gpio *gpio, u32 *hi, u32 *lo) +{ + u32 intr = nv_rd32(gpio, 0x001104); + u32 stat = nv_rd32(gpio, 0x001144) & intr; + *lo = (stat & 0xffff0000) >> 16; + *hi = (stat & 0x0000ffff); + nv_wr32(gpio, 0x001104, intr); +} + +static void +nv10_gpio_intr_mask(struct nvkm_gpio *gpio, u32 type, u32 mask, u32 data) +{ + u32 inte = nv_rd32(gpio, 0x001144); + if (type & NVKM_GPIO_LO) + inte = (inte & ~(mask << 16)) | (data << 16); + if (type & NVKM_GPIO_HI) + inte = (inte & ~mask) | data; + nv_wr32(gpio, 0x001144, inte); +} + +struct nvkm_oclass * +nv10_gpio_oclass = &(struct nvkm_gpio_impl) { + .base.handle = NV_SUBDEV(GPIO, 0x10), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_gpio_ctor, + .dtor = _nvkm_gpio_dtor, + .init = _nvkm_gpio_init, + .fini = _nvkm_gpio_fini, + }, + .lines = 16, + .intr_stat = nv10_gpio_intr_stat, + .intr_mask = nv10_gpio_intr_mask, + .drive = nv10_gpio_drive, + .sense = nv10_gpio_sense, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/nv50.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/nv50.c new file mode 100644 index 000000000..6a031035b --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/nv50.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 + */ +#include "priv.h" + +void +nv50_gpio_reset(struct nvkm_gpio *gpio, u8 match) +{ + struct nvkm_bios *bios = nvkm_bios(gpio); + 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 = nv_ro32(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; + + gpio->set(gpio, 0, func, line, defs); + + nv_mask(gpio, reg, 0x00010001 << lsh, val << lsh); + } +} + +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) +{ + u32 reg, shift; + + if (nv50_gpio_location(line, ®, &shift)) + return -EINVAL; + + nv_mask(gpio, reg, 3 << shift, (((dir ^ 1) << 1) | out) << shift); + return 0; +} + +int +nv50_gpio_sense(struct nvkm_gpio *gpio, int line) +{ + u32 reg, shift; + + if (nv50_gpio_location(line, ®, &shift)) + return -EINVAL; + + return !!(nv_rd32(gpio, reg) & (4 << shift)); +} + +static void +nv50_gpio_intr_stat(struct nvkm_gpio *gpio, u32 *hi, u32 *lo) +{ + u32 intr = nv_rd32(gpio, 0x00e054); + u32 stat = nv_rd32(gpio, 0x00e050) & intr; + *lo = (stat & 0xffff0000) >> 16; + *hi = (stat & 0x0000ffff); + nv_wr32(gpio, 0x00e054, intr); +} + +static void +nv50_gpio_intr_mask(struct nvkm_gpio *gpio, u32 type, u32 mask, u32 data) +{ + u32 inte = nv_rd32(gpio, 0x00e050); + if (type & NVKM_GPIO_LO) + inte = (inte & ~(mask << 16)) | (data << 16); + if (type & NVKM_GPIO_HI) + inte = (inte & ~mask) | data; + nv_wr32(gpio, 0x00e050, inte); +} + +struct nvkm_oclass * +nv50_gpio_oclass = &(struct nvkm_gpio_impl) { + .base.handle = NV_SUBDEV(GPIO, 0x50), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_gpio_ctor, + .dtor = _nvkm_gpio_dtor, + .init = _nvkm_gpio_init, + .fini = _nvkm_gpio_fini, + }, + .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, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/priv.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/priv.h new file mode 100644 index 000000000..382f8d44e --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/priv.h @@ -0,0 +1,64 @@ +#ifndef __NVKM_GPIO_PRIV_H__ +#define __NVKM_GPIO_PRIV_H__ +#include + +#define nvkm_gpio_create(p,e,o,d) \ + nvkm_gpio_create_((p), (e), (o), sizeof(**d), (void **)d) +#define nvkm_gpio_destroy(p) ({ \ + struct nvkm_gpio *gpio = (p); \ + _nvkm_gpio_dtor(nv_object(gpio)); \ +}) +#define nvkm_gpio_init(p) ({ \ + struct nvkm_gpio *gpio = (p); \ + _nvkm_gpio_init(nv_object(gpio)); \ +}) +#define nvkm_gpio_fini(p,s) ({ \ + struct nvkm_gpio *gpio = (p); \ + _nvkm_gpio_fini(nv_object(gpio), (s)); \ +}) + +int nvkm_gpio_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, int, void **); +int _nvkm_gpio_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); +void _nvkm_gpio_dtor(struct nvkm_object *); +int _nvkm_gpio_init(struct nvkm_object *); +int _nvkm_gpio_fini(struct nvkm_object *, bool); + +struct nvkm_gpio_impl { + struct nvkm_oclass base; + 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); +}; + +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 gf110_gpio_reset(struct nvkm_gpio *, u8); +int gf110_gpio_drive(struct nvkm_gpio *, int, int, int); +int gf110_gpio_sense(struct nvkm_gpio *, int); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/Kbuild new file mode 100644 index 000000000..d68307409 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/Kbuild @@ -0,0 +1,16 @@ +nvkm-y += nvkm/subdev/i2c/base.o +nvkm-y += nvkm/subdev/i2c/anx9805.o +nvkm-y += nvkm/subdev/i2c/aux.o +nvkm-y += nvkm/subdev/i2c/bit.o +nvkm-y += nvkm/subdev/i2c/pad.o +nvkm-y += nvkm/subdev/i2c/padnv04.o +nvkm-y += nvkm/subdev/i2c/padg94.o +nvkm-y += nvkm/subdev/i2c/padgm204.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/gf110.o +nvkm-y += nvkm/subdev/i2c/gf117.o +nvkm-y += nvkm/subdev/i2c/gk104.o +nvkm-y += nvkm/subdev/i2c/gm204.o diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/anx9805.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/anx9805.c new file mode 100644 index 000000000..d17dd1cf3 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/anx9805.c @@ -0,0 +1,292 @@ +/* + * 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 "port.h" + +struct anx9805_i2c_port { + struct nvkm_i2c_port base; + u32 addr; + u32 ctrl; +}; + +static int +anx9805_train(struct nvkm_i2c_port *port, int link_nr, int link_bw, bool enh) +{ + struct anx9805_i2c_port *chan = (void *)port; + struct nvkm_i2c_port *mast = (void *)nv_object(chan)->parent; + u8 tmp, i; + + DBG("ANX9805 train %d 0x%02x %d\n", link_nr, link_bw, enh); + + nv_wri2cr(mast, chan->addr, 0xa0, link_bw); + nv_wri2cr(mast, chan->addr, 0xa1, link_nr | (enh ? 0x80 : 0x00)); + nv_wri2cr(mast, chan->addr, 0xa2, 0x01); + nv_wri2cr(mast, chan->addr, 0xa8, 0x01); + + i = 0; + while ((tmp = nv_rdi2cr(mast, chan->addr, 0xa8)) & 0x01) { + mdelay(5); + if (i++ == 100) { + nv_error(port, "link training timed out\n"); + return -ETIMEDOUT; + } + } + + if (tmp & 0x70) { + nv_error(port, "link training failed: 0x%02x\n", tmp); + return -EIO; + } + + return 1; +} + +static int +anx9805_aux(struct nvkm_i2c_port *port, bool retry, + u8 type, u32 addr, u8 *data, u8 size) +{ + struct anx9805_i2c_port *chan = (void *)port; + struct nvkm_i2c_port *mast = (void *)nv_object(chan)->parent; + int i, ret = -ETIMEDOUT; + u8 buf[16] = {}; + u8 tmp; + + DBG("%02x %05x %d\n", type, addr, size); + + tmp = nv_rdi2cr(mast, chan->ctrl, 0x07) & ~0x04; + nv_wri2cr(mast, chan->ctrl, 0x07, tmp | 0x04); + nv_wri2cr(mast, chan->ctrl, 0x07, tmp); + nv_wri2cr(mast, chan->ctrl, 0xf7, 0x01); + + nv_wri2cr(mast, chan->addr, 0xe4, 0x80); + if (!(type & 1)) { + memcpy(buf, data, size); + DBG("%16ph", buf); + for (i = 0; i < size; i++) + nv_wri2cr(mast, chan->addr, 0xf0 + i, buf[i]); + } + nv_wri2cr(mast, chan->addr, 0xe5, ((size - 1) << 4) | type); + nv_wri2cr(mast, chan->addr, 0xe6, (addr & 0x000ff) >> 0); + nv_wri2cr(mast, chan->addr, 0xe7, (addr & 0x0ff00) >> 8); + nv_wri2cr(mast, chan->addr, 0xe8, (addr & 0xf0000) >> 16); + nv_wri2cr(mast, chan->addr, 0xe9, 0x01); + + i = 0; + while ((tmp = nv_rdi2cr(mast, chan->addr, 0xe9)) & 0x01) { + mdelay(5); + if (i++ == 32) + goto done; + } + + if ((tmp = nv_rdi2cr(mast, chan->ctrl, 0xf7)) & 0x01) { + ret = -EIO; + goto done; + } + + if (type & 1) { + for (i = 0; i < size; i++) + buf[i] = nv_rdi2cr(mast, chan->addr, 0xf0 + i); + DBG("%16ph", buf); + memcpy(data, buf, size); + } + + ret = 0; +done: + nv_wri2cr(mast, chan->ctrl, 0xf7, 0x01); + return ret; +} + +static const struct nvkm_i2c_func +anx9805_aux_func = { + .aux = anx9805_aux, + .lnk_ctl = anx9805_train, +}; + +static int +anx9805_aux_chan_ctor(struct nvkm_object *parent, + struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 index, + struct nvkm_object **pobject) +{ + struct nvkm_i2c_port *mast = (void *)parent; + struct anx9805_i2c_port *chan; + int ret; + + ret = nvkm_i2c_port_create(parent, engine, oclass, index, + &nvkm_i2c_aux_algo, &anx9805_aux_func, + &chan); + *pobject = nv_object(chan); + if (ret) + return ret; + + switch ((oclass->handle & 0xff00) >> 8) { + case 0x0d: + chan->addr = 0x38; + chan->ctrl = 0x39; + break; + case 0x0e: + chan->addr = 0x3c; + chan->ctrl = 0x3b; + break; + default: + BUG_ON(1); + } + + if (mast->adapter.algo == &i2c_bit_algo) { + struct i2c_algo_bit_data *algo = mast->adapter.algo_data; + algo->udelay = max(algo->udelay, 40); + } + + return 0; +} + +static struct nvkm_ofuncs +anx9805_aux_ofuncs = { + .ctor = anx9805_aux_chan_ctor, + .dtor = _nvkm_i2c_port_dtor, + .init = _nvkm_i2c_port_init, + .fini = _nvkm_i2c_port_fini, +}; + +static int +anx9805_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct anx9805_i2c_port *port = adap->algo_data; + struct nvkm_i2c_port *mast = (void *)nv_object(port)->parent; + struct i2c_msg *msg = msgs; + int ret = -ETIMEDOUT; + int i, j, cnt = num; + u8 seg = 0x00, off = 0x00, tmp; + + tmp = nv_rdi2cr(mast, port->ctrl, 0x07) & ~0x10; + nv_wri2cr(mast, port->ctrl, 0x07, tmp | 0x10); + nv_wri2cr(mast, port->ctrl, 0x07, tmp); + nv_wri2cr(mast, port->addr, 0x43, 0x05); + mdelay(5); + + while (cnt--) { + if ( (msg->flags & I2C_M_RD) && msg->addr == 0x50) { + nv_wri2cr(mast, port->addr, 0x40, msg->addr << 1); + nv_wri2cr(mast, port->addr, 0x41, seg); + nv_wri2cr(mast, port->addr, 0x42, off); + nv_wri2cr(mast, port->addr, 0x44, msg->len); + nv_wri2cr(mast, port->addr, 0x45, 0x00); + nv_wri2cr(mast, port->addr, 0x43, 0x01); + for (i = 0; i < msg->len; i++) { + j = 0; + while (nv_rdi2cr(mast, port->addr, 0x46) & 0x10) { + mdelay(5); + if (j++ == 32) + goto done; + } + msg->buf[i] = nv_rdi2cr(mast, port->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: + nv_wri2cr(mast, port->addr, 0x43, 0x00); + return ret; +} + +static u32 +anx9805_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm +anx9805_i2c_algo = { + .master_xfer = anx9805_xfer, + .functionality = anx9805_func +}; + +static const struct nvkm_i2c_func +anx9805_i2c_func = { +}; + +static int +anx9805_ddc_port_ctor(struct nvkm_object *parent, + struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 index, + struct nvkm_object **pobject) +{ + struct nvkm_i2c_port *mast = (void *)parent; + struct anx9805_i2c_port *port; + int ret; + + ret = nvkm_i2c_port_create(parent, engine, oclass, index, + &anx9805_i2c_algo, &anx9805_i2c_func, &port); + *pobject = nv_object(port); + if (ret) + return ret; + + switch ((oclass->handle & 0xff00) >> 8) { + case 0x0d: + port->addr = 0x3d; + port->ctrl = 0x39; + break; + case 0x0e: + port->addr = 0x3f; + port->ctrl = 0x3b; + break; + default: + BUG_ON(1); + } + + if (mast->adapter.algo == &i2c_bit_algo) { + struct i2c_algo_bit_data *algo = mast->adapter.algo_data; + algo->udelay = max(algo->udelay, 40); + } + + return 0; +} + +static struct nvkm_ofuncs +anx9805_ddc_ofuncs = { + .ctor = anx9805_ddc_port_ctor, + .dtor = _nvkm_i2c_port_dtor, + .init = _nvkm_i2c_port_init, + .fini = _nvkm_i2c_port_fini, +}; + +struct nvkm_oclass +nvkm_anx9805_sclass[] = { + { .handle = NV_I2C_TYPE_EXTDDC(0x0d), .ofuncs = &anx9805_ddc_ofuncs }, + { .handle = NV_I2C_TYPE_EXTAUX(0x0d), .ofuncs = &anx9805_aux_ofuncs }, + { .handle = NV_I2C_TYPE_EXTDDC(0x0e), .ofuncs = &anx9805_ddc_ofuncs }, + { .handle = NV_I2C_TYPE_EXTAUX(0x0e), .ofuncs = &anx9805_aux_ofuncs }, + {} +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c new file mode 100644 index 000000000..1c18860f8 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c @@ -0,0 +1,113 @@ +/* + * 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 "priv.h" + +int +nv_rdaux(struct nvkm_i2c_port *port, u32 addr, u8 *data, u8 size) +{ + struct nvkm_i2c *i2c = nvkm_i2c(port); + if (port->func->aux) { + int ret = i2c->acquire(port, 0); + if (ret == 0) { + ret = port->func->aux(port, true, 9, addr, data, size); + i2c->release(port); + } + return ret; + } + return -ENODEV; +} + +int +nv_wraux(struct nvkm_i2c_port *port, u32 addr, u8 *data, u8 size) +{ + struct nvkm_i2c *i2c = nvkm_i2c(port); + if (port->func->aux) { + int ret = i2c->acquire(port, 0); + if (ret == 0) { + ret = port->func->aux(port, true, 8, addr, data, size); + i2c->release(port); + } + return ret; + } + return -ENODEV; +} + +static int +aux_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct nvkm_i2c_port *port = adap->algo_data; + struct nvkm_i2c *i2c = nvkm_i2c(port); + struct i2c_msg *msg = msgs; + int ret, mcnt = num; + + if (!port->func->aux) + return -ENODEV; + + ret = i2c->acquire(port, 0); + if (ret) + return ret; + + while (mcnt--) { + u8 remaining = msg->len; + u8 *ptr = msg->buf; + + while (remaining) { + u8 cnt = (remaining > 16) ? 16 : remaining; + u8 cmd; + + if (msg->flags & I2C_M_RD) + cmd = 1; + else + cmd = 0; + + if (mcnt || remaining > 16) + cmd |= 4; /* MOT */ + + ret = port->func->aux(port, true, cmd, msg->addr, ptr, cnt); + if (ret < 0) { + i2c->release(port); + return ret; + } + + ptr += cnt; + remaining -= cnt; + } + + msg++; + } + + i2c->release(port); + return num; +} + +static u32 +aux_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +const struct i2c_algorithm nvkm_i2c_aux_algo = { + .master_xfer = aux_xfer, + .functionality = aux_func +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/base.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/base.c new file mode 100644 index 000000000..9200f122c --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/base.c @@ -0,0 +1,622 @@ +/* + * 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 "pad.h" + +#include +#include +#include +#include +#include + +/****************************************************************************** + * interface to linux i2c bit-banging algorithm + *****************************************************************************/ + +#ifdef CONFIG_NOUVEAU_I2C_INTERNAL_DEFAULT +#define CSTMSEL true +#else +#define CSTMSEL false +#endif + +static int +nvkm_i2c_pre_xfer(struct i2c_adapter *adap) +{ + struct i2c_algo_bit_data *bit = adap->algo_data; + struct nvkm_i2c_port *port = bit->data; + return nvkm_i2c(port)->acquire(port, bit->timeout); +} + +static void +nvkm_i2c_post_xfer(struct i2c_adapter *adap) +{ + struct i2c_algo_bit_data *bit = adap->algo_data; + struct nvkm_i2c_port *port = bit->data; + return nvkm_i2c(port)->release(port); +} + +static void +nvkm_i2c_setscl(void *data, int state) +{ + struct nvkm_i2c_port *port = data; + port->func->drive_scl(port, state); +} + +static void +nvkm_i2c_setsda(void *data, int state) +{ + struct nvkm_i2c_port *port = data; + port->func->drive_sda(port, state); +} + +static int +nvkm_i2c_getscl(void *data) +{ + struct nvkm_i2c_port *port = data; + return port->func->sense_scl(port); +} + +static int +nvkm_i2c_getsda(void *data) +{ + struct nvkm_i2c_port *port = data; + return port->func->sense_sda(port); +} + +/****************************************************************************** + * base i2c "port" class implementation + *****************************************************************************/ + +int +_nvkm_i2c_port_fini(struct nvkm_object *object, bool suspend) +{ + struct nvkm_i2c_port *port = (void *)object; + struct nvkm_i2c_pad *pad = nvkm_i2c_pad(port); + nv_ofuncs(pad)->fini(nv_object(pad), suspend); + return nvkm_object_fini(&port->base, suspend); +} + +void +_nvkm_i2c_port_dtor(struct nvkm_object *object) +{ + struct nvkm_i2c_port *port = (void *)object; + i2c_del_adapter(&port->adapter); + nvkm_object_destroy(&port->base); +} + +int +nvkm_i2c_port_create_(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, u8 index, + const struct i2c_algorithm *algo, + const struct nvkm_i2c_func *func, + int size, void **pobject) +{ + struct nvkm_device *device = nv_device(parent); + struct nvkm_i2c *i2c = nvkm_i2c(parent); + struct nvkm_i2c_port *port; + int ret; + + ret = nvkm_object_create_(parent, engine, oclass, 0, size, pobject); + port = *pobject; + if (ret) + return ret; + + snprintf(port->adapter.name, sizeof(port->adapter.name), + "nvkm-%s-%d", device->name, index); + port->adapter.owner = THIS_MODULE; + port->adapter.dev.parent = nv_device_base(device); + port->index = index; + port->aux = -1; + port->func = func; + mutex_init(&port->mutex); + + if ( algo == &nvkm_i2c_bit_algo && + !nvkm_boolopt(device->cfgopt, "NvI2C", CSTMSEL)) { + struct i2c_algo_bit_data *bit; + + bit = kzalloc(sizeof(*bit), GFP_KERNEL); + if (!bit) + return -ENOMEM; + + bit->udelay = 10; + bit->timeout = usecs_to_jiffies(2200); + bit->data = port; + bit->pre_xfer = nvkm_i2c_pre_xfer; + bit->post_xfer = nvkm_i2c_post_xfer; + bit->setsda = nvkm_i2c_setsda; + bit->setscl = nvkm_i2c_setscl; + bit->getsda = nvkm_i2c_getsda; + bit->getscl = nvkm_i2c_getscl; + + port->adapter.algo_data = bit; + ret = i2c_bit_add_bus(&port->adapter); + } else { + port->adapter.algo_data = port; + port->adapter.algo = algo; + ret = i2c_add_adapter(&port->adapter); + } + + if (ret == 0) + list_add_tail(&port->head, &i2c->ports); + return ret; +} + +/****************************************************************************** + * base i2c subdev class implementation + *****************************************************************************/ + +static struct nvkm_i2c_port * +nvkm_i2c_find(struct nvkm_i2c *i2c, u8 index) +{ + struct nvkm_bios *bios = nvkm_bios(i2c); + struct nvkm_i2c_port *port; + + if (index == NV_I2C_DEFAULT(0) || + index == NV_I2C_DEFAULT(1)) { + u8 ver, hdr, cnt, len; + u16 i2c = dcb_i2c_table(bios, &ver, &hdr, &cnt, &len); + if (i2c && ver >= 0x30) { + u8 auxidx = nv_ro08(bios, i2c + 4); + if (index == NV_I2C_DEFAULT(0)) + index = (auxidx & 0x0f) >> 0; + else + index = (auxidx & 0xf0) >> 4; + } else { + index = 2; + } + } + + list_for_each_entry(port, &i2c->ports, head) { + if (port->index == index) + return port; + } + + return NULL; +} + +static struct nvkm_i2c_port * +nvkm_i2c_find_type(struct nvkm_i2c *i2c, u16 type) +{ + struct nvkm_i2c_port *port; + + list_for_each_entry(port, &i2c->ports, head) { + if (nv_hclass(port) == type) + return port; + } + + return NULL; +} + +static void +nvkm_i2c_release_pad(struct nvkm_i2c_port *port) +{ + struct nvkm_i2c_pad *pad = nvkm_i2c_pad(port); + struct nvkm_i2c *i2c = nvkm_i2c(port); + + if (atomic_dec_and_test(&nv_object(pad)->usecount)) { + nv_ofuncs(pad)->fini(nv_object(pad), false); + wake_up_all(&i2c->wait); + } +} + +static int +nvkm_i2c_try_acquire_pad(struct nvkm_i2c_port *port) +{ + struct nvkm_i2c_pad *pad = nvkm_i2c_pad(port); + + if (atomic_add_return(1, &nv_object(pad)->usecount) != 1) { + struct nvkm_object *owner = (void *)pad->port; + do { + if (owner == (void *)port) + return 0; + owner = owner->parent; + } while(owner); + nvkm_i2c_release_pad(port); + return -EBUSY; + } + + pad->next = port; + nv_ofuncs(pad)->init(nv_object(pad)); + return 0; +} + +static int +nvkm_i2c_acquire_pad(struct nvkm_i2c_port *port, unsigned long timeout) +{ + struct nvkm_i2c *i2c = nvkm_i2c(port); + + if (timeout) { + if (wait_event_timeout(i2c->wait, + nvkm_i2c_try_acquire_pad(port) == 0, + timeout) == 0) + return -EBUSY; + } else { + wait_event(i2c->wait, nvkm_i2c_try_acquire_pad(port) == 0); + } + + return 0; +} + +static void +nvkm_i2c_release(struct nvkm_i2c_port *port) +__releases(pad->mutex) +{ + nvkm_i2c(port)->release_pad(port); + mutex_unlock(&port->mutex); +} + +static int +nvkm_i2c_acquire(struct nvkm_i2c_port *port, unsigned long timeout) +__acquires(pad->mutex) +{ + int ret; + mutex_lock(&port->mutex); + if ((ret = nvkm_i2c(port)->acquire_pad(port, timeout))) + mutex_unlock(&port->mutex); + return ret; +} + +static int +nvkm_i2c_identify(struct nvkm_i2c *i2c, int index, const char *what, + struct nvkm_i2c_board_info *info, + bool (*match)(struct nvkm_i2c_port *, + struct i2c_board_info *, void *), void *data) +{ + struct nvkm_i2c_port *port = nvkm_i2c_find(i2c, index); + int i; + + if (!port) { + nv_debug(i2c, "no bus when probing %s on %d\n", what, index); + return -ENODEV; + } + + nv_debug(i2c, "probing %ss on bus: %d\n", what, port->index); + for (i = 0; info[i].dev.addr; i++) { + u8 orig_udelay = 0; + + if ((port->adapter.algo == &i2c_bit_algo) && + (info[i].udelay != 0)) { + struct i2c_algo_bit_data *algo = port->adapter.algo_data; + nv_debug(i2c, "using custom udelay %d instead of %d\n", + info[i].udelay, algo->udelay); + orig_udelay = algo->udelay; + algo->udelay = info[i].udelay; + } + + if (nv_probe_i2c(port, info[i].dev.addr) && + (!match || match(port, &info[i].dev, data))) { + nv_info(i2c, "detected %s: %s\n", what, + info[i].dev.type); + return i; + } + + if (orig_udelay) { + struct i2c_algo_bit_data *algo = port->adapter.algo_data; + algo->udelay = orig_udelay; + } + } + + nv_debug(i2c, "no devices found.\n"); + return -ENODEV; +} + +static void +nvkm_i2c_intr_fini(struct nvkm_event *event, int type, int index) +{ + struct nvkm_i2c *i2c = container_of(event, typeof(*i2c), event); + struct nvkm_i2c_port *port = i2c->find(i2c, index); + const struct nvkm_i2c_impl *impl = (void *)nv_object(i2c)->oclass; + if (port && port->aux >= 0) + impl->aux_mask(i2c, type, 1 << port->aux, 0); +} + +static void +nvkm_i2c_intr_init(struct nvkm_event *event, int type, int index) +{ + struct nvkm_i2c *i2c = container_of(event, typeof(*i2c), event); + struct nvkm_i2c_port *port = i2c->find(i2c, index); + const struct nvkm_i2c_impl *impl = (void *)nv_object(i2c)->oclass; + if (port && port->aux >= 0) + impl->aux_mask(i2c, type, 1 << port->aux, 1 << port->aux); +} + +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 void +nvkm_i2c_intr(struct nvkm_subdev *subdev) +{ + struct nvkm_i2c_impl *impl = (void *)nv_oclass(subdev); + struct nvkm_i2c *i2c = nvkm_i2c(subdev); + struct nvkm_i2c_port *port; + u32 hi, lo, rq, tx, e; + + if (impl->aux_stat) { + impl->aux_stat(i2c, &hi, &lo, &rq, &tx); + if (hi || lo || rq || tx) { + list_for_each_entry(port, &i2c->ports, head) { + if (e = 0, port->aux < 0) + continue; + + if (hi & (1 << port->aux)) e |= NVKM_I2C_PLUG; + if (lo & (1 << port->aux)) e |= NVKM_I2C_UNPLUG; + if (rq & (1 << port->aux)) e |= NVKM_I2C_IRQ; + if (tx & (1 << port->aux)) e |= NVKM_I2C_DONE; + if (e) { + struct nvkm_i2c_ntfy_rep rep = { + .mask = e, + }; + nvkm_event_send(&i2c->event, rep.mask, + port->index, &rep, + sizeof(rep)); + } + } + } + } +} + +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, +}; + +int +_nvkm_i2c_fini(struct nvkm_object *object, bool suspend) +{ + struct nvkm_i2c_impl *impl = (void *)nv_oclass(object); + struct nvkm_i2c *i2c = (void *)object; + struct nvkm_i2c_port *port; + u32 mask; + int ret; + + list_for_each_entry(port, &i2c->ports, head) { + ret = nv_ofuncs(port)->fini(nv_object(port), suspend); + if (ret && suspend) + goto fail; + } + + if ((mask = (1 << impl->aux) - 1), impl->aux_stat) { + impl->aux_mask(i2c, NVKM_I2C_ANY, mask, 0); + impl->aux_stat(i2c, &mask, &mask, &mask, &mask); + } + + return nvkm_subdev_fini(&i2c->base, suspend); +fail: + list_for_each_entry_continue_reverse(port, &i2c->ports, head) { + nv_ofuncs(port)->init(nv_object(port)); + } + + return ret; +} + +int +_nvkm_i2c_init(struct nvkm_object *object) +{ + struct nvkm_i2c *i2c = (void *)object; + struct nvkm_i2c_port *port; + int ret; + + ret = nvkm_subdev_init(&i2c->base); + if (ret == 0) { + list_for_each_entry(port, &i2c->ports, head) { + ret = nv_ofuncs(port)->init(nv_object(port)); + if (ret) + goto fail; + } + } + + return ret; +fail: + list_for_each_entry_continue_reverse(port, &i2c->ports, head) { + nv_ofuncs(port)->fini(nv_object(port), false); + } + + return ret; +} + +void +_nvkm_i2c_dtor(struct nvkm_object *object) +{ + struct nvkm_i2c *i2c = (void *)object; + struct nvkm_i2c_port *port, *temp; + + nvkm_event_fini(&i2c->event); + + list_for_each_entry_safe(port, temp, &i2c->ports, head) { + nvkm_object_ref(NULL, (struct nvkm_object **)&port); + } + + nvkm_subdev_destroy(&i2c->base); +} + +static struct nvkm_oclass * +nvkm_i2c_extdev_sclass[] = { + nvkm_anx9805_sclass, +}; + +static void +nvkm_i2c_create_port(struct nvkm_i2c *i2c, int index, u8 type, + struct dcb_i2c_entry *info) +{ + const struct nvkm_i2c_impl *impl = (void *)nv_oclass(i2c); + struct nvkm_oclass *oclass; + struct nvkm_object *parent; + struct nvkm_object *object; + int ret, pad; + + if (info->share != DCB_I2C_UNUSED) { + pad = info->share; + oclass = impl->pad_s; + } else { + if (type != DCB_I2C_NVIO_AUX) + pad = 0x100 + info->drive; + else + pad = 0x100 + info->auxch; + oclass = impl->pad_x; + } + + ret = nvkm_object_ctor(nv_object(i2c), NULL, oclass, + NULL, pad, &parent); + if (ret < 0) + return; + + oclass = impl->sclass; + do { + ret = -EINVAL; + if (oclass->handle == type) { + ret = nvkm_object_ctor(parent, NULL, oclass, + info, index, &object); + } + } while (ret && (++oclass)->handle); + + nvkm_object_ref(NULL, &parent); +} + +int +nvkm_i2c_create_(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, int length, void **pobject) +{ + struct nvkm_bios *bios = nvkm_bios(parent); + struct nvkm_i2c *i2c; + struct nvkm_object *object; + struct dcb_i2c_entry info; + int ret, i, j, index = -1; + struct dcb_output outp; + u8 ver, hdr; + u32 data; + + ret = nvkm_subdev_create(parent, engine, oclass, 0, "I2C", "i2c", &i2c); + *pobject = nv_object(i2c); + if (ret) + return ret; + + nv_subdev(i2c)->intr = nvkm_i2c_intr; + i2c->find = nvkm_i2c_find; + i2c->find_type = nvkm_i2c_find_type; + i2c->acquire_pad = nvkm_i2c_acquire_pad; + i2c->release_pad = nvkm_i2c_release_pad; + i2c->acquire = nvkm_i2c_acquire; + i2c->release = nvkm_i2c_release; + i2c->identify = nvkm_i2c_identify; + init_waitqueue_head(&i2c->wait); + INIT_LIST_HEAD(&i2c->ports); + + while (!dcb_i2c_parse(bios, ++index, &info)) { + switch (info.type) { + case DCB_I2C_NV04_BIT: + case DCB_I2C_NV4E_BIT: + case DCB_I2C_NVIO_BIT: + nvkm_i2c_create_port(i2c, NV_I2C_PORT(index), + info.type, &info); + break; + case DCB_I2C_NVIO_AUX: + nvkm_i2c_create_port(i2c, NV_I2C_AUX(index), + info.type, &info); + break; + case DCB_I2C_PMGR: + if (info.drive != DCB_I2C_UNUSED) { + nvkm_i2c_create_port(i2c, NV_I2C_PORT(index), + DCB_I2C_NVIO_BIT, &info); + } + if (info.auxch != DCB_I2C_UNUSED) { + nvkm_i2c_create_port(i2c, NV_I2C_AUX(index), + DCB_I2C_NVIO_AUX, &info); + } + break; + case DCB_I2C_UNUSED: + default: + continue; + } + } + + /* in addition to the busses specified in the i2c table, there + * may be ddc/aux channels hiding behind external tmds/dp/etc + * transmitters. + */ + index = NV_I2C_EXT(0); + i = -1; + while ((data = dcb_outp_parse(bios, ++i, &ver, &hdr, &outp))) { + if (!outp.location || !outp.extdev) + continue; + + switch (outp.type) { + case DCB_OUTPUT_TMDS: + info.type = NV_I2C_TYPE_EXTDDC(outp.extdev); + break; + case DCB_OUTPUT_DP: + info.type = NV_I2C_TYPE_EXTAUX(outp.extdev); + break; + default: + continue; + } + + ret = -ENODEV; + j = -1; + while (ret && ++j < ARRAY_SIZE(nvkm_i2c_extdev_sclass)) { + parent = nv_object(i2c->find(i2c, outp.i2c_index)); + oclass = nvkm_i2c_extdev_sclass[j]; + do { + if (oclass->handle != info.type) + continue; + ret = nvkm_object_ctor(parent, NULL, oclass, + NULL, index++, &object); + } while (ret && (++oclass)->handle); + } + } + + ret = nvkm_event_init(&nvkm_i2c_intr_func, 4, index, &i2c->event); + if (ret) + return ret; + + return 0; +} + +int +_nvkm_i2c_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_i2c *i2c; + int ret; + + ret = nvkm_i2c_create(parent, engine, oclass, &i2c); + *pobject = nv_object(i2c); + if (ret) + return ret; + + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bit.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bit.c new file mode 100644 index 000000000..861a453d2 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bit.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 + */ +#include "priv.h" + +#ifdef CONFIG_NOUVEAU_I2C_INTERNAL +#define T_TIMEOUT 2200000 +#define T_RISEFALL 1000 +#define T_HOLD 5000 + +static inline void +i2c_drive_scl(struct nvkm_i2c_port *port, int state) +{ + port->func->drive_scl(port, state); +} + +static inline void +i2c_drive_sda(struct nvkm_i2c_port *port, int state) +{ + port->func->drive_sda(port, state); +} + +static inline int +i2c_sense_scl(struct nvkm_i2c_port *port) +{ + return port->func->sense_scl(port); +} + +static inline int +i2c_sense_sda(struct nvkm_i2c_port *port) +{ + return port->func->sense_sda(port); +} + +static void +i2c_delay(struct nvkm_i2c_port *port, u32 nsec) +{ + udelay((nsec + 500) / 1000); +} + +static bool +i2c_raise_scl(struct nvkm_i2c_port *port) +{ + u32 timeout = T_TIMEOUT / T_RISEFALL; + + i2c_drive_scl(port, 1); + do { + i2c_delay(port, T_RISEFALL); + } while (!i2c_sense_scl(port) && --timeout); + + return timeout != 0; +} + +static int +i2c_start(struct nvkm_i2c_port *port) +{ + int ret = 0; + + if (!i2c_sense_scl(port) || + !i2c_sense_sda(port)) { + i2c_drive_scl(port, 0); + i2c_drive_sda(port, 1); + if (!i2c_raise_scl(port)) + ret = -EBUSY; + } + + i2c_drive_sda(port, 0); + i2c_delay(port, T_HOLD); + i2c_drive_scl(port, 0); + i2c_delay(port, T_HOLD); + return ret; +} + +static void +i2c_stop(struct nvkm_i2c_port *port) +{ + i2c_drive_scl(port, 0); + i2c_drive_sda(port, 0); + i2c_delay(port, T_RISEFALL); + + i2c_drive_scl(port, 1); + i2c_delay(port, T_HOLD); + i2c_drive_sda(port, 1); + i2c_delay(port, T_HOLD); +} + +static int +i2c_bitw(struct nvkm_i2c_port *port, int sda) +{ + i2c_drive_sda(port, sda); + i2c_delay(port, T_RISEFALL); + + if (!i2c_raise_scl(port)) + return -ETIMEDOUT; + i2c_delay(port, T_HOLD); + + i2c_drive_scl(port, 0); + i2c_delay(port, T_HOLD); + return 0; +} + +static int +i2c_bitr(struct nvkm_i2c_port *port) +{ + int sda; + + i2c_drive_sda(port, 1); + i2c_delay(port, T_RISEFALL); + + if (!i2c_raise_scl(port)) + return -ETIMEDOUT; + i2c_delay(port, T_HOLD); + + sda = i2c_sense_sda(port); + + i2c_drive_scl(port, 0); + i2c_delay(port, T_HOLD); + return sda; +} + +static int +i2c_get_byte(struct nvkm_i2c_port *port, u8 *byte, bool last) +{ + int i, bit; + + *byte = 0; + for (i = 7; i >= 0; i--) { + bit = i2c_bitr(port); + if (bit < 0) + return bit; + *byte |= bit << i; + } + + return i2c_bitw(port, last ? 1 : 0); +} + +static int +i2c_put_byte(struct nvkm_i2c_port *port, u8 byte) +{ + int i, ret; + for (i = 7; i >= 0; i--) { + ret = i2c_bitw(port, !!(byte & (1 << i))); + if (ret < 0) + return ret; + } + + ret = i2c_bitr(port); + if (ret == 1) /* nack */ + ret = -EIO; + return ret; +} + +static int +i2c_addr(struct nvkm_i2c_port *port, struct i2c_msg *msg) +{ + u32 addr = msg->addr << 1; + if (msg->flags & I2C_M_RD) + addr |= 1; + return i2c_put_byte(port, addr); +} + +static int +i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct nvkm_i2c_port *port = adap->algo_data; + struct i2c_msg *msg = msgs; + int ret = 0, mcnt = num; + + ret = nvkm_i2c(port)->acquire(port, nsecs_to_jiffies(T_TIMEOUT)); + if (ret) + return ret; + + while (!ret && mcnt--) { + u8 remaining = msg->len; + u8 *ptr = msg->buf; + + ret = i2c_start(port); + if (ret == 0) + ret = i2c_addr(port, msg); + + if (msg->flags & I2C_M_RD) { + while (!ret && remaining--) + ret = i2c_get_byte(port, ptr++, !remaining); + } else { + while (!ret && remaining--) + ret = i2c_put_byte(port, *ptr++); + } + + msg++; + } + + i2c_stop(port); + nvkm_i2c(port)->release(port); + return (ret < 0) ? ret : num; +} +#else +static int +i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + return -ENODEV; +} +#endif + +static u32 +i2c_bit_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +const struct i2c_algorithm nvkm_i2c_bit_algo = { + .master_xfer = i2c_bit_xfer, + .functionality = i2c_bit_func +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/g94.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/g94.c new file mode 100644 index 000000000..2a2dd47b9 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/g94.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 + */ +#include "nv50.h" + +void +g94_aux_stat(struct nvkm_i2c *i2c, u32 *hi, u32 *lo, u32 *rq, u32 *tx) +{ + u32 intr = nv_rd32(i2c, 0x00e06c); + u32 stat = nv_rd32(i2c, 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; + } + nv_wr32(i2c, 0x00e06c, intr); +} + +void +g94_aux_mask(struct nvkm_i2c *i2c, u32 type, u32 mask, u32 data) +{ + u32 temp = nv_rd32(i2c, 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); + } + } + nv_wr32(i2c, 0x00e068, temp); +} + +#define AUX_DBG(fmt, args...) nv_debug(aux, "AUXCH(%d): " fmt, ch, ##args) +#define AUX_ERR(fmt, args...) nv_error(aux, "AUXCH(%d): " fmt, ch, ##args) + +static void +auxch_fini(struct nvkm_i2c *aux, int ch) +{ + nv_mask(aux, 0x00e4e4 + (ch * 0x50), 0x00310000, 0x00000000); +} + +static int +auxch_init(struct nvkm_i2c *aux, int ch) +{ + 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 = nv_rd32(aux, 0x00e4e4 + (ch * 0x50)); + udelay(1); + if (!timeout--) { + AUX_ERR("begin idle timeout 0x%08x\n", ctrl); + return -EBUSY; + } + } while (ctrl & 0x03010000); + + /* set some magic, and wait up to 1ms for it to appear */ + nv_mask(aux, 0x00e4e4 + (ch * 0x50), 0x00300000, ureq); + timeout = 1000; + do { + ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50)); + udelay(1); + if (!timeout--) { + AUX_ERR("magic wait 0x%08x\n", ctrl); + auxch_fini(aux, ch); + return -EBUSY; + } + } while ((ctrl & 0x03000000) != urep); + + return 0; +} + +int +g94_aux(struct nvkm_i2c_port *base, bool retry, + u8 type, u32 addr, u8 *data, u8 size) +{ + struct nvkm_i2c *aux = nvkm_i2c(base); + struct nv50_i2c_port *port = (void *)base; + u32 ctrl, stat, timeout, retries; + u32 xbuf[4] = {}; + int ch = port->addr; + int ret, i; + + AUX_DBG("%d: 0x%08x %d\n", type, addr, size); + + ret = auxch_init(aux, ch); + if (ret) + goto out; + + stat = nv_rd32(aux, 0x00e4e8 + (ch * 0x50)); + if (!(stat & 0x10000000)) { + AUX_DBG("sink not detected\n"); + ret = -ENXIO; + goto out; + } + + if (!(type & 1)) { + memcpy(xbuf, data, size); + for (i = 0; i < 16; i += 4) { + AUX_DBG("wr 0x%08x\n", xbuf[i / 4]); + nv_wr32(aux, 0x00e4c0 + (ch * 0x50) + i, xbuf[i / 4]); + } + } + + ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50)); + ctrl &= ~0x0001f0ff; + ctrl |= type << 12; + ctrl |= size - 1; + nv_wr32(aux, 0x00e4e0 + (ch * 0x50), addr); + + /* (maybe) retry transaction a number of times on failure... */ + for (retries = 0; !ret && retries < 32; retries++) { + /* reset, and delay a while if this is a retry */ + nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x80000000 | ctrl); + nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x00000000 | ctrl); + if (retries) + udelay(400); + + /* transaction request, wait up to 1ms for it to complete */ + nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x00010000 | ctrl); + + timeout = 1000; + do { + ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50)); + udelay(1); + if (!timeout--) { + AUX_ERR("tx req timeout 0x%08x\n", ctrl); + ret = -EIO; + goto out; + } + } while (ctrl & 0x00010000); + ret = 1; + + /* read status, and check if transaction completed ok */ + stat = nv_mask(aux, 0x00e4e8 + (ch * 0x50), 0, 0); + if ((stat & 0x000f0000) == 0x00080000 || + (stat & 0x000f0000) == 0x00020000) + ret = retry ? 0 : 1; + if ((stat & 0x00000100)) + ret = -ETIMEDOUT; + if ((stat & 0x00000e00)) + ret = -EIO; + + AUX_DBG("%02d 0x%08x 0x%08x\n", retries, ctrl, stat); + } + + if (type & 1) { + for (i = 0; i < 16; i += 4) { + xbuf[i / 4] = nv_rd32(aux, 0x00e4d0 + (ch * 0x50) + i); + AUX_DBG("rd 0x%08x\n", xbuf[i / 4]); + } + memcpy(data, xbuf, size); + } + +out: + auxch_fini(aux, ch); + return ret < 0 ? ret : (stat & 0x000f0000) >> 16; +} + +static const struct nvkm_i2c_func +g94_i2c_func = { + .drive_scl = nv50_i2c_drive_scl, + .drive_sda = nv50_i2c_drive_sda, + .sense_scl = nv50_i2c_sense_scl, + .sense_sda = nv50_i2c_sense_sda, +}; + +static int +g94_i2c_port_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 index, + struct nvkm_object **pobject) +{ + struct dcb_i2c_entry *info = data; + struct nv50_i2c_port *port; + int ret; + + ret = nvkm_i2c_port_create(parent, engine, oclass, index, + &nvkm_i2c_bit_algo, &g94_i2c_func, &port); + *pobject = nv_object(port); + if (ret) + return ret; + + if (info->drive >= nv50_i2c_addr_nr) + return -EINVAL; + + port->state = 7; + port->addr = nv50_i2c_addr[info->drive]; + return 0; +} + +static const struct nvkm_i2c_func +g94_aux_func = { + .aux = g94_aux, +}; + +int +g94_aux_port_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 index, + struct nvkm_object **pobject) +{ + struct dcb_i2c_entry *info = data; + struct nv50_i2c_port *port; + int ret; + + ret = nvkm_i2c_port_create(parent, engine, oclass, index, + &nvkm_i2c_aux_algo, &g94_aux_func, &port); + *pobject = nv_object(port); + if (ret) + return ret; + + port->base.aux = info->auxch; + port->addr = info->auxch; + return 0; +} + +static struct nvkm_oclass +g94_i2c_sclass[] = { + { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_BIT), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = g94_i2c_port_ctor, + .dtor = _nvkm_i2c_port_dtor, + .init = nv50_i2c_port_init, + .fini = _nvkm_i2c_port_fini, + }, + }, + { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_AUX), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = g94_aux_port_ctor, + .dtor = _nvkm_i2c_port_dtor, + .init = _nvkm_i2c_port_init, + .fini = _nvkm_i2c_port_fini, + }, + }, + {} +}; + +struct nvkm_oclass * +g94_i2c_oclass = &(struct nvkm_i2c_impl) { + .base.handle = NV_SUBDEV(I2C, 0x94), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_i2c_ctor, + .dtor = _nvkm_i2c_dtor, + .init = _nvkm_i2c_init, + .fini = _nvkm_i2c_fini, + }, + .sclass = g94_i2c_sclass, + .pad_x = &nv04_i2c_pad_oclass, + .pad_s = &g94_i2c_pad_oclass, + .aux = 4, + .aux_stat = g94_aux_stat, + .aux_mask = g94_aux_mask, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gf110.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gf110.c new file mode 100644 index 000000000..4d4ac6638 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gf110.c @@ -0,0 +1,106 @@ +/* + * 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" + +static int +gf110_i2c_sense_scl(struct nvkm_i2c_port *base) +{ + struct nv50_i2c_priv *priv = (void *)nvkm_i2c(base); + struct nv50_i2c_port *port = (void *)base; + return !!(nv_rd32(priv, port->addr) & 0x00000010); +} + +static int +gf110_i2c_sense_sda(struct nvkm_i2c_port *base) +{ + struct nv50_i2c_priv *priv = (void *)nvkm_i2c(base); + struct nv50_i2c_port *port = (void *)base; + return !!(nv_rd32(priv, port->addr) & 0x00000020); +} + +static const struct nvkm_i2c_func +gf110_i2c_func = { + .drive_scl = nv50_i2c_drive_scl, + .drive_sda = nv50_i2c_drive_sda, + .sense_scl = gf110_i2c_sense_scl, + .sense_sda = gf110_i2c_sense_sda, +}; + +int +gf110_i2c_port_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 index, + struct nvkm_object **pobject) +{ + struct dcb_i2c_entry *info = data; + struct nv50_i2c_port *port; + int ret; + + ret = nvkm_i2c_port_create(parent, engine, oclass, index, + &nvkm_i2c_bit_algo, &gf110_i2c_func, &port); + *pobject = nv_object(port); + if (ret) + return ret; + + port->state = 0x00000007; + port->addr = 0x00d014 + (info->drive * 0x20); + return 0; +} + +struct nvkm_oclass +gf110_i2c_sclass[] = { + { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_BIT), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf110_i2c_port_ctor, + .dtor = _nvkm_i2c_port_dtor, + .init = nv50_i2c_port_init, + .fini = _nvkm_i2c_port_fini, + }, + }, + { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_AUX), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = g94_aux_port_ctor, + .dtor = _nvkm_i2c_port_dtor, + .init = _nvkm_i2c_port_init, + .fini = _nvkm_i2c_port_fini, + }, + }, + {} +}; + +struct nvkm_oclass * +gf110_i2c_oclass = &(struct nvkm_i2c_impl) { + .base.handle = NV_SUBDEV(I2C, 0xd0), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_i2c_ctor, + .dtor = _nvkm_i2c_dtor, + .init = _nvkm_i2c_init, + .fini = _nvkm_i2c_fini, + }, + .sclass = gf110_i2c_sclass, + .pad_x = &nv04_i2c_pad_oclass, + .pad_s = &g94_i2c_pad_oclass, + .aux = 4, + .aux_stat = g94_aux_stat, + .aux_mask = g94_aux_mask, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gf117.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gf117.c new file mode 100644 index 000000000..e290b40f2 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gf117.c @@ -0,0 +1,38 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv50.h" + +struct nvkm_oclass * +gf117_i2c_oclass = &(struct nvkm_i2c_impl) { + .base.handle = NV_SUBDEV(I2C, 0xd7), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_i2c_ctor, + .dtor = _nvkm_i2c_dtor, + .init = _nvkm_i2c_init, + .fini = _nvkm_i2c_fini, + }, + .sclass = gf110_i2c_sclass, + .pad_x = &nv04_i2c_pad_oclass, + .pad_s = &nv04_i2c_pad_oclass, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gk104.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gk104.c new file mode 100644 index 000000000..1a464903a --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gk104.c @@ -0,0 +1,71 @@ +/* + * 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" + +void +gk104_aux_stat(struct nvkm_i2c *i2c, u32 *hi, u32 *lo, u32 *rq, u32 *tx) +{ + u32 intr = nv_rd32(i2c, 0x00dc60); + u32 stat = nv_rd32(i2c, 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; + } + nv_wr32(i2c, 0x00dc60, intr); +} + +void +gk104_aux_mask(struct nvkm_i2c *i2c, u32 type, u32 mask, u32 data) +{ + u32 temp = nv_rd32(i2c, 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); + } + } + nv_wr32(i2c, 0x00dc68, temp); +} + +struct nvkm_oclass * +gk104_i2c_oclass = &(struct nvkm_i2c_impl) { + .base.handle = NV_SUBDEV(I2C, 0xe0), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_i2c_ctor, + .dtor = _nvkm_i2c_dtor, + .init = _nvkm_i2c_init, + .fini = _nvkm_i2c_fini, + }, + .sclass = gf110_i2c_sclass, + .pad_x = &nv04_i2c_pad_oclass, + .pad_s = &g94_i2c_pad_oclass, + .aux = 4, + .aux_stat = gk104_aux_stat, + .aux_mask = gk104_aux_mask, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gm204.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gm204.c new file mode 100644 index 000000000..ab64237b3 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gm204.c @@ -0,0 +1,219 @@ +/* + * 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" + +#define AUX_DBG(fmt, args...) nv_debug(aux, "AUXCH(%d): " fmt, ch, ##args) +#define AUX_ERR(fmt, args...) nv_error(aux, "AUXCH(%d): " fmt, ch, ##args) + +static void +auxch_fini(struct nvkm_i2c *aux, int ch) +{ + nv_mask(aux, 0x00d954 + (ch * 0x50), 0x00310000, 0x00000000); +} + +static int +auxch_init(struct nvkm_i2c *aux, int ch) +{ + 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 = nv_rd32(aux, 0x00d954 + (ch * 0x50)); + udelay(1); + if (!timeout--) { + AUX_ERR("begin idle timeout 0x%08x\n", ctrl); + return -EBUSY; + } + } while (ctrl & 0x03010000); + + /* set some magic, and wait up to 1ms for it to appear */ + nv_mask(aux, 0x00d954 + (ch * 0x50), 0x00300000, ureq); + timeout = 1000; + do { + ctrl = nv_rd32(aux, 0x00d954 + (ch * 0x50)); + udelay(1); + if (!timeout--) { + AUX_ERR("magic wait 0x%08x\n", ctrl); + auxch_fini(aux, ch); + return -EBUSY; + } + } while ((ctrl & 0x03000000) != urep); + + return 0; +} + +int +gm204_aux(struct nvkm_i2c_port *base, bool retry, + u8 type, u32 addr, u8 *data, u8 size) +{ + struct nvkm_i2c *aux = nvkm_i2c(base); + struct nv50_i2c_port *port = (void *)base; + u32 ctrl, stat, timeout, retries; + u32 xbuf[4] = {}; + int ch = port->addr; + int ret, i; + + AUX_DBG("%d: 0x%08x %d\n", type, addr, size); + + ret = auxch_init(aux, ch); + if (ret) + goto out; + + stat = nv_rd32(aux, 0x00d958 + (ch * 0x50)); + if (!(stat & 0x10000000)) { + AUX_DBG("sink not detected\n"); + ret = -ENXIO; + goto out; + } + + if (!(type & 1)) { + memcpy(xbuf, data, size); + for (i = 0; i < 16; i += 4) { + AUX_DBG("wr 0x%08x\n", xbuf[i / 4]); + nv_wr32(aux, 0x00d930 + (ch * 0x50) + i, xbuf[i / 4]); + } + } + + ctrl = nv_rd32(aux, 0x00d954 + (ch * 0x50)); + ctrl &= ~0x0001f0ff; + ctrl |= type << 12; + ctrl |= size - 1; + nv_wr32(aux, 0x00d950 + (ch * 0x50), addr); + + /* (maybe) retry transaction a number of times on failure... */ + for (retries = 0; !ret && retries < 32; retries++) { + /* reset, and delay a while if this is a retry */ + nv_wr32(aux, 0x00d954 + (ch * 0x50), 0x80000000 | ctrl); + nv_wr32(aux, 0x00d954 + (ch * 0x50), 0x00000000 | ctrl); + if (retries) + udelay(400); + + /* transaction request, wait up to 1ms for it to complete */ + nv_wr32(aux, 0x00d954 + (ch * 0x50), 0x00010000 | ctrl); + + timeout = 1000; + do { + ctrl = nv_rd32(aux, 0x00d954 + (ch * 0x50)); + udelay(1); + if (!timeout--) { + AUX_ERR("tx req timeout 0x%08x\n", ctrl); + ret = -EIO; + goto out; + } + } while (ctrl & 0x00010000); + ret = 1; + + /* read status, and check if transaction completed ok */ + stat = nv_mask(aux, 0x00d958 + (ch * 0x50), 0, 0); + if ((stat & 0x000f0000) == 0x00080000 || + (stat & 0x000f0000) == 0x00020000) + ret = retry ? 0 : 1; + if ((stat & 0x00000100)) + ret = -ETIMEDOUT; + if ((stat & 0x00000e00)) + ret = -EIO; + + AUX_DBG("%02d 0x%08x 0x%08x\n", retries, ctrl, stat); + } + + if (type & 1) { + for (i = 0; i < 16; i += 4) { + xbuf[i / 4] = nv_rd32(aux, 0x00d940 + (ch * 0x50) + i); + AUX_DBG("rd 0x%08x\n", xbuf[i / 4]); + } + memcpy(data, xbuf, size); + } + +out: + auxch_fini(aux, ch); + return ret < 0 ? ret : (stat & 0x000f0000) >> 16; +} + +static const struct nvkm_i2c_func +gm204_aux_func = { + .aux = gm204_aux, +}; + +int +gm204_aux_port_ctor(struct nvkm_object *parent, + struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 index, + struct nvkm_object **pobject) +{ + struct dcb_i2c_entry *info = data; + struct nv50_i2c_port *port; + int ret; + + ret = nvkm_i2c_port_create(parent, engine, oclass, index, + &nvkm_i2c_aux_algo, &gm204_aux_func, &port); + *pobject = nv_object(port); + if (ret) + return ret; + + port->base.aux = info->auxch; + port->addr = info->auxch; + return 0; +} + +struct nvkm_oclass +gm204_i2c_sclass[] = { + { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_BIT), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf110_i2c_port_ctor, + .dtor = _nvkm_i2c_port_dtor, + .init = nv50_i2c_port_init, + .fini = _nvkm_i2c_port_fini, + }, + }, + { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_AUX), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gm204_aux_port_ctor, + .dtor = _nvkm_i2c_port_dtor, + .init = _nvkm_i2c_port_init, + .fini = _nvkm_i2c_port_fini, + }, + }, + {} +}; + +struct nvkm_oclass * +gm204_i2c_oclass = &(struct nvkm_i2c_impl) { + .base.handle = NV_SUBDEV(I2C, 0x24), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_i2c_ctor, + .dtor = _nvkm_i2c_dtor, + .init = _nvkm_i2c_init, + .fini = _nvkm_i2c_fini, + }, + .sclass = gm204_i2c_sclass, + .pad_x = &nv04_i2c_pad_oclass, + .pad_s = &gm204_i2c_pad_oclass, + .aux = 8, + .aux_stat = gk104_aux_stat, + .aux_mask = gk104_aux_mask, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv04.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv04.c new file mode 100644 index 000000000..4cdf1c489 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv04.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 + */ +#include "priv.h" + +#include + +struct nv04_i2c_priv { + struct nvkm_i2c base; +}; + +struct nv04_i2c_port { + struct nvkm_i2c_port base; + u8 drive; + u8 sense; +}; + +static void +nv04_i2c_drive_scl(struct nvkm_i2c_port *base, int state) +{ + struct nv04_i2c_priv *priv = (void *)nvkm_i2c(base); + struct nv04_i2c_port *port = (void *)base; + u8 val = nv_rdvgac(priv, 0, port->drive); + if (state) val |= 0x20; + else val &= 0xdf; + nv_wrvgac(priv, 0, port->drive, val | 0x01); +} + +static void +nv04_i2c_drive_sda(struct nvkm_i2c_port *base, int state) +{ + struct nv04_i2c_priv *priv = (void *)nvkm_i2c(base); + struct nv04_i2c_port *port = (void *)base; + u8 val = nv_rdvgac(priv, 0, port->drive); + if (state) val |= 0x10; + else val &= 0xef; + nv_wrvgac(priv, 0, port->drive, val | 0x01); +} + +static int +nv04_i2c_sense_scl(struct nvkm_i2c_port *base) +{ + struct nv04_i2c_priv *priv = (void *)nvkm_i2c(base); + struct nv04_i2c_port *port = (void *)base; + return !!(nv_rdvgac(priv, 0, port->sense) & 0x04); +} + +static int +nv04_i2c_sense_sda(struct nvkm_i2c_port *base) +{ + struct nv04_i2c_priv *priv = (void *)nvkm_i2c(base); + struct nv04_i2c_port *port = (void *)base; + return !!(nv_rdvgac(priv, 0, port->sense) & 0x08); +} + +static const struct nvkm_i2c_func +nv04_i2c_func = { + .drive_scl = nv04_i2c_drive_scl, + .drive_sda = nv04_i2c_drive_sda, + .sense_scl = nv04_i2c_sense_scl, + .sense_sda = nv04_i2c_sense_sda, +}; + +static int +nv04_i2c_port_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 index, + struct nvkm_object **pobject) +{ + struct dcb_i2c_entry *info = data; + struct nv04_i2c_port *port; + int ret; + + ret = nvkm_i2c_port_create(parent, engine, oclass, index, + &nvkm_i2c_bit_algo, &nv04_i2c_func, &port); + *pobject = nv_object(port); + if (ret) + return ret; + + port->drive = info->drive; + port->sense = info->sense; + return 0; +} + +static struct nvkm_oclass +nv04_i2c_sclass[] = { + { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NV04_BIT), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_i2c_port_ctor, + .dtor = _nvkm_i2c_port_dtor, + .init = _nvkm_i2c_port_init, + .fini = _nvkm_i2c_port_fini, + }, + }, + {} +}; + +struct nvkm_oclass * +nv04_i2c_oclass = &(struct nvkm_i2c_impl) { + .base.handle = NV_SUBDEV(I2C, 0x04), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_i2c_ctor, + .dtor = _nvkm_i2c_dtor, + .init = _nvkm_i2c_init, + .fini = _nvkm_i2c_fini, + }, + .sclass = nv04_i2c_sclass, + .pad_x = &nv04_i2c_pad_oclass, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv4e.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv4e.c new file mode 100644 index 000000000..046fe5e2e --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv4e.c @@ -0,0 +1,120 @@ +/* + * 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 + +struct nv4e_i2c_priv { + struct nvkm_i2c base; +}; + +struct nv4e_i2c_port { + struct nvkm_i2c_port base; + u32 addr; +}; + +static void +nv4e_i2c_drive_scl(struct nvkm_i2c_port *base, int state) +{ + struct nv4e_i2c_priv *priv = (void *)nvkm_i2c(base); + struct nv4e_i2c_port *port = (void *)base; + nv_mask(priv, port->addr, 0x2f, state ? 0x21 : 0x01); +} + +static void +nv4e_i2c_drive_sda(struct nvkm_i2c_port *base, int state) +{ + struct nv4e_i2c_priv *priv = (void *)nvkm_i2c(base); + struct nv4e_i2c_port *port = (void *)base; + nv_mask(priv, port->addr, 0x1f, state ? 0x11 : 0x01); +} + +static int +nv4e_i2c_sense_scl(struct nvkm_i2c_port *base) +{ + struct nv4e_i2c_priv *priv = (void *)nvkm_i2c(base); + struct nv4e_i2c_port *port = (void *)base; + return !!(nv_rd32(priv, port->addr) & 0x00040000); +} + +static int +nv4e_i2c_sense_sda(struct nvkm_i2c_port *base) +{ + struct nv4e_i2c_priv *priv = (void *)nvkm_i2c(base); + struct nv4e_i2c_port *port = (void *)base; + return !!(nv_rd32(priv, port->addr) & 0x00080000); +} + +static const struct nvkm_i2c_func +nv4e_i2c_func = { + .drive_scl = nv4e_i2c_drive_scl, + .drive_sda = nv4e_i2c_drive_sda, + .sense_scl = nv4e_i2c_sense_scl, + .sense_sda = nv4e_i2c_sense_sda, +}; + +static int +nv4e_i2c_port_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 index, + struct nvkm_object **pobject) +{ + struct dcb_i2c_entry *info = data; + struct nv4e_i2c_port *port; + int ret; + + ret = nvkm_i2c_port_create(parent, engine, oclass, index, + &nvkm_i2c_bit_algo, &nv4e_i2c_func, &port); + *pobject = nv_object(port); + if (ret) + return ret; + + port->addr = 0x600800 + info->drive; + return 0; +} + +static struct nvkm_oclass +nv4e_i2c_sclass[] = { + { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NV4E_BIT), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv4e_i2c_port_ctor, + .dtor = _nvkm_i2c_port_dtor, + .init = _nvkm_i2c_port_init, + .fini = _nvkm_i2c_port_fini, + }, + }, + {} +}; + +struct nvkm_oclass * +nv4e_i2c_oclass = &(struct nvkm_i2c_impl) { + .base.handle = NV_SUBDEV(I2C, 0x4e), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_i2c_ctor, + .dtor = _nvkm_i2c_dtor, + .init = _nvkm_i2c_init, + .fini = _nvkm_i2c_fini, + }, + .sclass = nv4e_i2c_sclass, + .pad_x = &nv04_i2c_pad_oclass, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv50.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv50.c new file mode 100644 index 000000000..fba5b26a5 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/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 "nv50.h" + +void +nv50_i2c_drive_scl(struct nvkm_i2c_port *base, int state) +{ + struct nv50_i2c_priv *priv = (void *)nvkm_i2c(base); + struct nv50_i2c_port *port = (void *)base; + if (state) port->state |= 0x01; + else port->state &= 0xfe; + nv_wr32(priv, port->addr, port->state); +} + +void +nv50_i2c_drive_sda(struct nvkm_i2c_port *base, int state) +{ + struct nv50_i2c_priv *priv = (void *)nvkm_i2c(base); + struct nv50_i2c_port *port = (void *)base; + if (state) port->state |= 0x02; + else port->state &= 0xfd; + nv_wr32(priv, port->addr, port->state); +} + +int +nv50_i2c_sense_scl(struct nvkm_i2c_port *base) +{ + struct nv50_i2c_priv *priv = (void *)nvkm_i2c(base); + struct nv50_i2c_port *port = (void *)base; + return !!(nv_rd32(priv, port->addr) & 0x00000001); +} + +int +nv50_i2c_sense_sda(struct nvkm_i2c_port *base) +{ + struct nv50_i2c_priv *priv = (void *)nvkm_i2c(base); + struct nv50_i2c_port *port = (void *)base; + return !!(nv_rd32(priv, port->addr) & 0x00000002); +} + +static const struct nvkm_i2c_func +nv50_i2c_func = { + .drive_scl = nv50_i2c_drive_scl, + .drive_sda = nv50_i2c_drive_sda, + .sense_scl = nv50_i2c_sense_scl, + .sense_sda = nv50_i2c_sense_sda, +}; + +const u32 nv50_i2c_addr[] = { + 0x00e138, 0x00e150, 0x00e168, 0x00e180, + 0x00e254, 0x00e274, 0x00e764, 0x00e780, + 0x00e79c, 0x00e7b8 +}; +const int nv50_i2c_addr_nr = ARRAY_SIZE(nv50_i2c_addr); + +static int +nv50_i2c_port_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 index, + struct nvkm_object **pobject) +{ + struct dcb_i2c_entry *info = data; + struct nv50_i2c_port *port; + int ret; + + ret = nvkm_i2c_port_create(parent, engine, oclass, index, + &nvkm_i2c_bit_algo, &nv50_i2c_func, &port); + *pobject = nv_object(port); + if (ret) + return ret; + + if (info->drive >= nv50_i2c_addr_nr) + return -EINVAL; + + port->state = 0x00000007; + port->addr = nv50_i2c_addr[info->drive]; + return 0; +} + +int +nv50_i2c_port_init(struct nvkm_object *object) +{ + struct nv50_i2c_priv *priv = (void *)nvkm_i2c(object); + struct nv50_i2c_port *port = (void *)object; + nv_wr32(priv, port->addr, port->state); + return nvkm_i2c_port_init(&port->base); +} + +static struct nvkm_oclass +nv50_i2c_sclass[] = { + { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_BIT), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_i2c_port_ctor, + .dtor = _nvkm_i2c_port_dtor, + .init = nv50_i2c_port_init, + .fini = _nvkm_i2c_port_fini, + }, + }, + {} +}; + +struct nvkm_oclass * +nv50_i2c_oclass = &(struct nvkm_i2c_impl) { + .base.handle = NV_SUBDEV(I2C, 0x50), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_i2c_ctor, + .dtor = _nvkm_i2c_dtor, + .init = _nvkm_i2c_init, + .fini = _nvkm_i2c_fini, + }, + .sclass = nv50_i2c_sclass, + .pad_x = &nv04_i2c_pad_oclass, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv50.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv50.h new file mode 100644 index 000000000..b3139e721 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv50.h @@ -0,0 +1,32 @@ +#ifndef __NV50_I2C_H__ +#define __NV50_I2C_H__ +#include "priv.h" + +struct nv50_i2c_priv { + struct nvkm_i2c base; +}; + +struct nv50_i2c_port { + struct nvkm_i2c_port base; + u32 addr; + u32 state; +}; + +extern const u32 nv50_i2c_addr[]; +extern const int nv50_i2c_addr_nr; +int nv50_i2c_port_init(struct nvkm_object *); +int nv50_i2c_sense_scl(struct nvkm_i2c_port *); +int nv50_i2c_sense_sda(struct nvkm_i2c_port *); +void nv50_i2c_drive_scl(struct nvkm_i2c_port *, int state); +void nv50_i2c_drive_sda(struct nvkm_i2c_port *, int state); + +int g94_aux_port_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); +void g94_i2c_acquire(struct nvkm_i2c_port *); +void g94_i2c_release(struct nvkm_i2c_port *); + +int gf110_i2c_port_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/pad.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/pad.c new file mode 100644 index 000000000..a242eeb67 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/pad.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 "pad.h" + +int +_nvkm_i2c_pad_fini(struct nvkm_object *object, bool suspend) +{ + struct nvkm_i2c_pad *pad = (void *)object; + DBG("-> NULL\n"); + pad->port = NULL; + return nvkm_object_fini(&pad->base, suspend); +} + +int +_nvkm_i2c_pad_init(struct nvkm_object *object) +{ + struct nvkm_i2c_pad *pad = (void *)object; + DBG("-> PORT:%02x\n", pad->next->index); + pad->port = pad->next; + return nvkm_object_init(&pad->base); +} + +int +nvkm_i2c_pad_create_(struct nvkm_object *parent, + struct nvkm_object *engine, + struct nvkm_oclass *oclass, int index, + int size, void **pobject) +{ + struct nvkm_i2c *i2c = nvkm_i2c(parent); + struct nvkm_i2c_port *port; + struct nvkm_i2c_pad *pad; + int ret; + + list_for_each_entry(port, &i2c->ports, head) { + pad = nvkm_i2c_pad(port); + if (pad->index == index) { + atomic_inc(&nv_object(pad)->refcount); + *pobject = pad; + return 1; + } + } + + ret = nvkm_object_create_(parent, engine, oclass, 0, size, pobject); + pad = *pobject; + if (ret) + return ret; + + pad->index = index; + return 0; +} + +int +_nvkm_i2c_pad_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 index, + struct nvkm_object **pobject) +{ + struct nvkm_i2c_pad *pad; + int ret; + ret = nvkm_i2c_pad_create(parent, engine, oclass, index, &pad); + *pobject = nv_object(pad); + return ret; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/pad.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/pad.h new file mode 100644 index 000000000..f3422cc6f --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/pad.h @@ -0,0 +1,56 @@ +#ifndef __NVKM_I2C_PAD_H__ +#define __NVKM_I2C_PAD_H__ +#include "priv.h" + +struct nvkm_i2c_pad { + struct nvkm_object base; + int index; + struct nvkm_i2c_port *port; + struct nvkm_i2c_port *next; +}; + +static inline struct nvkm_i2c_pad * +nvkm_i2c_pad(struct nvkm_i2c_port *port) +{ + struct nvkm_object *pad = nv_object(port); + while (!nv_iclass(pad->parent, NV_SUBDEV_CLASS)) + pad = pad->parent; + return (void *)pad; +} + +#define nvkm_i2c_pad_create(p,e,o,i,d) \ + nvkm_i2c_pad_create_((p), (e), (o), (i), sizeof(**d), (void **)d) +#define nvkm_i2c_pad_destroy(p) ({ \ + struct nvkm_i2c_pad *_p = (p); \ + _nvkm_i2c_pad_dtor(nv_object(_p)); \ +}) +#define nvkm_i2c_pad_init(p) ({ \ + struct nvkm_i2c_pad *_p = (p); \ + _nvkm_i2c_pad_init(nv_object(_p)); \ +}) +#define nvkm_i2c_pad_fini(p,s) ({ \ + struct nvkm_i2c_pad *_p = (p); \ + _nvkm_i2c_pad_fini(nv_object(_p), (s)); \ +}) + +int nvkm_i2c_pad_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, int index, int, void **); + +int _nvkm_i2c_pad_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); +#define _nvkm_i2c_pad_dtor nvkm_object_destroy +int _nvkm_i2c_pad_init(struct nvkm_object *); +int _nvkm_i2c_pad_fini(struct nvkm_object *, bool); + +#ifndef MSG +#define MSG(l,f,a...) do { \ + struct nvkm_i2c_pad *_pad = (void *)pad; \ + nv_##l(_pad, "PAD:%c:%02x: "f, \ + _pad->index >= 0x100 ? 'X' : 'S', \ + _pad->index >= 0x100 ? _pad->index - 0x100 : _pad->index, ##a); \ +} while(0) +#define DBG(f,a...) MSG(debug, f, ##a) +#define ERR(f,a...) MSG(error, f, ##a) +#endif +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padg94.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padg94.c new file mode 100644 index 000000000..e9832f7a7 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padg94.c @@ -0,0 +1,85 @@ +/* + * 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" + +struct g94_i2c_pad { + struct nvkm_i2c_pad base; + int addr; +}; + +static int +g94_i2c_pad_fini(struct nvkm_object *object, bool suspend) +{ + struct nvkm_i2c *i2c = (void *)nvkm_i2c(object); + struct g94_i2c_pad *pad = (void *)object; + nv_mask(i2c, 0x00e50c + pad->addr, 0x00000001, 0x00000001); + return nvkm_i2c_pad_fini(&pad->base, suspend); +} + +static int +g94_i2c_pad_init(struct nvkm_object *object) +{ + struct nvkm_i2c *i2c = (void *)nvkm_i2c(object); + struct g94_i2c_pad *pad = (void *)object; + + switch (nv_oclass(pad->base.next)->handle) { + case NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_AUX): + nv_mask(i2c, 0x00e500 + pad->addr, 0x0000c003, 0x00000002); + break; + case NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_BIT): + default: + nv_mask(i2c, 0x00e500 + pad->addr, 0x0000c003, 0x0000c001); + break; + } + + nv_mask(i2c, 0x00e50c + pad->addr, 0x00000001, 0x00000000); + return nvkm_i2c_pad_init(&pad->base); +} + +static int +g94_i2c_pad_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 index, + struct nvkm_object **pobject) +{ + struct g94_i2c_pad *pad; + int ret; + + ret = nvkm_i2c_pad_create(parent, engine, oclass, index, &pad); + *pobject = nv_object(pad); + if (ret) + return ret; + + pad->addr = index * 0x50;; + return 0; +} + +struct nvkm_oclass +g94_i2c_pad_oclass = { + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = g94_i2c_pad_ctor, + .dtor = _nvkm_i2c_pad_dtor, + .init = g94_i2c_pad_init, + .fini = g94_i2c_pad_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padgm204.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padgm204.c new file mode 100644 index 000000000..be5904054 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padgm204.c @@ -0,0 +1,85 @@ +/* + * 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" + +struct gm204_i2c_pad { + struct nvkm_i2c_pad base; + int addr; +}; + +static int +gm204_i2c_pad_fini(struct nvkm_object *object, bool suspend) +{ + struct nvkm_i2c *i2c = (void *)nvkm_i2c(object); + struct gm204_i2c_pad *pad = (void *)object; + nv_mask(i2c, 0x00d97c + pad->addr, 0x00000001, 0x00000001); + return nvkm_i2c_pad_fini(&pad->base, suspend); +} + +static int +gm204_i2c_pad_init(struct nvkm_object *object) +{ + struct nvkm_i2c *i2c = (void *)nvkm_i2c(object); + struct gm204_i2c_pad *pad = (void *)object; + + switch (nv_oclass(pad->base.next)->handle) { + case NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_AUX): + nv_mask(i2c, 0x00d970 + pad->addr, 0x0000c003, 0x00000002); + break; + case NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_BIT): + default: + nv_mask(i2c, 0x00d970 + pad->addr, 0x0000c003, 0x0000c001); + break; + } + + nv_mask(i2c, 0x00d97c + pad->addr, 0x00000001, 0x00000000); + return nvkm_i2c_pad_init(&pad->base); +} + +static int +gm204_i2c_pad_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 index, + struct nvkm_object **pobject) +{ + struct gm204_i2c_pad *pad; + int ret; + + ret = nvkm_i2c_pad_create(parent, engine, oclass, index, &pad); + *pobject = nv_object(pad); + if (ret) + return ret; + + pad->addr = index * 0x50;; + return 0; +} + +struct nvkm_oclass +gm204_i2c_pad_oclass = { + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gm204_i2c_pad_ctor, + .dtor = _nvkm_i2c_pad_dtor, + .init = gm204_i2c_pad_init, + .fini = gm204_i2c_pad_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padnv04.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padnv04.c new file mode 100644 index 000000000..22c7daaad --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padnv04.c @@ -0,0 +1,34 @@ +/* + * 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" + +struct nvkm_oclass +nv04_i2c_pad_oclass = { + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_i2c_pad_ctor, + .dtor = _nvkm_i2c_pad_dtor, + .init = _nvkm_i2c_pad_init, + .fini = _nvkm_i2c_pad_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/port.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/port.h new file mode 100644 index 000000000..586f53dad --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/port.h @@ -0,0 +1,13 @@ +#ifndef __NVKM_I2C_PORT_H__ +#define __NVKM_I2C_PORT_H__ +#include "priv.h" + +#ifndef MSG +#define MSG(l,f,a...) do { \ + struct nvkm_i2c_port *_port = (void *)port; \ + nv_##l(_port, "PORT:%02x: "f, _port->index, ##a); \ +} while(0) +#define DBG(f,a...) MSG(debug, f, ##a) +#define ERR(f,a...) MSG(error, f, ##a) +#endif +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/priv.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/priv.h new file mode 100644 index 000000000..6586e1567 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/priv.h @@ -0,0 +1,87 @@ +#ifndef __NVKM_I2C_PRIV_H__ +#define __NVKM_I2C_PRIV_H__ +#include + +extern struct nvkm_oclass nv04_i2c_pad_oclass; +extern struct nvkm_oclass g94_i2c_pad_oclass; +extern struct nvkm_oclass gm204_i2c_pad_oclass; + +#define nvkm_i2c_port_create(p,e,o,i,a,f,d) \ + nvkm_i2c_port_create_((p), (e), (o), (i), (a), (f), \ + sizeof(**d), (void **)d) +#define nvkm_i2c_port_destroy(p) ({ \ + struct nvkm_i2c_port *port = (p); \ + _nvkm_i2c_port_dtor(nv_object(i2c)); \ +}) +#define nvkm_i2c_port_init(p) \ + nvkm_object_init(&(p)->base) +#define nvkm_i2c_port_fini(p,s) \ + nvkm_object_fini(&(p)->base, (s)) + +int nvkm_i2c_port_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, u8, + const struct i2c_algorithm *, + const struct nvkm_i2c_func *, + int, void **); +void _nvkm_i2c_port_dtor(struct nvkm_object *); +#define _nvkm_i2c_port_init nvkm_object_init +int _nvkm_i2c_port_fini(struct nvkm_object *, bool); + +#define nvkm_i2c_create(p,e,o,d) \ + nvkm_i2c_create_((p), (e), (o), sizeof(**d), (void **)d) +#define nvkm_i2c_destroy(p) ({ \ + struct nvkm_i2c *i2c = (p); \ + _nvkm_i2c_dtor(nv_object(i2c)); \ +}) +#define nvkm_i2c_init(p) ({ \ + struct nvkm_i2c *i2c = (p); \ + _nvkm_i2c_init(nv_object(i2c)); \ +}) +#define nvkm_i2c_fini(p,s) ({ \ + struct nvkm_i2c *i2c = (p); \ + _nvkm_i2c_fini(nv_object(i2c), (s)); \ +}) + +int nvkm_i2c_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, int, void **); +int _nvkm_i2c_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); +void _nvkm_i2c_dtor(struct nvkm_object *); +int _nvkm_i2c_init(struct nvkm_object *); +int _nvkm_i2c_fini(struct nvkm_object *, bool); + +extern struct nvkm_oclass nvkm_anx9805_sclass[]; +extern struct nvkm_oclass gf110_i2c_sclass[]; + +extern const struct i2c_algorithm nvkm_i2c_bit_algo; +extern const struct i2c_algorithm nvkm_i2c_aux_algo; + +struct nvkm_i2c_impl { + struct nvkm_oclass base; + + /* supported i2c port classes */ + struct nvkm_oclass *sclass; + struct nvkm_oclass *pad_x; + struct nvkm_oclass *pad_s; + + /* 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); +}; + +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/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ibus/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ibus/Kbuild new file mode 100644 index 000000000..a0b12d272 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ibus/Kbuild @@ -0,0 +1,3 @@ +nvkm-y += nvkm/subdev/ibus/gf100.o +nvkm-y += nvkm/subdev/ibus/gk104.o +nvkm-y += nvkm/subdev/ibus/gk20a.o diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ibus/gf100.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ibus/gf100.c new file mode 100644 index 000000000..8e578f802 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ibus/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 + +struct gf100_ibus_priv { + struct nvkm_ibus base; +}; + +static void +gf100_ibus_intr_hub(struct gf100_ibus_priv *priv, int i) +{ + u32 addr = nv_rd32(priv, 0x122120 + (i * 0x0400)); + u32 data = nv_rd32(priv, 0x122124 + (i * 0x0400)); + u32 stat = nv_rd32(priv, 0x122128 + (i * 0x0400)); + nv_error(priv, "HUB%d: 0x%06x 0x%08x (0x%08x)\n", i, addr, data, stat); + nv_mask(priv, 0x122128 + (i * 0x0400), 0x00000200, 0x00000000); +} + +static void +gf100_ibus_intr_rop(struct gf100_ibus_priv *priv, int i) +{ + u32 addr = nv_rd32(priv, 0x124120 + (i * 0x0400)); + u32 data = nv_rd32(priv, 0x124124 + (i * 0x0400)); + u32 stat = nv_rd32(priv, 0x124128 + (i * 0x0400)); + nv_error(priv, "ROP%d: 0x%06x 0x%08x (0x%08x)\n", i, addr, data, stat); + nv_mask(priv, 0x124128 + (i * 0x0400), 0x00000200, 0x00000000); +} + +static void +gf100_ibus_intr_gpc(struct gf100_ibus_priv *priv, int i) +{ + u32 addr = nv_rd32(priv, 0x128120 + (i * 0x0400)); + u32 data = nv_rd32(priv, 0x128124 + (i * 0x0400)); + u32 stat = nv_rd32(priv, 0x128128 + (i * 0x0400)); + nv_error(priv, "GPC%d: 0x%06x 0x%08x (0x%08x)\n", i, addr, data, stat); + nv_mask(priv, 0x128128 + (i * 0x0400), 0x00000200, 0x00000000); +} + +static void +gf100_ibus_intr(struct nvkm_subdev *subdev) +{ + struct gf100_ibus_priv *priv = (void *)subdev; + u32 intr0 = nv_rd32(priv, 0x121c58); + u32 intr1 = nv_rd32(priv, 0x121c5c); + u32 hubnr = nv_rd32(priv, 0x121c70); + u32 ropnr = nv_rd32(priv, 0x121c74); + u32 gpcnr = nv_rd32(priv, 0x121c78); + u32 i; + + for (i = 0; (intr0 & 0x0000ff00) && i < hubnr; i++) { + u32 stat = 0x00000100 << i; + if (intr0 & stat) { + gf100_ibus_intr_hub(priv, i); + intr0 &= ~stat; + } + } + + for (i = 0; (intr0 & 0xffff0000) && i < ropnr; i++) { + u32 stat = 0x00010000 << i; + if (intr0 & stat) { + gf100_ibus_intr_rop(priv, i); + intr0 &= ~stat; + } + } + + for (i = 0; intr1 && i < gpcnr; i++) { + u32 stat = 0x00000001 << i; + if (intr1 & stat) { + gf100_ibus_intr_gpc(priv, i); + intr1 &= ~stat; + } + } +} + +static int +gf100_ibus_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gf100_ibus_priv *priv; + int ret; + + ret = nvkm_ibus_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->intr = gf100_ibus_intr; + return 0; +} + +struct nvkm_oclass +gf100_ibus_oclass = { + .handle = NV_SUBDEV(IBUS, 0xc0), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_ibus_ctor, + .dtor = _nvkm_ibus_dtor, + .init = _nvkm_ibus_init, + .fini = _nvkm_ibus_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ibus/gk104.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ibus/gk104.c new file mode 100644 index 000000000..7b6e9a6cd --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ibus/gk104.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 + +struct gk104_ibus_priv { + struct nvkm_ibus base; +}; + +static void +gk104_ibus_intr_hub(struct gk104_ibus_priv *priv, int i) +{ + u32 addr = nv_rd32(priv, 0x122120 + (i * 0x0800)); + u32 data = nv_rd32(priv, 0x122124 + (i * 0x0800)); + u32 stat = nv_rd32(priv, 0x122128 + (i * 0x0800)); + nv_error(priv, "HUB%d: 0x%06x 0x%08x (0x%08x)\n", i, addr, data, stat); + nv_mask(priv, 0x122128 + (i * 0x0800), 0x00000200, 0x00000000); +} + +static void +gk104_ibus_intr_rop(struct gk104_ibus_priv *priv, int i) +{ + u32 addr = nv_rd32(priv, 0x124120 + (i * 0x0800)); + u32 data = nv_rd32(priv, 0x124124 + (i * 0x0800)); + u32 stat = nv_rd32(priv, 0x124128 + (i * 0x0800)); + nv_error(priv, "ROP%d: 0x%06x 0x%08x (0x%08x)\n", i, addr, data, stat); + nv_mask(priv, 0x124128 + (i * 0x0800), 0x00000200, 0x00000000); +} + +static void +gk104_ibus_intr_gpc(struct gk104_ibus_priv *priv, int i) +{ + u32 addr = nv_rd32(priv, 0x128120 + (i * 0x0800)); + u32 data = nv_rd32(priv, 0x128124 + (i * 0x0800)); + u32 stat = nv_rd32(priv, 0x128128 + (i * 0x0800)); + nv_error(priv, "GPC%d: 0x%06x 0x%08x (0x%08x)\n", i, addr, data, stat); + nv_mask(priv, 0x128128 + (i * 0x0800), 0x00000200, 0x00000000); +} + +static void +gk104_ibus_intr(struct nvkm_subdev *subdev) +{ + struct gk104_ibus_priv *priv = (void *)subdev; + u32 intr0 = nv_rd32(priv, 0x120058); + u32 intr1 = nv_rd32(priv, 0x12005c); + u32 hubnr = nv_rd32(priv, 0x120070); + u32 ropnr = nv_rd32(priv, 0x120074); + u32 gpcnr = nv_rd32(priv, 0x120078); + u32 i; + + for (i = 0; (intr0 & 0x0000ff00) && i < hubnr; i++) { + u32 stat = 0x00000100 << i; + if (intr0 & stat) { + gk104_ibus_intr_hub(priv, i); + intr0 &= ~stat; + } + } + + for (i = 0; (intr0 & 0xffff0000) && i < ropnr; i++) { + u32 stat = 0x00010000 << i; + if (intr0 & stat) { + gk104_ibus_intr_rop(priv, i); + intr0 &= ~stat; + } + } + + for (i = 0; intr1 && i < gpcnr; i++) { + u32 stat = 0x00000001 << i; + if (intr1 & stat) { + gk104_ibus_intr_gpc(priv, i); + intr1 &= ~stat; + } + } +} + +static int +gk104_ibus_init(struct nvkm_object *object) +{ + struct gk104_ibus_priv *priv = (void *)object; + int ret = nvkm_ibus_init(&priv->base); + if (ret == 0) { + nv_mask(priv, 0x122318, 0x0003ffff, 0x00001000); + nv_mask(priv, 0x12231c, 0x0003ffff, 0x00000200); + nv_mask(priv, 0x122310, 0x0003ffff, 0x00000800); + nv_mask(priv, 0x122348, 0x0003ffff, 0x00000100); + nv_mask(priv, 0x1223b0, 0x0003ffff, 0x00000fff); + nv_mask(priv, 0x122348, 0x0003ffff, 0x00000200); + nv_mask(priv, 0x122358, 0x0003ffff, 0x00002880); + } + return ret; +} + +static int +gk104_ibus_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gk104_ibus_priv *priv; + int ret; + + ret = nvkm_ibus_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->intr = gk104_ibus_intr; + return 0; +} + +struct nvkm_oclass +gk104_ibus_oclass = { + .handle = NV_SUBDEV(IBUS, 0xe0), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gk104_ibus_ctor, + .dtor = _nvkm_ibus_dtor, + .init = gk104_ibus_init, + .fini = _nvkm_ibus_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ibus/gk20a.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ibus/gk20a.c new file mode 100644 index 000000000..c0fdb89e7 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ibus/gk20a.c @@ -0,0 +1,102 @@ +/* + * 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 + +struct gk20a_ibus_priv { + struct nvkm_ibus base; +}; + +static void +gk20a_ibus_init_priv_ring(struct gk20a_ibus_priv *priv) +{ + nv_mask(priv, 0x137250, 0x3f, 0); + + nv_mask(priv, 0x000200, 0x20, 0); + usleep_range(20, 30); + nv_mask(priv, 0x000200, 0x20, 0x20); + + nv_wr32(priv, 0x12004c, 0x4); + nv_wr32(priv, 0x122204, 0x2); + nv_rd32(priv, 0x122204); +} + +static void +gk20a_ibus_intr(struct nvkm_subdev *subdev) +{ + struct gk20a_ibus_priv *priv = (void *)subdev; + u32 status0 = nv_rd32(priv, 0x120058); + + if (status0 & 0x7) { + nv_debug(priv, "resetting priv ring\n"); + gk20a_ibus_init_priv_ring(priv); + } + + /* Acknowledge interrupt */ + nv_mask(priv, 0x12004c, 0x2, 0x2); + + if (!nv_wait(subdev, 0x12004c, 0x3f, 0x00)) + nv_warn(priv, "timeout waiting for ringmaster ack\n"); +} + +static int +gk20a_ibus_init(struct nvkm_object *object) +{ + struct gk20a_ibus_priv *priv = (void *)object; + int ret; + + ret = _nvkm_ibus_init(object); + if (ret) + return ret; + + gk20a_ibus_init_priv_ring(priv); + + return 0; +} + +static int +gk20a_ibus_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gk20a_ibus_priv *priv; + int ret; + + ret = nvkm_ibus_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->intr = gk20a_ibus_intr; + return 0; +} + +struct nvkm_oclass +gk20a_ibus_oclass = { + .handle = NV_SUBDEV(IBUS, 0xea), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gk20a_ibus_ctor, + .dtor = _nvkm_ibus_dtor, + .init = gk20a_ibus_init, + .fini = _nvkm_ibus_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/Kbuild new file mode 100644 index 000000000..13bb7fc0a --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/Kbuild @@ -0,0 +1,5 @@ +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/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/base.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/base.c new file mode 100644 index 000000000..d16358cc6 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/base.c @@ -0,0 +1,146 @@ +/* + * 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 + *****************************************************************************/ + +void +_nvkm_instobj_dtor(struct nvkm_object *object) +{ + struct nvkm_instmem *imem = nvkm_instmem(object); + struct nvkm_instobj *iobj = (void *)object; + + mutex_lock(&nv_subdev(imem)->mutex); + list_del(&iobj->head); + mutex_unlock(&nv_subdev(imem)->mutex); + + return nvkm_object_destroy(&iobj->base); +} + +int +nvkm_instobj_create_(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, int length, void **pobject) +{ + struct nvkm_instmem *imem = nvkm_instmem(parent); + struct nvkm_instobj *iobj; + int ret; + + ret = nvkm_object_create_(parent, engine, oclass, NV_MEMOBJ_CLASS, + length, pobject); + iobj = *pobject; + if (ret) + return ret; + + mutex_lock(&imem->base.mutex); + list_add(&iobj->head, &imem->list); + mutex_unlock(&imem->base.mutex); + return 0; +} + +/****************************************************************************** + * instmem subdev base implementation + *****************************************************************************/ + +static int +nvkm_instmem_alloc(struct nvkm_instmem *imem, struct nvkm_object *parent, + u32 size, u32 align, struct nvkm_object **pobject) +{ + struct nvkm_instmem_impl *impl = (void *)imem->base.object.oclass; + struct nvkm_instobj_args args = { .size = size, .align = align }; + return nvkm_object_ctor(parent, &parent->engine->subdev.object, + impl->instobj, &args, sizeof(args), pobject); +} + +int +_nvkm_instmem_fini(struct nvkm_object *object, bool suspend) +{ + struct nvkm_instmem *imem = (void *)object; + struct nvkm_instobj *iobj; + int i, ret = 0; + + if (suspend) { + mutex_lock(&imem->base.mutex); + list_for_each_entry(iobj, &imem->list, head) { + iobj->suspend = vmalloc(iobj->size); + if (!iobj->suspend) { + ret = -ENOMEM; + break; + } + + for (i = 0; i < iobj->size; i += 4) + iobj->suspend[i / 4] = nv_ro32(iobj, i); + } + mutex_unlock(&imem->base.mutex); + if (ret) + return ret; + } + + return nvkm_subdev_fini(&imem->base, suspend); +} + +int +_nvkm_instmem_init(struct nvkm_object *object) +{ + struct nvkm_instmem *imem = (void *)object; + struct nvkm_instobj *iobj; + int ret, i; + + ret = nvkm_subdev_init(&imem->base); + if (ret) + return ret; + + mutex_lock(&imem->base.mutex); + list_for_each_entry(iobj, &imem->list, head) { + if (iobj->suspend) { + for (i = 0; i < iobj->size; i += 4) + nv_wo32(iobj, i, iobj->suspend[i / 4]); + vfree(iobj->suspend); + iobj->suspend = NULL; + } + } + mutex_unlock(&imem->base.mutex); + return 0; +} + +int +nvkm_instmem_create_(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, int length, void **pobject) +{ + struct nvkm_instmem *imem; + int ret; + + ret = nvkm_subdev_create_(parent, engine, oclass, 0, "INSTMEM", + "instmem", length, pobject); + imem = *pobject; + if (ret) + return ret; + + INIT_LIST_HEAD(&imem->list); + imem->alloc = nvkm_instmem_alloc; + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c new file mode 100644 index 000000000..dd0994d9e --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c @@ -0,0 +1,440 @@ +/* + * 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 providing + * coherent read and write operations. + * + * Instmem can be allocated through two means: + * 1) If an IOMMU mapping 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 mapping is probed, the DMA API is used to allocate physically + * contiguous memory. + * + * In both cases CPU read and writes are performed using PRAMIN (i.e. using the + * GPU path) to ensure these operations are coherent for the GPU. This allows us + * to use more "relaxed" allocation parameters when using the DMA API, since we + * never need a kernel mapping. + */ + +#include +#include +#include + +#ifdef __KERNEL__ +#include +#include +#include +#endif + +#include "priv.h" + +struct gk20a_instobj_priv { + struct nvkm_instobj base; + /* Must be second member here - see nouveau_gpuobj_map_vm() */ + struct nvkm_mem *mem; + /* Pointed by mem */ + struct nvkm_mem _mem; +}; + +/* + * Used for objects allocated using the DMA API + */ +struct gk20a_instobj_dma { + struct gk20a_instobj_priv base; + + void *cpuaddr; + dma_addr_t handle; + struct nvkm_mm_node r; +}; + +/* + * Used for objects flattened using the IOMMU API + */ +struct gk20a_instobj_iommu { + struct gk20a_instobj_priv base; + + /* array of base.mem->size pages */ + struct page *pages[]; +}; + +struct gk20a_instmem_priv { + struct nvkm_instmem base; + spinlock_t lock; + u64 addr; + + /* Only used if IOMMU if present */ + struct mutex *mm_mutex; + struct nvkm_mm *mm; + struct iommu_domain *domain; + unsigned long iommu_pgshift; + + /* Only used by DMA API */ + struct dma_attrs attrs; +}; + +/* + * Use PRAMIN to read/write data and avoid coherency issues. + * PRAMIN uses the GPU path and ensures data will always be coherent. + * + * A dynamic mapping based solution would be desirable in the future, but + * the issue remains of how to maintain coherency efficiently. On ARM it is + * not easy (if possible at all?) to create uncached temporary mappings. + */ + +static u32 +gk20a_instobj_rd32(struct nvkm_object *object, u64 offset) +{ + struct gk20a_instmem_priv *priv = (void *)nvkm_instmem(object); + struct gk20a_instobj_priv *node = (void *)object; + unsigned long flags; + u64 base = (node->mem->offset + offset) & 0xffffff00000ULL; + u64 addr = (node->mem->offset + offset) & 0x000000fffffULL; + u32 data; + + spin_lock_irqsave(&priv->lock, flags); + if (unlikely(priv->addr != base)) { + nv_wr32(priv, 0x001700, base >> 16); + priv->addr = base; + } + data = nv_rd32(priv, 0x700000 + addr); + spin_unlock_irqrestore(&priv->lock, flags); + return data; +} + +static void +gk20a_instobj_wr32(struct nvkm_object *object, u64 offset, u32 data) +{ + struct gk20a_instmem_priv *priv = (void *)nvkm_instmem(object); + struct gk20a_instobj_priv *node = (void *)object; + unsigned long flags; + u64 base = (node->mem->offset + offset) & 0xffffff00000ULL; + u64 addr = (node->mem->offset + offset) & 0x000000fffffULL; + + spin_lock_irqsave(&priv->lock, flags); + if (unlikely(priv->addr != base)) { + nv_wr32(priv, 0x001700, base >> 16); + priv->addr = base; + } + nv_wr32(priv, 0x700000 + addr, data); + spin_unlock_irqrestore(&priv->lock, flags); +} + +static void +gk20a_instobj_dtor_dma(struct gk20a_instobj_priv *_node) +{ + struct gk20a_instobj_dma *node = (void *)_node; + struct gk20a_instmem_priv *priv = (void *)nvkm_instmem(node); + struct device *dev = nv_device_base(nv_device(priv)); + + if (unlikely(!node->cpuaddr)) + return; + + dma_free_attrs(dev, _node->mem->size << PAGE_SHIFT, node->cpuaddr, + node->handle, &priv->attrs); +} + +static void +gk20a_instobj_dtor_iommu(struct gk20a_instobj_priv *_node) +{ + struct gk20a_instobj_iommu *node = (void *)_node; + struct gk20a_instmem_priv *priv = (void *)nvkm_instmem(node); + struct nvkm_mm_node *r; + int i; + + if (unlikely(list_empty(&_node->mem->regions))) + return; + + r = list_first_entry(&_node->mem->regions, struct nvkm_mm_node, + rl_entry); + + /* clear bit 34 to unmap pages */ + r->offset &= ~BIT(34 - priv->iommu_pgshift); + + /* Unmap pages from GPU address space and free them */ + for (i = 0; i < _node->mem->size; i++) { + iommu_unmap(priv->domain, + (r->offset + i) << priv->iommu_pgshift, PAGE_SIZE); + __free_page(node->pages[i]); + } + + /* Release area from GPU address space */ + mutex_lock(priv->mm_mutex); + nvkm_mm_free(priv->mm, &r); + mutex_unlock(priv->mm_mutex); +} + +static void +gk20a_instobj_dtor(struct nvkm_object *object) +{ + struct gk20a_instobj_priv *node = (void *)object; + struct gk20a_instmem_priv *priv = (void *)nvkm_instmem(node); + + if (priv->domain) + gk20a_instobj_dtor_iommu(node); + else + gk20a_instobj_dtor_dma(node); + + nvkm_instobj_destroy(&node->base); +} + +static int +gk20a_instobj_ctor_dma(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, u32 npages, u32 align, + struct gk20a_instobj_priv **_node) +{ + struct gk20a_instobj_dma *node; + struct gk20a_instmem_priv *priv = (void *)nvkm_instmem(parent); + struct device *dev = nv_device_base(nv_device(parent)); + int ret; + + ret = nvkm_instobj_create_(parent, engine, oclass, sizeof(*node), + (void **)&node); + *_node = &node->base; + if (ret) + return ret; + + node->cpuaddr = dma_alloc_attrs(dev, npages << PAGE_SHIFT, + &node->handle, GFP_KERNEL, + &priv->attrs); + if (!node->cpuaddr) { + nv_error(priv, "cannot allocate DMA memory\n"); + return -ENOMEM; + } + + /* alignment check */ + if (unlikely(node->handle & (align - 1))) + nv_warn(priv, "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._mem.offset = node->handle; + + INIT_LIST_HEAD(&node->base._mem.regions); + list_add_tail(&node->r.rl_entry, &node->base._mem.regions); + + return 0; +} + +static int +gk20a_instobj_ctor_iommu(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, u32 npages, u32 align, + struct gk20a_instobj_priv **_node) +{ + struct gk20a_instobj_iommu *node; + struct gk20a_instmem_priv *priv = (void *)nvkm_instmem(parent); + struct nvkm_mm_node *r; + int ret; + int i; + + ret = nvkm_instobj_create_(parent, engine, oclass, + sizeof(*node) + sizeof(node->pages[0]) * npages, + (void **)&node); + *_node = &node->base; + if (ret) + return ret; + + /* Allocate backing memory */ + for (i = 0; i < npages; i++) { + struct page *p = alloc_page(GFP_KERNEL); + + if (p == NULL) { + ret = -ENOMEM; + goto free_pages; + } + node->pages[i] = p; + } + + mutex_lock(priv->mm_mutex); + /* Reserve area from GPU address space */ + ret = nvkm_mm_head(priv->mm, 0, 1, npages, npages, + align >> priv->iommu_pgshift, &r); + mutex_unlock(priv->mm_mutex); + if (ret) { + nv_error(priv, "virtual space is full!\n"); + goto free_pages; + } + + /* Map into GPU address space */ + for (i = 0; i < npages; i++) { + struct page *p = node->pages[i]; + u32 offset = (r->offset + i) << priv->iommu_pgshift; + + ret = iommu_map(priv->domain, offset, page_to_phys(p), + PAGE_SIZE, IOMMU_READ | IOMMU_WRITE); + if (ret < 0) { + nv_error(priv, "IOMMU mapping failure: %d\n", ret); + + while (i-- > 0) { + offset -= PAGE_SIZE; + iommu_unmap(priv->domain, offset, PAGE_SIZE); + } + goto release_area; + } + } + + /* Bit 34 tells that an address is to be resolved through the IOMMU */ + r->offset |= BIT(34 - priv->iommu_pgshift); + + node->base._mem.offset = ((u64)r->offset) << priv->iommu_pgshift; + + INIT_LIST_HEAD(&node->base._mem.regions); + list_add_tail(&r->rl_entry, &node->base._mem.regions); + + return 0; + +release_area: + mutex_lock(priv->mm_mutex); + nvkm_mm_free(priv->mm, &r); + mutex_unlock(priv->mm_mutex); + +free_pages: + for (i = 0; i < npages && node->pages[i] != NULL; i++) + __free_page(node->pages[i]); + + return ret; +} + +static int +gk20a_instobj_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 _size, + struct nvkm_object **pobject) +{ + struct nvkm_instobj_args *args = data; + struct gk20a_instmem_priv *priv = (void *)nvkm_instmem(parent); + struct gk20a_instobj_priv *node; + u32 size, align; + int ret; + + nv_debug(parent, "%s (%s): size: %x align: %x\n", __func__, + priv->domain ? "IOMMU" : "DMA", args->size, args->align); + + /* Round size and align to page bounds */ + size = max(roundup(args->size, PAGE_SIZE), PAGE_SIZE); + align = max(roundup(args->align, PAGE_SIZE), PAGE_SIZE); + + if (priv->domain) + ret = gk20a_instobj_ctor_iommu(parent, engine, oclass, + size >> PAGE_SHIFT, align, &node); + else + ret = gk20a_instobj_ctor_dma(parent, engine, oclass, + size >> PAGE_SHIFT, align, &node); + *pobject = nv_object(node); + if (ret) + return ret; + + node->mem = &node->_mem; + + /* present memory for being mapped using small pages */ + node->mem->size = size >> 12; + node->mem->memtype = 0; + node->mem->page_shift = 12; + + node->base.addr = node->mem->offset; + node->base.size = size; + + nv_debug(parent, "alloc size: 0x%x, align: 0x%x, gaddr: 0x%llx\n", + size, align, node->mem->offset); + + return 0; +} + +static struct nvkm_instobj_impl +gk20a_instobj_oclass = { + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gk20a_instobj_ctor, + .dtor = gk20a_instobj_dtor, + .init = _nvkm_instobj_init, + .fini = _nvkm_instobj_fini, + .rd32 = gk20a_instobj_rd32, + .wr32 = gk20a_instobj_wr32, + }, +}; + + + +static int +gk20a_instmem_fini(struct nvkm_object *object, bool suspend) +{ + struct gk20a_instmem_priv *priv = (void *)object; + priv->addr = ~0ULL; + return nvkm_instmem_fini(&priv->base, suspend); +} + +static int +gk20a_instmem_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gk20a_instmem_priv *priv; + struct nouveau_platform_device *plat; + int ret; + + ret = nvkm_instmem_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + spin_lock_init(&priv->lock); + + plat = nv_device_to_platform(nv_device(parent)); + if (plat->gpu->iommu.domain) { + priv->domain = plat->gpu->iommu.domain; + priv->mm = plat->gpu->iommu.mm; + priv->iommu_pgshift = plat->gpu->iommu.pgshift; + priv->mm_mutex = &plat->gpu->iommu.mutex; + + nv_info(priv, "using IOMMU\n"); + } else { + init_dma_attrs(&priv->attrs); + /* + * We will access instmem through PRAMIN and thus do not need a + * consistent CPU pointer or kernel mapping + */ + dma_set_attr(DMA_ATTR_NON_CONSISTENT, &priv->attrs); + dma_set_attr(DMA_ATTR_WEAK_ORDERING, &priv->attrs); + dma_set_attr(DMA_ATTR_WRITE_COMBINE, &priv->attrs); + dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &priv->attrs); + + nv_info(priv, "using DMA API\n"); + } + + return 0; +} + +struct nvkm_oclass * +gk20a_instmem_oclass = &(struct nvkm_instmem_impl) { + .base.handle = NV_SUBDEV(INSTMEM, 0xea), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gk20a_instmem_ctor, + .dtor = _nvkm_instmem_dtor, + .init = _nvkm_instmem_init, + .fini = gk20a_instmem_fini, + }, + .instobj = &gk20a_instobj_oclass.base, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv04.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv04.c new file mode 100644 index 000000000..80614f1b2 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv04.c @@ -0,0 +1,185 @@ +/* + * 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 + +/****************************************************************************** + * instmem object implementation + *****************************************************************************/ + +static u32 +nv04_instobj_rd32(struct nvkm_object *object, u64 addr) +{ + struct nv04_instmem_priv *priv = (void *)nvkm_instmem(object); + struct nv04_instobj_priv *node = (void *)object; + return nv_ro32(priv, node->mem->offset + addr); +} + +static void +nv04_instobj_wr32(struct nvkm_object *object, u64 addr, u32 data) +{ + struct nv04_instmem_priv *priv = (void *)nvkm_instmem(object); + struct nv04_instobj_priv *node = (void *)object; + nv_wo32(priv, node->mem->offset + addr, data); +} + +static void +nv04_instobj_dtor(struct nvkm_object *object) +{ + struct nv04_instmem_priv *priv = (void *)nvkm_instmem(object); + struct nv04_instobj_priv *node = (void *)object; + nvkm_mm_free(&priv->heap, &node->mem); + nvkm_instobj_destroy(&node->base); +} + +static int +nv04_instobj_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv04_instmem_priv *priv = (void *)nvkm_instmem(parent); + struct nv04_instobj_priv *node; + struct nvkm_instobj_args *args = data; + int ret; + + if (!args->align) + args->align = 1; + + ret = nvkm_instobj_create(parent, engine, oclass, &node); + *pobject = nv_object(node); + if (ret) + return ret; + + ret = nvkm_mm_head(&priv->heap, 0, 1, args->size, args->size, + args->align, &node->mem); + if (ret) + return ret; + + node->base.addr = node->mem->offset; + node->base.size = node->mem->length; + return 0; +} + +struct nvkm_instobj_impl +nv04_instobj_oclass = { + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_instobj_ctor, + .dtor = nv04_instobj_dtor, + .init = _nvkm_instobj_init, + .fini = _nvkm_instobj_fini, + .rd32 = nv04_instobj_rd32, + .wr32 = nv04_instobj_wr32, + }, +}; + +/****************************************************************************** + * instmem subdev implementation + *****************************************************************************/ + +static u32 +nv04_instmem_rd32(struct nvkm_object *object, u64 addr) +{ + return nv_rd32(object, 0x700000 + addr); +} + +static void +nv04_instmem_wr32(struct nvkm_object *object, u64 addr, u32 data) +{ + return nv_wr32(object, 0x700000 + addr, data); +} + +void +nv04_instmem_dtor(struct nvkm_object *object) +{ + struct nv04_instmem_priv *priv = (void *)object; + nvkm_gpuobj_ref(NULL, &priv->ramfc); + nvkm_gpuobj_ref(NULL, &priv->ramro); + nvkm_ramht_ref(NULL, &priv->ramht); + nvkm_gpuobj_ref(NULL, &priv->vbios); + nvkm_mm_fini(&priv->heap); + if (priv->iomem) + iounmap(priv->iomem); + nvkm_instmem_destroy(&priv->base); +} + +static int +nv04_instmem_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv04_instmem_priv *priv; + int ret; + + ret = nvkm_instmem_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + /* PRAMIN aperture maps over the end of VRAM, reserve it */ + priv->base.reserved = 512 * 1024; + + ret = nvkm_mm_init(&priv->heap, 0, priv->base.reserved, 1); + if (ret) + return ret; + + /* 0x00000-0x10000: reserve for probable vbios image */ + ret = nvkm_gpuobj_new(nv_object(priv), NULL, 0x10000, 0, 0, + &priv->vbios); + if (ret) + return ret; + + /* 0x10000-0x18000: reserve for RAMHT */ + ret = nvkm_ramht_new(nv_object(priv), NULL, 0x08000, 0, &priv->ramht); + if (ret) + return ret; + + /* 0x18000-0x18800: reserve for RAMFC (enough for 32 nv30 channels) */ + ret = nvkm_gpuobj_new(nv_object(priv), NULL, 0x00800, 0, + NVOBJ_FLAG_ZERO_ALLOC, &priv->ramfc); + if (ret) + return ret; + + /* 0x18800-0x18a00: reserve for RAMRO */ + ret = nvkm_gpuobj_new(nv_object(priv), NULL, 0x00200, 0, 0, + &priv->ramro); + if (ret) + return ret; + + return 0; +} + +struct nvkm_oclass * +nv04_instmem_oclass = &(struct nvkm_instmem_impl) { + .base.handle = NV_SUBDEV(INSTMEM, 0x04), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_instmem_ctor, + .dtor = nv04_instmem_dtor, + .init = _nvkm_instmem_init, + .fini = _nvkm_instmem_fini, + .rd32 = nv04_instmem_rd32, + .wr32 = nv04_instmem_wr32, + }, + .instobj = &nv04_instobj_oclass.base, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv04.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv04.h new file mode 100644 index 000000000..42b6c9280 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv04.h @@ -0,0 +1,36 @@ +#ifndef __NV04_INSTMEM_H__ +#define __NV04_INSTMEM_H__ +#include "priv.h" + +#include + +extern struct nvkm_instobj_impl nv04_instobj_oclass; + +struct nv04_instmem_priv { + struct nvkm_instmem base; + + void __iomem *iomem; + struct nvkm_mm heap; + + struct nvkm_gpuobj *vbios; + struct nvkm_ramht *ramht; + struct nvkm_gpuobj *ramro; + struct nvkm_gpuobj *ramfc; +}; + +static inline struct nv04_instmem_priv * +nv04_instmem(void *obj) +{ + return (void *)nvkm_instmem(obj); +} + +struct nv04_instobj_priv { + struct nvkm_instobj base; + struct nvkm_mm_node *mem; +}; + +void nv04_instmem_dtor(struct nvkm_object *); + +int nv04_instmem_alloc(struct nvkm_instmem *, struct nvkm_object *, + u32 size, u32 align, struct nvkm_object **pobject); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv40.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv40.c new file mode 100644 index 000000000..b42b8588f --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv40.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 "nv04.h" + +#include +#include + +/****************************************************************************** + * instmem subdev implementation + *****************************************************************************/ + +static u32 +nv40_instmem_rd32(struct nvkm_object *object, u64 addr) +{ + struct nv04_instmem_priv *priv = (void *)object; + return ioread32_native(priv->iomem + addr); +} + +static void +nv40_instmem_wr32(struct nvkm_object *object, u64 addr, u32 data) +{ + struct nv04_instmem_priv *priv = (void *)object; + iowrite32_native(data, priv->iomem + addr); +} + +static int +nv40_instmem_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_device *device = nv_device(parent); + struct nv04_instmem_priv *priv; + int ret, bar, vs; + + ret = nvkm_instmem_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + /* map bar */ + if (nv_device_resource_len(device, 2)) + bar = 2; + else + bar = 3; + + priv->iomem = ioremap(nv_device_resource_start(device, bar), + nv_device_resource_len(device, bar)); + if (!priv->iomem) { + nv_error(priv, "unable to map PRAMIN BAR\n"); + return -EFAULT; + } + + /* 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((nv_rd32(priv, 0x001540) & 0x0000ff00) >> 8); + if (device->chipset == 0x40) priv->base.reserved = 0x6aa0 * vs; + else if (device->chipset < 0x43) priv->base.reserved = 0x4f00 * vs; + else if (nv44_gr_class(priv)) priv->base.reserved = 0x4980 * vs; + else priv->base.reserved = 0x4a40 * vs; + priv->base.reserved += 16 * 1024; + priv->base.reserved *= 32; /* per-channel */ + priv->base.reserved += 512 * 1024; /* pci(e)gart table */ + priv->base.reserved += 512 * 1024; /* object storage */ + + priv->base.reserved = round_up(priv->base.reserved, 4096); + + ret = nvkm_mm_init(&priv->heap, 0, priv->base.reserved, 1); + if (ret) + return ret; + + /* 0x00000-0x10000: reserve for probable vbios image */ + ret = nvkm_gpuobj_new(nv_object(priv), NULL, 0x10000, 0, 0, + &priv->vbios); + if (ret) + return ret; + + /* 0x10000-0x18000: reserve for RAMHT */ + ret = nvkm_ramht_new(nv_object(priv), NULL, 0x08000, 0, &priv->ramht); + if (ret) + return ret; + + /* 0x18000-0x18200: reserve for RAMRO + * 0x18200-0x20000: padding + */ + ret = nvkm_gpuobj_new(nv_object(priv), NULL, 0x08000, 0, 0, + &priv->ramro); + if (ret) + return ret; + + /* 0x20000-0x21000: reserve for RAMFC + * 0x21000-0x40000: padding and some unknown crap + */ + ret = nvkm_gpuobj_new(nv_object(priv), NULL, 0x20000, 0, + NVOBJ_FLAG_ZERO_ALLOC, &priv->ramfc); + if (ret) + return ret; + + return 0; +} + +struct nvkm_oclass * +nv40_instmem_oclass = &(struct nvkm_instmem_impl) { + .base.handle = NV_SUBDEV(INSTMEM, 0x40), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv40_instmem_ctor, + .dtor = nv04_instmem_dtor, + .init = _nvkm_instmem_init, + .fini = _nvkm_instmem_fini, + .rd32 = nv40_instmem_rd32, + .wr32 = nv40_instmem_wr32, + }, + .instobj = &nv04_instobj_oclass.base, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c new file mode 100644 index 000000000..8404143f9 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c @@ -0,0 +1,169 @@ +/* + * 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 + +struct nv50_instmem_priv { + struct nvkm_instmem base; + spinlock_t lock; + u64 addr; +}; + +struct nv50_instobj_priv { + struct nvkm_instobj base; + struct nvkm_mem *mem; +}; + +/****************************************************************************** + * instmem object implementation + *****************************************************************************/ + +static u32 +nv50_instobj_rd32(struct nvkm_object *object, u64 offset) +{ + struct nv50_instmem_priv *priv = (void *)nvkm_instmem(object); + struct nv50_instobj_priv *node = (void *)object; + unsigned long flags; + u64 base = (node->mem->offset + offset) & 0xffffff00000ULL; + u64 addr = (node->mem->offset + offset) & 0x000000fffffULL; + u32 data; + + spin_lock_irqsave(&priv->lock, flags); + if (unlikely(priv->addr != base)) { + nv_wr32(priv, 0x001700, base >> 16); + priv->addr = base; + } + data = nv_rd32(priv, 0x700000 + addr); + spin_unlock_irqrestore(&priv->lock, flags); + return data; +} + +static void +nv50_instobj_wr32(struct nvkm_object *object, u64 offset, u32 data) +{ + struct nv50_instmem_priv *priv = (void *)nvkm_instmem(object); + struct nv50_instobj_priv *node = (void *)object; + unsigned long flags; + u64 base = (node->mem->offset + offset) & 0xffffff00000ULL; + u64 addr = (node->mem->offset + offset) & 0x000000fffffULL; + + spin_lock_irqsave(&priv->lock, flags); + if (unlikely(priv->addr != base)) { + nv_wr32(priv, 0x001700, base >> 16); + priv->addr = base; + } + nv_wr32(priv, 0x700000 + addr, data); + spin_unlock_irqrestore(&priv->lock, flags); +} + +static void +nv50_instobj_dtor(struct nvkm_object *object) +{ + struct nv50_instobj_priv *node = (void *)object; + struct nvkm_fb *pfb = nvkm_fb(object); + pfb->ram->put(pfb, &node->mem); + nvkm_instobj_destroy(&node->base); +} + +static int +nv50_instobj_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_fb *pfb = nvkm_fb(parent); + struct nvkm_instobj_args *args = data; + struct nv50_instobj_priv *node; + int ret; + + args->size = max((args->size + 4095) & ~4095, (u32)4096); + args->align = max((args->align + 4095) & ~4095, (u32)4096); + + ret = nvkm_instobj_create(parent, engine, oclass, &node); + *pobject = nv_object(node); + if (ret) + return ret; + + ret = pfb->ram->get(pfb, args->size, args->align, 0, 0x800, &node->mem); + if (ret) + return ret; + + node->base.addr = node->mem->offset; + node->base.size = node->mem->size << 12; + node->mem->page_shift = 12; + return 0; +} + +static struct nvkm_instobj_impl +nv50_instobj_oclass = { + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_instobj_ctor, + .dtor = nv50_instobj_dtor, + .init = _nvkm_instobj_init, + .fini = _nvkm_instobj_fini, + .rd32 = nv50_instobj_rd32, + .wr32 = nv50_instobj_wr32, + }, +}; + +/****************************************************************************** + * instmem subdev implementation + *****************************************************************************/ + +static int +nv50_instmem_fini(struct nvkm_object *object, bool suspend) +{ + struct nv50_instmem_priv *priv = (void *)object; + priv->addr = ~0ULL; + return nvkm_instmem_fini(&priv->base, suspend); +} + +static int +nv50_instmem_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv50_instmem_priv *priv; + int ret; + + ret = nvkm_instmem_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + spin_lock_init(&priv->lock); + return 0; +} + +struct nvkm_oclass * +nv50_instmem_oclass = &(struct nvkm_instmem_impl) { + .base.handle = NV_SUBDEV(INSTMEM, 0x50), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_instmem_ctor, + .dtor = _nvkm_instmem_dtor, + .init = _nvkm_instmem_init, + .fini = nv50_instmem_fini, + }, + .instobj = &nv50_instobj_oclass.base, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/priv.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/priv.h new file mode 100644 index 000000000..b10e292e5 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/priv.h @@ -0,0 +1,54 @@ +#ifndef __NVKM_INSTMEM_PRIV_H__ +#define __NVKM_INSTMEM_PRIV_H__ +#include + +struct nvkm_instobj_impl { + struct nvkm_oclass base; +}; + +struct nvkm_instobj_args { + u32 size; + u32 align; +}; + +#define nvkm_instobj_create(p,e,o,d) \ + nvkm_instobj_create_((p), (e), (o), sizeof(**d), (void **)d) +#define nvkm_instobj_destroy(p) ({ \ + struct nvkm_instobj *iobj = (p); \ + _nvkm_instobj_dtor(nv_object(iobj)); \ +}) +#define nvkm_instobj_init(p) \ + nvkm_object_init(&(p)->base) +#define nvkm_instobj_fini(p,s) \ + nvkm_object_fini(&(p)->base, (s)) + +int nvkm_instobj_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, int, void **); +void _nvkm_instobj_dtor(struct nvkm_object *); +#define _nvkm_instobj_init nvkm_object_init +#define _nvkm_instobj_fini nvkm_object_fini + +struct nvkm_instmem_impl { + struct nvkm_oclass base; + struct nvkm_oclass *instobj; +}; + +#define nvkm_instmem_create(p,e,o,d) \ + nvkm_instmem_create_((p), (e), (o), sizeof(**d), (void **)d) +#define nvkm_instmem_destroy(p) \ + nvkm_subdev_destroy(&(p)->base) +#define nvkm_instmem_init(p) ({ \ + struct nvkm_instmem *imem = (p); \ + _nvkm_instmem_init(nv_object(imem)); \ +}) +#define nvkm_instmem_fini(p,s) ({ \ + struct nvkm_instmem *imem = (p); \ + _nvkm_instmem_fini(nv_object(imem), (s)); \ +}) + +int nvkm_instmem_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, int, void **); +#define _nvkm_instmem_dtor _nvkm_subdev_dtor +int _nvkm_instmem_init(struct nvkm_object *); +int _nvkm_instmem_fini(struct nvkm_object *, bool); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/Kbuild new file mode 100644 index 000000000..e5df3d865 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/Kbuild @@ -0,0 +1,4 @@ +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 diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c new file mode 100644 index 000000000..2fb87fbfd --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c @@ -0,0 +1,124 @@ +/* + * 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" + +static int +nvkm_ltc_tags_alloc(struct nvkm_ltc *ltc, u32 n, struct nvkm_mm_node **pnode) +{ + struct nvkm_ltc_priv *priv = (void *)ltc; + int ret; + + ret = nvkm_mm_head(&priv->tags, 0, 1, n, n, 1, pnode); + if (ret) + *pnode = NULL; + + return ret; +} + +static void +nvkm_ltc_tags_free(struct nvkm_ltc *ltc, struct nvkm_mm_node **pnode) +{ + struct nvkm_ltc_priv *priv = (void *)ltc; + nvkm_mm_free(&priv->tags, pnode); +} + +static void +nvkm_ltc_tags_clear(struct nvkm_ltc *ltc, u32 first, u32 count) +{ + const struct nvkm_ltc_impl *impl = (void *)nv_oclass(ltc); + struct nvkm_ltc_priv *priv = (void *)ltc; + const u32 limit = first + count - 1; + + BUG_ON((first > limit) || (limit >= priv->num_tags)); + + impl->cbc_clear(priv, first, limit); + impl->cbc_wait(priv); +} + +static int +nvkm_ltc_zbc_color_get(struct nvkm_ltc *ltc, int index, const u32 color[4]) +{ + const struct nvkm_ltc_impl *impl = (void *)nv_oclass(ltc); + struct nvkm_ltc_priv *priv = (void *)ltc; + memcpy(priv->zbc_color[index], color, sizeof(priv->zbc_color[index])); + impl->zbc_clear_color(priv, index, color); + return index; +} + +static int +nvkm_ltc_zbc_depth_get(struct nvkm_ltc *ltc, int index, const u32 depth) +{ + const struct nvkm_ltc_impl *impl = (void *)nv_oclass(ltc); + struct nvkm_ltc_priv *priv = (void *)ltc; + priv->zbc_depth[index] = depth; + impl->zbc_clear_depth(priv, index, depth); + return index; +} + +int +_nvkm_ltc_init(struct nvkm_object *object) +{ + const struct nvkm_ltc_impl *impl = (void *)nv_oclass(object); + struct nvkm_ltc_priv *priv = (void *)object; + int ret, i; + + ret = nvkm_subdev_init(&priv->base.base); + if (ret) + return ret; + + for (i = priv->base.zbc_min; i <= priv->base.zbc_max; i++) { + impl->zbc_clear_color(priv, i, priv->zbc_color[i]); + impl->zbc_clear_depth(priv, i, priv->zbc_depth[i]); + } + + return 0; +} + +int +nvkm_ltc_create_(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, int length, void **pobject) +{ + const struct nvkm_ltc_impl *impl = (void *)oclass; + struct nvkm_ltc_priv *priv; + int ret; + + ret = nvkm_subdev_create_(parent, engine, oclass, 0, "PLTCG", + "l2c", length, pobject); + priv = *pobject; + if (ret) + return ret; + + memset(priv->zbc_color, 0x00, sizeof(priv->zbc_color)); + memset(priv->zbc_depth, 0x00, sizeof(priv->zbc_depth)); + + priv->base.base.intr = impl->intr; + priv->base.tags_alloc = nvkm_ltc_tags_alloc; + priv->base.tags_free = nvkm_ltc_tags_free; + priv->base.tags_clear = nvkm_ltc_tags_clear; + priv->base.zbc_min = 1; /* reserve 0 for disabled */ + priv->base.zbc_max = min(impl->zbc, NVKM_LTC_MAX_ZBC_CNT) - 1; + priv->base.zbc_color_get = nvkm_ltc_zbc_color_get; + priv->base.zbc_depth_get = nvkm_ltc_zbc_depth_get; + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gf100.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gf100.c new file mode 100644 index 000000000..7fb5ea031 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gf100.c @@ -0,0 +1,244 @@ +/* + * 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_priv *priv, u32 start, u32 limit) +{ + nv_wr32(priv, 0x17e8cc, start); + nv_wr32(priv, 0x17e8d0, limit); + nv_wr32(priv, 0x17e8c8, 0x00000004); +} + +void +gf100_ltc_cbc_wait(struct nvkm_ltc_priv *priv) +{ + int c, s; + for (c = 0; c < priv->ltc_nr; c++) { + for (s = 0; s < priv->lts_nr; s++) + nv_wait(priv, 0x1410c8 + c * 0x2000 + s * 0x400, ~0, 0); + } +} + +void +gf100_ltc_zbc_clear_color(struct nvkm_ltc_priv *priv, int i, const u32 color[4]) +{ + nv_mask(priv, 0x17ea44, 0x0000000f, i); + nv_wr32(priv, 0x17ea48, color[0]); + nv_wr32(priv, 0x17ea4c, color[1]); + nv_wr32(priv, 0x17ea50, color[2]); + nv_wr32(priv, 0x17ea54, color[3]); +} + +void +gf100_ltc_zbc_clear_depth(struct nvkm_ltc_priv *priv, int i, const u32 depth) +{ + nv_mask(priv, 0x17ea44, 0x0000000f, i); + nv_wr32(priv, 0x17ea58, depth); +} + +static 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_priv *priv, int ltc, int lts) +{ + u32 base = 0x141000 + (ltc * 0x2000) + (lts * 0x400); + u32 intr = nv_rd32(priv, base + 0x020); + u32 stat = intr & 0x0000ffff; + + if (stat) { + nv_info(priv, "LTC%d_LTS%d:", ltc, lts); + nvkm_bitfield_print(gf100_ltc_lts_intr_name, stat); + pr_cont("\n"); + } + + nv_wr32(priv, base + 0x020, intr); +} + +void +gf100_ltc_intr(struct nvkm_subdev *subdev) +{ + struct nvkm_ltc_priv *priv = (void *)subdev; + u32 mask; + + mask = nv_rd32(priv, 0x00017c); + while (mask) { + u32 lts, ltc = __ffs(mask); + for (lts = 0; lts < priv->lts_nr; lts++) + gf100_ltc_lts_intr(priv, ltc, lts); + mask &= ~(1 << ltc); + } +} + +static int +gf100_ltc_init(struct nvkm_object *object) +{ + struct nvkm_ltc_priv *priv = (void *)object; + u32 lpg128 = !(nv_rd32(priv, 0x100c80) & 0x00000001); + int ret; + + ret = nvkm_ltc_init(priv); + if (ret) + return ret; + + nv_mask(priv, 0x17e820, 0x00100000, 0x00000000); /* INTR_EN &= ~0x10 */ + nv_wr32(priv, 0x17e8d8, priv->ltc_nr); + nv_wr32(priv, 0x17e8d4, priv->tag_base); + nv_mask(priv, 0x17e8c0, 0x00000002, lpg128 ? 0x00000002 : 0x00000000); + return 0; +} + +void +gf100_ltc_dtor(struct nvkm_object *object) +{ + struct nvkm_fb *pfb = nvkm_fb(object); + struct nvkm_ltc_priv *priv = (void *)object; + + nvkm_mm_fini(&priv->tags); + if (pfb->ram) + nvkm_mm_free(&pfb->vram, &priv->tag_ram); + + nvkm_ltc_destroy(priv); +} + +/* TODO: Figure out tag memory details and drop the over-cautious allocation. + */ +int +gf100_ltc_init_tag_ram(struct nvkm_fb *pfb, struct nvkm_ltc_priv *priv) +{ + u32 tag_size, tag_margin, tag_align; + int ret; + + /* No VRAM, no tags for now. */ + if (!pfb->ram) { + priv->num_tags = 0; + goto mm_init; + } + + /* tags for 1/4 of VRAM should be enough (8192/4 per GiB of VRAM) */ + priv->num_tags = (pfb->ram->size >> 17) / 4; + if (priv->num_tags > (1 << 17)) + priv->num_tags = 1 << 17; /* we have 17 bits in PTE */ + priv->num_tags = (priv->num_tags + 63) & ~63; /* round up to 64 */ + + tag_align = priv->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 = (priv->num_tags / 64) * 0x6000 + tag_margin; + tag_size += tag_align; + tag_size = (tag_size + 0xfff) >> 12; /* round up */ + + ret = nvkm_mm_tail(&pfb->vram, 1, 1, tag_size, tag_size, 1, + &priv->tag_ram); + if (ret) { + priv->num_tags = 0; + } else { + u64 tag_base = ((u64)priv->tag_ram->offset << 12) + tag_margin; + + tag_base += tag_align - 1; + ret = do_div(tag_base, tag_align); + + priv->tag_base = tag_base; + } + +mm_init: + ret = nvkm_mm_init(&priv->tags, 0, priv->num_tags, 1); + return ret; +} + +int +gf100_ltc_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_fb *pfb = nvkm_fb(parent); + struct nvkm_ltc_priv *priv; + u32 parts, mask; + int ret, i; + + ret = nvkm_ltc_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + parts = nv_rd32(priv, 0x022438); + mask = nv_rd32(priv, 0x022554); + for (i = 0; i < parts; i++) { + if (!(mask & (1 << i))) + priv->ltc_nr++; + } + priv->lts_nr = nv_rd32(priv, 0x17e8dc) >> 28; + + ret = gf100_ltc_init_tag_ram(pfb, priv); + if (ret) + return ret; + + nv_subdev(priv)->intr = gf100_ltc_intr; + return 0; +} + +struct nvkm_oclass * +gf100_ltc_oclass = &(struct nvkm_ltc_impl) { + .base.handle = NV_SUBDEV(LTC, 0xc0), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_ltc_ctor, + .dtor = gf100_ltc_dtor, + .init = gf100_ltc_init, + .fini = _nvkm_ltc_fini, + }, + .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, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gk104.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gk104.c new file mode 100644 index 000000000..d53959b5e --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gk104.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" + +static int +gk104_ltc_init(struct nvkm_object *object) +{ + struct nvkm_ltc_priv *priv = (void *)object; + u32 lpg128 = !(nv_rd32(priv, 0x100c80) & 0x00000001); + int ret; + + ret = nvkm_ltc_init(priv); + if (ret) + return ret; + + nv_wr32(priv, 0x17e8d8, priv->ltc_nr); + nv_wr32(priv, 0x17e000, priv->ltc_nr); + nv_wr32(priv, 0x17e8d4, priv->tag_base); + nv_mask(priv, 0x17e8c0, 0x00000002, lpg128 ? 0x00000002 : 0x00000000); + return 0; +} + +struct nvkm_oclass * +gk104_ltc_oclass = &(struct nvkm_ltc_impl) { + .base.handle = NV_SUBDEV(LTC, 0xe4), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_ltc_ctor, + .dtor = gf100_ltc_dtor, + .init = gk104_ltc_init, + .fini = _nvkm_ltc_fini, + }, + .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, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gm107.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gm107.c new file mode 100644 index 000000000..6b3f6f4ce --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gm107.c @@ -0,0 +1,153 @@ +/* + * 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 + +static void +gm107_ltc_cbc_clear(struct nvkm_ltc_priv *priv, u32 start, u32 limit) +{ + nv_wr32(priv, 0x17e270, start); + nv_wr32(priv, 0x17e274, limit); + nv_wr32(priv, 0x17e26c, 0x00000004); +} + +static void +gm107_ltc_cbc_wait(struct nvkm_ltc_priv *priv) +{ + int c, s; + for (c = 0; c < priv->ltc_nr; c++) { + for (s = 0; s < priv->lts_nr; s++) + nv_wait(priv, 0x14046c + c * 0x2000 + s * 0x200, ~0, 0); + } +} + +static void +gm107_ltc_zbc_clear_color(struct nvkm_ltc_priv *priv, int i, const u32 color[4]) +{ + nv_mask(priv, 0x17e338, 0x0000000f, i); + nv_wr32(priv, 0x17e33c, color[0]); + nv_wr32(priv, 0x17e340, color[1]); + nv_wr32(priv, 0x17e344, color[2]); + nv_wr32(priv, 0x17e348, color[3]); +} + +static void +gm107_ltc_zbc_clear_depth(struct nvkm_ltc_priv *priv, int i, const u32 depth) +{ + nv_mask(priv, 0x17e338, 0x0000000f, i); + nv_wr32(priv, 0x17e34c, depth); +} + +static void +gm107_ltc_lts_isr(struct nvkm_ltc_priv *priv, int ltc, int lts) +{ + u32 base = 0x140000 + (ltc * 0x2000) + (lts * 0x400); + u32 stat = nv_rd32(priv, base + 0x00c); + + if (stat) { + nv_info(priv, "LTC%d_LTS%d: 0x%08x\n", ltc, lts, stat); + nv_wr32(priv, base + 0x00c, stat); + } +} + +static void +gm107_ltc_intr(struct nvkm_subdev *subdev) +{ + struct nvkm_ltc_priv *priv = (void *)subdev; + u32 mask; + + mask = nv_rd32(priv, 0x00017c); + while (mask) { + u32 lts, ltc = __ffs(mask); + for (lts = 0; lts < priv->lts_nr; lts++) + gm107_ltc_lts_isr(priv, ltc, lts); + mask &= ~(1 << ltc); + } +} + +static int +gm107_ltc_init(struct nvkm_object *object) +{ + struct nvkm_ltc_priv *priv = (void *)object; + u32 lpg128 = !(nv_rd32(priv, 0x100c80) & 0x00000001); + int ret; + + ret = nvkm_ltc_init(priv); + if (ret) + return ret; + + nv_wr32(priv, 0x17e27c, priv->ltc_nr); + nv_wr32(priv, 0x17e278, priv->tag_base); + nv_mask(priv, 0x17e264, 0x00000002, lpg128 ? 0x00000002 : 0x00000000); + return 0; +} + +static int +gm107_ltc_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_fb *pfb = nvkm_fb(parent); + struct nvkm_ltc_priv *priv; + u32 parts, mask; + int ret, i; + + ret = nvkm_ltc_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + parts = nv_rd32(priv, 0x022438); + mask = nv_rd32(priv, 0x021c14); + for (i = 0; i < parts; i++) { + if (!(mask & (1 << i))) + priv->ltc_nr++; + } + priv->lts_nr = nv_rd32(priv, 0x17e280) >> 28; + + ret = gf100_ltc_init_tag_ram(pfb, priv); + if (ret) + return ret; + + return 0; +} + +struct nvkm_oclass * +gm107_ltc_oclass = &(struct nvkm_ltc_impl) { + .base.handle = NV_SUBDEV(LTC, 0xff), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gm107_ltc_ctor, + .dtor = gf100_ltc_dtor, + .init = gm107_ltc_init, + .fini = _nvkm_ltc_fini, + }, + .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, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/priv.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/priv.h new file mode 100644 index 000000000..09537d7b6 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/priv.h @@ -0,0 +1,69 @@ +#ifndef __NVKM_LTC_PRIV_H__ +#define __NVKM_LTC_PRIV_H__ +#include + +#include +struct nvkm_fb; + +struct nvkm_ltc_priv { + struct nvkm_ltc base; + u32 ltc_nr; + u32 lts_nr; + + u32 num_tags; + u32 tag_base; + struct nvkm_mm tags; + struct nvkm_mm_node *tag_ram; + + u32 zbc_color[NVKM_LTC_MAX_ZBC_CNT][4]; + u32 zbc_depth[NVKM_LTC_MAX_ZBC_CNT]; +}; + +#define nvkm_ltc_create(p,e,o,d) \ + nvkm_ltc_create_((p), (e), (o), sizeof(**d), (void **)d) +#define nvkm_ltc_destroy(p) ({ \ + struct nvkm_ltc_priv *_priv = (p); \ + _nvkm_ltc_dtor(nv_object(_priv)); \ +}) +#define nvkm_ltc_init(p) ({ \ + struct nvkm_ltc_priv *_priv = (p); \ + _nvkm_ltc_init(nv_object(_priv)); \ +}) +#define nvkm_ltc_fini(p,s) ({ \ + struct nvkm_ltc_priv *_priv = (p); \ + _nvkm_ltc_fini(nv_object(_priv), (s)); \ +}) + +int nvkm_ltc_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, int, void **); + +#define _nvkm_ltc_dtor _nvkm_subdev_dtor +int _nvkm_ltc_init(struct nvkm_object *); +#define _nvkm_ltc_fini _nvkm_subdev_fini + +int gf100_ltc_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); +void gf100_ltc_dtor(struct nvkm_object *); +int gf100_ltc_init_tag_ram(struct nvkm_fb *, struct nvkm_ltc_priv *); +int gf100_ltc_tags_alloc(struct nvkm_ltc *, u32, struct nvkm_mm_node **); +void gf100_ltc_tags_free(struct nvkm_ltc *, struct nvkm_mm_node **); + +struct nvkm_ltc_impl { + struct nvkm_oclass base; + void (*intr)(struct nvkm_subdev *); + + void (*cbc_clear)(struct nvkm_ltc_priv *, u32 start, u32 limit); + void (*cbc_wait)(struct nvkm_ltc_priv *); + + int zbc; + void (*zbc_clear_color)(struct nvkm_ltc_priv *, int, const u32[4]); + void (*zbc_clear_depth)(struct nvkm_ltc_priv *, int, const u32); +}; + +void gf100_ltc_intr(struct nvkm_subdev *); +void gf100_ltc_cbc_clear(struct nvkm_ltc_priv *, u32, u32); +void gf100_ltc_cbc_wait(struct nvkm_ltc_priv *); +void gf100_ltc_zbc_clear_color(struct nvkm_ltc_priv *, int, const u32[4]); +void gf100_ltc_zbc_clear_depth(struct nvkm_ltc_priv *, int, const u32); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/Kbuild new file mode 100644 index 000000000..721643f04 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/Kbuild @@ -0,0 +1,11 @@ +nvkm-y += nvkm/subdev/mc/base.o +nvkm-y += nvkm/subdev/mc/nv04.o +nvkm-y += nvkm/subdev/mc/nv40.o +nvkm-y += nvkm/subdev/mc/nv44.o +nvkm-y += nvkm/subdev/mc/nv4c.o +nvkm-y += nvkm/subdev/mc/nv50.o +nvkm-y += nvkm/subdev/mc/g94.o +nvkm-y += nvkm/subdev/mc/g98.o +nvkm-y += nvkm/subdev/mc/gf100.o +nvkm-y += nvkm/subdev/mc/gf106.o +nvkm-y += nvkm/subdev/mc/gk20a.o diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/base.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/base.c new file mode 100644 index 000000000..5b051a266 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/base.c @@ -0,0 +1,169 @@ +/* + * 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 inline void +nvkm_mc_unk260(struct nvkm_mc *pmc, u32 data) +{ + const struct nvkm_mc_oclass *impl = (void *)nv_oclass(pmc); + if (impl->unk260) + impl->unk260(pmc, data); +} + +static inline u32 +nvkm_mc_intr_mask(struct nvkm_mc *pmc) +{ + u32 intr = nv_rd32(pmc, 0x000100); + if (intr == 0xffffffff) /* likely fallen off the bus */ + intr = 0x00000000; + return intr; +} + +static irqreturn_t +nvkm_mc_intr(int irq, void *arg) +{ + struct nvkm_mc *pmc = arg; + const struct nvkm_mc_oclass *oclass = (void *)nv_object(pmc)->oclass; + const struct nvkm_mc_intr *map = oclass->intr; + struct nvkm_subdev *unit; + u32 intr; + + nv_wr32(pmc, 0x000140, 0x00000000); + nv_rd32(pmc, 0x000140); + intr = nvkm_mc_intr_mask(pmc); + if (pmc->use_msi) + oclass->msi_rearm(pmc); + + if (intr) { + u32 stat = intr = nvkm_mc_intr_mask(pmc); + while (map->stat) { + if (intr & map->stat) { + unit = nvkm_subdev(pmc, map->unit); + if (unit && unit->intr) + unit->intr(unit); + stat &= ~map->stat; + } + map++; + } + + if (stat) + nv_error(pmc, "unknown intr 0x%08x\n", stat); + } + + nv_wr32(pmc, 0x000140, 0x00000001); + return intr ? IRQ_HANDLED : IRQ_NONE; +} + +int +_nvkm_mc_fini(struct nvkm_object *object, bool suspend) +{ + struct nvkm_mc *pmc = (void *)object; + nv_wr32(pmc, 0x000140, 0x00000000); + return nvkm_subdev_fini(&pmc->base, suspend); +} + +int +_nvkm_mc_init(struct nvkm_object *object) +{ + struct nvkm_mc *pmc = (void *)object; + int ret = nvkm_subdev_init(&pmc->base); + if (ret) + return ret; + nv_wr32(pmc, 0x000140, 0x00000001); + return 0; +} + +void +_nvkm_mc_dtor(struct nvkm_object *object) +{ + struct nvkm_device *device = nv_device(object); + struct nvkm_mc *pmc = (void *)object; + free_irq(pmc->irq, pmc); + if (pmc->use_msi) + pci_disable_msi(device->pdev); + nvkm_subdev_destroy(&pmc->base); +} + +int +nvkm_mc_create_(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *bclass, int length, void **pobject) +{ + const struct nvkm_mc_oclass *oclass = (void *)bclass; + struct nvkm_device *device = nv_device(parent); + struct nvkm_mc *pmc; + int ret; + + ret = nvkm_subdev_create_(parent, engine, bclass, 0, "PMC", + "master", length, pobject); + pmc = *pobject; + if (ret) + return ret; + + pmc->unk260 = nvkm_mc_unk260; + + if (nv_device_is_pci(device)) { + switch (device->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: + pmc->use_msi = true; + break; + } + } + + pmc->use_msi = nvkm_boolopt(device->cfgopt, "NvMSI", + pmc->use_msi); + + if (pmc->use_msi && oclass->msi_rearm) { + pmc->use_msi = pci_enable_msi(device->pdev) == 0; + if (pmc->use_msi) { + nv_info(pmc, "MSI interrupts enabled\n"); + oclass->msi_rearm(pmc); + } + } else { + pmc->use_msi = false; + } + } + + ret = nv_device_get_irq(device, true); + if (ret < 0) + return ret; + pmc->irq = ret; + + ret = request_irq(pmc->irq, nvkm_mc_intr, IRQF_SHARED, "nvkm", pmc); + if (ret < 0) + return ret; + + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/g94.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/g94.c new file mode 100644 index 000000000..f042e7d83 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/g94.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 "nv04.h" + +struct nvkm_oclass * +g94_mc_oclass = &(struct nvkm_mc_oclass) { + .base.handle = NV_SUBDEV(MC, 0x94), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_mc_ctor, + .dtor = _nvkm_mc_dtor, + .init = nv50_mc_init, + .fini = _nvkm_mc_fini, + }, + .intr = nv50_mc_intr, + .msi_rearm = nv40_mc_msi_rearm, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/g98.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/g98.c new file mode 100644 index 000000000..8ab7f1272 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/g98.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 "nv04.h" + +static const struct nvkm_mc_intr +g98_mc_intr[] = { + { 0x04000000, NVDEV_ENGINE_DISP }, /* DISP first, so pageflip timestamps work */ + { 0x00000001, NVDEV_ENGINE_MSPPP }, + { 0x00000100, NVDEV_ENGINE_FIFO }, + { 0x00001000, NVDEV_ENGINE_GR }, + { 0x00004000, NVDEV_ENGINE_SEC }, /* NV84:NVA3 */ + { 0x00008000, NVDEV_ENGINE_MSVLD }, + { 0x00020000, NVDEV_ENGINE_MSPDEC }, + { 0x00040000, NVDEV_SUBDEV_PMU }, /* NVA3:NVC0 */ + { 0x00080000, NVDEV_SUBDEV_THERM }, /* NVA3:NVC0 */ + { 0x00100000, NVDEV_SUBDEV_TIMER }, + { 0x00200000, NVDEV_SUBDEV_GPIO }, /* PMGR->GPIO */ + { 0x00200000, NVDEV_SUBDEV_I2C }, /* PMGR->I2C/AUX */ + { 0x00400000, NVDEV_ENGINE_CE0 }, /* NVA3- */ + { 0x10000000, NVDEV_SUBDEV_BUS }, + { 0x80000000, NVDEV_ENGINE_SW }, + { 0x0042d101, NVDEV_SUBDEV_FB }, + {}, +}; + +struct nvkm_oclass * +g98_mc_oclass = &(struct nvkm_mc_oclass) { + .base.handle = NV_SUBDEV(MC, 0x98), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_mc_ctor, + .dtor = _nvkm_mc_dtor, + .init = nv50_mc_init, + .fini = _nvkm_mc_fini, + }, + .intr = g98_mc_intr, + .msi_rearm = nv40_mc_msi_rearm, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gf100.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gf100.c new file mode 100644 index 000000000..2425984b0 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gf100.c @@ -0,0 +1,76 @@ +/* + * 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" + +const struct nvkm_mc_intr +gf100_mc_intr[] = { + { 0x04000000, NVDEV_ENGINE_DISP }, /* DISP first, so pageflip timestamps work. */ + { 0x00000001, NVDEV_ENGINE_MSPPP }, + { 0x00000020, NVDEV_ENGINE_CE0 }, + { 0x00000040, NVDEV_ENGINE_CE1 }, + { 0x00000080, NVDEV_ENGINE_CE2 }, + { 0x00000100, NVDEV_ENGINE_FIFO }, + { 0x00001000, NVDEV_ENGINE_GR }, + { 0x00002000, NVDEV_SUBDEV_FB }, + { 0x00008000, NVDEV_ENGINE_MSVLD }, + { 0x00040000, NVDEV_SUBDEV_THERM }, + { 0x00020000, NVDEV_ENGINE_MSPDEC }, + { 0x00100000, NVDEV_SUBDEV_TIMER }, + { 0x00200000, NVDEV_SUBDEV_GPIO }, /* PMGR->GPIO */ + { 0x00200000, NVDEV_SUBDEV_I2C }, /* PMGR->I2C/AUX */ + { 0x01000000, NVDEV_SUBDEV_PMU }, + { 0x02000000, NVDEV_SUBDEV_LTC }, + { 0x08000000, NVDEV_SUBDEV_FB }, + { 0x10000000, NVDEV_SUBDEV_BUS }, + { 0x40000000, NVDEV_SUBDEV_IBUS }, + { 0x80000000, NVDEV_ENGINE_SW }, + {}, +}; + +static void +gf100_mc_msi_rearm(struct nvkm_mc *pmc) +{ + struct nv04_mc_priv *priv = (void *)pmc; + nv_wr32(priv, 0x088704, 0x00000000); +} + +void +gf100_mc_unk260(struct nvkm_mc *pmc, u32 data) +{ + nv_wr32(pmc, 0x000260, data); +} + +struct nvkm_oclass * +gf100_mc_oclass = &(struct nvkm_mc_oclass) { + .base.handle = NV_SUBDEV(MC, 0xc0), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_mc_ctor, + .dtor = _nvkm_mc_dtor, + .init = nv50_mc_init, + .fini = _nvkm_mc_fini, + }, + .intr = gf100_mc_intr, + .msi_rearm = gf100_mc_msi_rearm, + .unk260 = gf100_mc_unk260, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gf106.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gf106.c new file mode 100644 index 000000000..8d2a8f457 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gf106.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 "nv04.h" + +struct nvkm_oclass * +gf106_mc_oclass = &(struct nvkm_mc_oclass) { + .base.handle = NV_SUBDEV(MC, 0xc3), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_mc_ctor, + .dtor = _nvkm_mc_dtor, + .init = nv50_mc_init, + .fini = _nvkm_mc_fini, + }, + .intr = gf100_mc_intr, + .msi_rearm = nv40_mc_msi_rearm, + .unk260 = gf100_mc_unk260, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gk20a.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gk20a.c new file mode 100644 index 000000000..43b277429 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gk20a.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 "nv04.h" + +struct nvkm_oclass * +gk20a_mc_oclass = &(struct nvkm_mc_oclass) { + .base.handle = NV_SUBDEV(MC, 0xea), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_mc_ctor, + .dtor = _nvkm_mc_dtor, + .init = nv50_mc_init, + .fini = _nvkm_mc_fini, + }, + .intr = gf100_mc_intr, + .msi_rearm = nv40_mc_msi_rearm, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv04.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv04.c new file mode 100644 index 000000000..32713827b --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv04.c @@ -0,0 +1,78 @@ +/* + * 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" + +const struct nvkm_mc_intr +nv04_mc_intr[] = { + { 0x00000001, NVDEV_ENGINE_MPEG }, /* NV17- MPEG/ME */ + { 0x00000100, NVDEV_ENGINE_FIFO }, + { 0x00001000, NVDEV_ENGINE_GR }, + { 0x00010000, NVDEV_ENGINE_DISP }, + { 0x00020000, NVDEV_ENGINE_VP }, /* NV40- */ + { 0x00100000, NVDEV_SUBDEV_TIMER }, + { 0x01000000, NVDEV_ENGINE_DISP }, /* NV04- PCRTC0 */ + { 0x02000000, NVDEV_ENGINE_DISP }, /* NV11- PCRTC1 */ + { 0x10000000, NVDEV_SUBDEV_BUS }, + { 0x80000000, NVDEV_ENGINE_SW }, + {} +}; + +int +nv04_mc_init(struct nvkm_object *object) +{ + struct nv04_mc_priv *priv = (void *)object; + + nv_wr32(priv, 0x000200, 0xffffffff); /* everything enabled */ + nv_wr32(priv, 0x001850, 0x00000001); /* disable rom access */ + + return nvkm_mc_init(&priv->base); +} + +int +nv04_mc_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv04_mc_priv *priv; + int ret; + + ret = nvkm_mc_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + return 0; +} + +struct nvkm_oclass * +nv04_mc_oclass = &(struct nvkm_mc_oclass) { + .base.handle = NV_SUBDEV(MC, 0x04), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_mc_ctor, + .dtor = _nvkm_mc_dtor, + .init = nv04_mc_init, + .fini = _nvkm_mc_fini, + }, + .intr = nv04_mc_intr, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv04.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv04.h new file mode 100644 index 000000000..411de3d08 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv04.h @@ -0,0 +1,20 @@ +#ifndef __NVKM_MC_NV04_H__ +#define __NVKM_MC_NV04_H__ +#include "priv.h" + +struct nv04_mc_priv { + struct nvkm_mc base; +}; + +int nv04_mc_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); + +extern const struct nvkm_mc_intr nv04_mc_intr[]; +int nv04_mc_init(struct nvkm_object *); +void nv40_mc_msi_rearm(struct nvkm_mc *); +int nv44_mc_init(struct nvkm_object *object); +int nv50_mc_init(struct nvkm_object *); +extern const struct nvkm_mc_intr nv50_mc_intr[]; +extern const struct nvkm_mc_intr gf100_mc_intr[]; +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv40.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv40.c new file mode 100644 index 000000000..b7613059d --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv40.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 "nv04.h" + +void +nv40_mc_msi_rearm(struct nvkm_mc *pmc) +{ + struct nv04_mc_priv *priv = (void *)pmc; + nv_wr08(priv, 0x088068, 0xff); +} + +struct nvkm_oclass * +nv40_mc_oclass = &(struct nvkm_mc_oclass) { + .base.handle = NV_SUBDEV(MC, 0x40), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_mc_ctor, + .dtor = _nvkm_mc_dtor, + .init = nv04_mc_init, + .fini = _nvkm_mc_fini, + }, + .intr = nv04_mc_intr, + .msi_rearm = nv40_mc_msi_rearm, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv44.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv44.c new file mode 100644 index 000000000..2c7f7c701 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv44.c @@ -0,0 +1,53 @@ +/* + * 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" + +int +nv44_mc_init(struct nvkm_object *object) +{ + struct nv04_mc_priv *priv = (void *)object; + u32 tmp = nv_rd32(priv, 0x10020c); + + nv_wr32(priv, 0x000200, 0xffffffff); /* everything enabled */ + + nv_wr32(priv, 0x001700, tmp); + nv_wr32(priv, 0x001704, 0); + nv_wr32(priv, 0x001708, 0); + nv_wr32(priv, 0x00170c, tmp); + + return nvkm_mc_init(&priv->base); +} + +struct nvkm_oclass * +nv44_mc_oclass = &(struct nvkm_mc_oclass) { + .base.handle = NV_SUBDEV(MC, 0x44), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_mc_ctor, + .dtor = _nvkm_mc_dtor, + .init = nv44_mc_init, + .fini = _nvkm_mc_fini, + }, + .intr = nv04_mc_intr, + .msi_rearm = nv40_mc_msi_rearm, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv4c.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv4c.c new file mode 100644 index 000000000..c0aac7e20 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv4c.c @@ -0,0 +1,36 @@ +/* + * Copyright 2014 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 "nv04.h" + +struct nvkm_oclass * +nv4c_mc_oclass = &(struct nvkm_mc_oclass) { + .base.handle = NV_SUBDEV(MC, 0x4c), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_mc_ctor, + .dtor = _nvkm_mc_dtor, + .init = nv44_mc_init, + .fini = _nvkm_mc_fini, + }, + .intr = nv04_mc_intr, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv50.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv50.c new file mode 100644 index 000000000..40e3019e1 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv50.c @@ -0,0 +1,72 @@ +/* + * 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 + +const struct nvkm_mc_intr +nv50_mc_intr[] = { + { 0x04000000, NVDEV_ENGINE_DISP }, /* DISP before FIFO, so pageflip-timestamping works! */ + { 0x00000001, NVDEV_ENGINE_MPEG }, + { 0x00000100, NVDEV_ENGINE_FIFO }, + { 0x00001000, NVDEV_ENGINE_GR }, + { 0x00004000, NVDEV_ENGINE_CIPHER }, /* NV84- */ + { 0x00008000, NVDEV_ENGINE_BSP }, /* NV84- */ + { 0x00020000, NVDEV_ENGINE_VP }, /* NV84- */ + { 0x00100000, NVDEV_SUBDEV_TIMER }, + { 0x00200000, NVDEV_SUBDEV_GPIO }, /* PMGR->GPIO */ + { 0x00200000, NVDEV_SUBDEV_I2C }, /* PMGR->I2C/AUX */ + { 0x10000000, NVDEV_SUBDEV_BUS }, + { 0x80000000, NVDEV_ENGINE_SW }, + { 0x0002d101, NVDEV_SUBDEV_FB }, + {}, +}; + +static void +nv50_mc_msi_rearm(struct nvkm_mc *pmc) +{ + struct nvkm_device *device = nv_device(pmc); + pci_write_config_byte(device->pdev, 0x68, 0xff); +} + +int +nv50_mc_init(struct nvkm_object *object) +{ + struct nv04_mc_priv *priv = (void *)object; + nv_wr32(priv, 0x000200, 0xffffffff); /* everything on */ + return nvkm_mc_init(&priv->base); +} + +struct nvkm_oclass * +nv50_mc_oclass = &(struct nvkm_mc_oclass) { + .base.handle = NV_SUBDEV(MC, 0x50), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_mc_ctor, + .dtor = _nvkm_mc_dtor, + .init = nv50_mc_init, + .fini = _nvkm_mc_fini, + }, + .intr = nv50_mc_intr, + .msi_rearm = nv50_mc_msi_rearm, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/priv.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/priv.h new file mode 100644 index 000000000..d2cad07af --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mc/priv.h @@ -0,0 +1,36 @@ +#ifndef __NVKM_MC_PRIV_H__ +#define __NVKM_MC_PRIV_H__ +#include + +#define nvkm_mc_create(p,e,o,d) \ + nvkm_mc_create_((p), (e), (o), sizeof(**d), (void **)d) +#define nvkm_mc_destroy(p) ({ \ + struct nvkm_mc *pmc = (p); _nvkm_mc_dtor(nv_object(pmc)); \ +}) +#define nvkm_mc_init(p) ({ \ + struct nvkm_mc *pmc = (p); _nvkm_mc_init(nv_object(pmc)); \ +}) +#define nvkm_mc_fini(p,s) ({ \ + struct nvkm_mc *pmc = (p); _nvkm_mc_fini(nv_object(pmc), (s)); \ +}) + +int nvkm_mc_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, int, void **); +void _nvkm_mc_dtor(struct nvkm_object *); +int _nvkm_mc_init(struct nvkm_object *); +int _nvkm_mc_fini(struct nvkm_object *, bool); + +struct nvkm_mc_intr { + u32 stat; + u32 unit; +}; + +struct nvkm_mc_oclass { + struct nvkm_oclass base; + const struct nvkm_mc_intr *intr; + void (*msi_rearm)(struct nvkm_mc *); + void (*unk260)(struct nvkm_mc *, u32); +}; + +void gf100_mc_unk260(struct nvkm_mc *, u32); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/Kbuild new file mode 100644 index 000000000..012c9db68 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/Kbuild @@ -0,0 +1,6 @@ +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/gf100.o diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/base.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/base.c new file mode 100644 index 000000000..277b6ec04 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/base.c @@ -0,0 +1,480 @@ +/* + * 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 +#include + +#include + +void +nvkm_vm_map_at(struct nvkm_vma *vma, u64 delta, struct nvkm_mem *node) +{ + struct nvkm_vm *vm = vma->vm; + struct nvkm_mmu *mmu = vm->mmu; + struct nvkm_mm_node *r; + int big = vma->node->type != mmu->spg_shift; + u32 offset = vma->node->offset + (delta >> 12); + u32 bits = vma->node->type - 12; + u32 pde = (offset >> mmu->pgt_bits) - vm->fpde; + u32 pte = (offset & ((1 << mmu->pgt_bits) - 1)) >> bits; + u32 max = 1 << (mmu->pgt_bits - bits); + u32 end, len; + + delta = 0; + list_for_each_entry(r, &node->regions, rl_entry) { + u64 phys = (u64)r->offset << 12; + u32 num = r->length >> bits; + + while (num) { + struct nvkm_gpuobj *pgt = vm->pgt[pde].obj[big]; + + end = (pte + num); + if (unlikely(end >= max)) + end = max; + len = end - pte; + + mmu->map(vma, pgt, node, pte, len, phys, delta); + + num -= len; + pte += len; + if (unlikely(end >= max)) { + phys += len << (bits + 12); + pde++; + pte = 0; + } + + delta += (u64)len << vma->node->type; + } + } + + mmu->flush(vm); +} + +static void +nvkm_vm_map_sg_table(struct nvkm_vma *vma, u64 delta, u64 length, + struct nvkm_mem *mem) +{ + struct nvkm_vm *vm = vma->vm; + struct nvkm_mmu *mmu = vm->mmu; + int big = vma->node->type != mmu->spg_shift; + u32 offset = vma->node->offset + (delta >> 12); + u32 bits = vma->node->type - 12; + u32 num = length >> vma->node->type; + u32 pde = (offset >> mmu->pgt_bits) - vm->fpde; + u32 pte = (offset & ((1 << mmu->pgt_bits) - 1)) >> bits; + u32 max = 1 << (mmu->pgt_bits - bits); + unsigned m, sglen; + u32 end, len; + int i; + struct scatterlist *sg; + + for_each_sg(mem->sg->sgl, sg, mem->sg->nents, i) { + struct nvkm_gpuobj *pgt = vm->pgt[pde].obj[big]; + sglen = sg_dma_len(sg) >> PAGE_SHIFT; + + end = pte + sglen; + if (unlikely(end >= max)) + end = max; + len = end - pte; + + for (m = 0; m < len; m++) { + dma_addr_t addr = sg_dma_address(sg) + (m << PAGE_SHIFT); + + mmu->map_sg(vma, pgt, mem, pte, 1, &addr); + num--; + pte++; + + if (num == 0) + goto finish; + } + if (unlikely(end >= max)) { + pde++; + pte = 0; + } + if (m < sglen) { + for (; m < sglen; m++) { + dma_addr_t addr = sg_dma_address(sg) + (m << PAGE_SHIFT); + + mmu->map_sg(vma, pgt, mem, pte, 1, &addr); + num--; + pte++; + if (num == 0) + goto finish; + } + } + + } +finish: + mmu->flush(vm); +} + +static void +nvkm_vm_map_sg(struct nvkm_vma *vma, u64 delta, u64 length, + struct nvkm_mem *mem) +{ + struct nvkm_vm *vm = vma->vm; + struct nvkm_mmu *mmu = vm->mmu; + dma_addr_t *list = mem->pages; + int big = vma->node->type != mmu->spg_shift; + u32 offset = vma->node->offset + (delta >> 12); + u32 bits = vma->node->type - 12; + u32 num = length >> vma->node->type; + u32 pde = (offset >> mmu->pgt_bits) - vm->fpde; + u32 pte = (offset & ((1 << mmu->pgt_bits) - 1)) >> bits; + u32 max = 1 << (mmu->pgt_bits - bits); + u32 end, len; + + while (num) { + struct nvkm_gpuobj *pgt = vm->pgt[pde].obj[big]; + + end = (pte + num); + if (unlikely(end >= max)) + end = max; + len = end - pte; + + mmu->map_sg(vma, pgt, mem, pte, len, list); + + num -= len; + pte += len; + list += len; + if (unlikely(end >= max)) { + pde++; + pte = 0; + } + } + + mmu->flush(vm); +} + +void +nvkm_vm_map(struct nvkm_vma *vma, struct nvkm_mem *node) +{ + if (node->sg) + nvkm_vm_map_sg_table(vma, 0, node->size << 12, node); + else + if (node->pages) + nvkm_vm_map_sg(vma, 0, node->size << 12, node); + else + nvkm_vm_map_at(vma, 0, node); +} + +void +nvkm_vm_unmap_at(struct nvkm_vma *vma, u64 delta, u64 length) +{ + struct nvkm_vm *vm = vma->vm; + struct nvkm_mmu *mmu = vm->mmu; + int big = vma->node->type != mmu->spg_shift; + u32 offset = vma->node->offset + (delta >> 12); + u32 bits = vma->node->type - 12; + u32 num = length >> vma->node->type; + u32 pde = (offset >> mmu->pgt_bits) - vm->fpde; + u32 pte = (offset & ((1 << mmu->pgt_bits) - 1)) >> bits; + u32 max = 1 << (mmu->pgt_bits - bits); + u32 end, len; + + while (num) { + struct nvkm_gpuobj *pgt = vm->pgt[pde].obj[big]; + + end = (pte + num); + if (unlikely(end >= max)) + end = max; + len = end - pte; + + mmu->unmap(pgt, pte, len); + + num -= len; + pte += len; + if (unlikely(end >= max)) { + pde++; + pte = 0; + } + } + + mmu->flush(vm); +} + +void +nvkm_vm_unmap(struct nvkm_vma *vma) +{ + nvkm_vm_unmap_at(vma, 0, (u64)vma->node->length << 12); +} + +static void +nvkm_vm_unmap_pgt(struct nvkm_vm *vm, int big, u32 fpde, u32 lpde) +{ + struct nvkm_mmu *mmu = vm->mmu; + struct nvkm_vm_pgd *vpgd; + struct nvkm_vm_pgt *vpgt; + struct nvkm_gpuobj *pgt; + u32 pde; + + for (pde = fpde; pde <= lpde; pde++) { + vpgt = &vm->pgt[pde - vm->fpde]; + if (--vpgt->refcount[big]) + continue; + + pgt = vpgt->obj[big]; + vpgt->obj[big] = NULL; + + list_for_each_entry(vpgd, &vm->pgd_list, head) { + mmu->map_pgt(vpgd->obj, pde, vpgt->obj); + } + + mutex_unlock(&nv_subdev(mmu)->mutex); + nvkm_gpuobj_ref(NULL, &pgt); + mutex_lock(&nv_subdev(mmu)->mutex); + } +} + +static int +nvkm_vm_map_pgt(struct nvkm_vm *vm, u32 pde, u32 type) +{ + struct nvkm_mmu *mmu = vm->mmu; + struct nvkm_vm_pgt *vpgt = &vm->pgt[pde - vm->fpde]; + struct nvkm_vm_pgd *vpgd; + struct nvkm_gpuobj *pgt; + int big = (type != mmu->spg_shift); + u32 pgt_size; + int ret; + + pgt_size = (1 << (mmu->pgt_bits + 12)) >> type; + pgt_size *= 8; + + mutex_unlock(&nv_subdev(mmu)->mutex); + ret = nvkm_gpuobj_new(nv_object(vm->mmu), NULL, pgt_size, 0x1000, + NVOBJ_FLAG_ZERO_ALLOC, &pgt); + mutex_lock(&nv_subdev(mmu)->mutex); + if (unlikely(ret)) + return ret; + + /* someone beat us to filling the PDE while we didn't have the lock */ + if (unlikely(vpgt->refcount[big]++)) { + mutex_unlock(&nv_subdev(mmu)->mutex); + nvkm_gpuobj_ref(NULL, &pgt); + mutex_lock(&nv_subdev(mmu)->mutex); + return 0; + } + + vpgt->obj[big] = pgt; + list_for_each_entry(vpgd, &vm->pgd_list, head) { + mmu->map_pgt(vpgd->obj, pde, vpgt->obj); + } + + return 0; +} + +int +nvkm_vm_get(struct nvkm_vm *vm, u64 size, u32 page_shift, u32 access, + struct nvkm_vma *vma) +{ + struct nvkm_mmu *mmu = vm->mmu; + u32 align = (1 << page_shift) >> 12; + u32 msize = size >> 12; + u32 fpde, lpde, pde; + int ret; + + mutex_lock(&nv_subdev(mmu)->mutex); + ret = nvkm_mm_head(&vm->mm, 0, page_shift, msize, msize, align, + &vma->node); + if (unlikely(ret != 0)) { + mutex_unlock(&nv_subdev(mmu)->mutex); + return ret; + } + + fpde = (vma->node->offset >> mmu->pgt_bits); + lpde = (vma->node->offset + vma->node->length - 1) >> mmu->pgt_bits; + + for (pde = fpde; pde <= lpde; pde++) { + struct nvkm_vm_pgt *vpgt = &vm->pgt[pde - vm->fpde]; + int big = (vma->node->type != mmu->spg_shift); + + if (likely(vpgt->refcount[big])) { + vpgt->refcount[big]++; + continue; + } + + ret = nvkm_vm_map_pgt(vm, pde, vma->node->type); + if (ret) { + if (pde != fpde) + nvkm_vm_unmap_pgt(vm, big, fpde, pde - 1); + nvkm_mm_free(&vm->mm, &vma->node); + mutex_unlock(&nv_subdev(mmu)->mutex); + return ret; + } + } + mutex_unlock(&nv_subdev(mmu)->mutex); + + vma->vm = NULL; + nvkm_vm_ref(vm, &vma->vm, NULL); + vma->offset = (u64)vma->node->offset << 12; + vma->access = access; + return 0; +} + +void +nvkm_vm_put(struct nvkm_vma *vma) +{ + struct nvkm_vm *vm = vma->vm; + struct nvkm_mmu *mmu = vm->mmu; + u32 fpde, lpde; + + if (unlikely(vma->node == NULL)) + return; + fpde = (vma->node->offset >> mmu->pgt_bits); + lpde = (vma->node->offset + vma->node->length - 1) >> mmu->pgt_bits; + + mutex_lock(&nv_subdev(mmu)->mutex); + nvkm_vm_unmap_pgt(vm, vma->node->type != mmu->spg_shift, fpde, lpde); + nvkm_mm_free(&vm->mm, &vma->node); + mutex_unlock(&nv_subdev(mmu)->mutex); + + nvkm_vm_ref(NULL, &vma->vm, NULL); +} + +int +nvkm_vm_create(struct nvkm_mmu *mmu, u64 offset, u64 length, u64 mm_offset, + u32 block, struct nvkm_vm **pvm) +{ + struct nvkm_vm *vm; + u64 mm_length = (offset + length) - mm_offset; + int ret; + + vm = kzalloc(sizeof(*vm), GFP_KERNEL); + if (!vm) + return -ENOMEM; + + INIT_LIST_HEAD(&vm->pgd_list); + vm->mmu = mmu; + kref_init(&vm->refcount); + vm->fpde = offset >> (mmu->pgt_bits + 12); + vm->lpde = (offset + length - 1) >> (mmu->pgt_bits + 12); + + vm->pgt = vzalloc((vm->lpde - vm->fpde + 1) * sizeof(*vm->pgt)); + if (!vm->pgt) { + kfree(vm); + return -ENOMEM; + } + + ret = nvkm_mm_init(&vm->mm, mm_offset >> 12, mm_length >> 12, + block >> 12); + if (ret) { + vfree(vm->pgt); + kfree(vm); + return ret; + } + + *pvm = vm; + + return 0; +} + +int +nvkm_vm_new(struct nvkm_device *device, u64 offset, u64 length, u64 mm_offset, + struct nvkm_vm **pvm) +{ + struct nvkm_mmu *mmu = nvkm_mmu(device); + return mmu->create(mmu, offset, length, mm_offset, pvm); +} + +static int +nvkm_vm_link(struct nvkm_vm *vm, struct nvkm_gpuobj *pgd) +{ + struct nvkm_mmu *mmu = vm->mmu; + struct nvkm_vm_pgd *vpgd; + int i; + + if (!pgd) + return 0; + + vpgd = kzalloc(sizeof(*vpgd), GFP_KERNEL); + if (!vpgd) + return -ENOMEM; + + nvkm_gpuobj_ref(pgd, &vpgd->obj); + + mutex_lock(&nv_subdev(mmu)->mutex); + for (i = vm->fpde; i <= vm->lpde; i++) + mmu->map_pgt(pgd, i, vm->pgt[i - vm->fpde].obj); + list_add(&vpgd->head, &vm->pgd_list); + mutex_unlock(&nv_subdev(mmu)->mutex); + return 0; +} + +static void +nvkm_vm_unlink(struct nvkm_vm *vm, struct nvkm_gpuobj *mpgd) +{ + struct nvkm_mmu *mmu = vm->mmu; + struct nvkm_vm_pgd *vpgd, *tmp; + struct nvkm_gpuobj *pgd = NULL; + + if (!mpgd) + return; + + mutex_lock(&nv_subdev(mmu)->mutex); + list_for_each_entry_safe(vpgd, tmp, &vm->pgd_list, head) { + if (vpgd->obj == mpgd) { + pgd = vpgd->obj; + list_del(&vpgd->head); + kfree(vpgd); + break; + } + } + mutex_unlock(&nv_subdev(mmu)->mutex); + + nvkm_gpuobj_ref(NULL, &pgd); +} + +static void +nvkm_vm_del(struct kref *kref) +{ + struct nvkm_vm *vm = container_of(kref, typeof(*vm), refcount); + struct nvkm_vm_pgd *vpgd, *tmp; + + list_for_each_entry_safe(vpgd, tmp, &vm->pgd_list, head) { + nvkm_vm_unlink(vm, vpgd->obj); + } + + nvkm_mm_fini(&vm->mm); + vfree(vm->pgt); + kfree(vm); +} + +int +nvkm_vm_ref(struct nvkm_vm *ref, struct nvkm_vm **ptr, struct nvkm_gpuobj *pgd) +{ + if (ref) { + int ret = nvkm_vm_link(ref, pgd); + if (ret) + return ret; + + kref_get(&ref->refcount); + } + + if (*ptr) { + nvkm_vm_unlink(*ptr, pgd); + kref_put(&(*ptr)->refcount, nvkm_vm_del); + } + + *ptr = ref; + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gf100.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gf100.c new file mode 100644 index 000000000..294cda37f --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gf100.c @@ -0,0 +1,237 @@ +/* + * 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 +#include +#include +#include +#include + +#include + +struct gf100_mmu_priv { + struct nvkm_mmu base; +}; + + +/* Map from compressed to corresponding uncompressed storage type. + * The value 0xff represents an invalid storage type. + */ +const u8 gf100_pte_storage_type_map[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 +}; + + +static void +gf100_vm_map_pgt(struct nvkm_gpuobj *pgd, u32 index, struct nvkm_gpuobj *pgt[2]) +{ + u32 pde[2] = { 0, 0 }; + + if (pgt[0]) + pde[1] = 0x00000001 | (pgt[0]->addr >> 8); + if (pgt[1]) + pde[0] = 0x00000001 | (pgt[1]->addr >> 8); + + nv_wo32(pgd, (index * 8) + 0, pde[0]); + nv_wo32(pgd, (index * 8) + 4, pde[1]); +} + +static inline u64 +gf100_vm_addr(struct nvkm_vma *vma, u64 phys, u32 memtype, u32 target) +{ + phys >>= 8; + + phys |= 0x00000001; /* present */ + if (vma->access & NV_MEM_ACCESS_SYS) + phys |= 0x00000002; + + phys |= ((u64)target << 32); + phys |= ((u64)memtype << 36); + return phys; +} + +static void +gf100_vm_map(struct nvkm_vma *vma, struct nvkm_gpuobj *pgt, + struct nvkm_mem *mem, u32 pte, u32 cnt, u64 phys, u64 delta) +{ + u64 next = 1 << (vma->node->type - 8); + + phys = gf100_vm_addr(vma, phys, mem->memtype, 0); + pte <<= 3; + + if (mem->tag) { + struct nvkm_ltc *ltc = nvkm_ltc(vma->vm->mmu); + u32 tag = mem->tag->offset + (delta >> 17); + phys |= (u64)tag << (32 + 12); + next |= (u64)1 << (32 + 12); + ltc->tags_clear(ltc, tag, cnt); + } + + while (cnt--) { + nv_wo32(pgt, pte + 0, lower_32_bits(phys)); + nv_wo32(pgt, pte + 4, upper_32_bits(phys)); + phys += next; + pte += 8; + } +} + +static void +gf100_vm_map_sg(struct nvkm_vma *vma, struct nvkm_gpuobj *pgt, + struct nvkm_mem *mem, u32 pte, u32 cnt, dma_addr_t *list) +{ + u32 target = (vma->access & NV_MEM_ACCESS_NOSNOOP) ? 7 : 5; + /* compressed storage types are invalid for system memory */ + u32 memtype = gf100_pte_storage_type_map[mem->memtype & 0xff]; + + pte <<= 3; + while (cnt--) { + u64 phys = gf100_vm_addr(vma, *list++, memtype, target); + nv_wo32(pgt, pte + 0, lower_32_bits(phys)); + nv_wo32(pgt, pte + 4, upper_32_bits(phys)); + pte += 8; + } +} + +static void +gf100_vm_unmap(struct nvkm_gpuobj *pgt, u32 pte, u32 cnt) +{ + pte <<= 3; + while (cnt--) { + nv_wo32(pgt, pte + 0, 0x00000000); + nv_wo32(pgt, pte + 4, 0x00000000); + pte += 8; + } +} + +static void +gf100_vm_flush(struct nvkm_vm *vm) +{ + struct gf100_mmu_priv *priv = (void *)vm->mmu; + struct nvkm_bar *bar = nvkm_bar(priv); + struct nvkm_vm_pgd *vpgd; + u32 type; + + bar->flush(bar); + + type = 0x00000001; /* PAGE_ALL */ + if (atomic_read(&vm->engref[NVDEV_SUBDEV_BAR])) + type |= 0x00000004; /* HUB_ONLY */ + + mutex_lock(&nv_subdev(priv)->mutex); + list_for_each_entry(vpgd, &vm->pgd_list, head) { + /* looks like maybe a "free flush slots" counter, the + * faster you write to 0x100cbc to more it decreases + */ + if (!nv_wait_ne(priv, 0x100c80, 0x00ff0000, 0x00000000)) { + nv_error(priv, "vm timeout 0: 0x%08x %d\n", + nv_rd32(priv, 0x100c80), type); + } + + nv_wr32(priv, 0x100cb8, vpgd->obj->addr >> 8); + nv_wr32(priv, 0x100cbc, 0x80000000 | type); + + /* wait for flush to be queued? */ + if (!nv_wait(priv, 0x100c80, 0x00008000, 0x00008000)) { + nv_error(priv, "vm timeout 1: 0x%08x %d\n", + nv_rd32(priv, 0x100c80), type); + } + } + mutex_unlock(&nv_subdev(priv)->mutex); +} + +static int +gf100_vm_create(struct nvkm_mmu *mmu, u64 offset, u64 length, u64 mm_offset, + struct nvkm_vm **pvm) +{ + return nvkm_vm_create(mmu, offset, length, mm_offset, 4096, pvm); +} + +static int +gf100_mmu_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gf100_mmu_priv *priv; + int ret; + + ret = nvkm_mmu_create(parent, engine, oclass, "VM", "vm", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.limit = 1ULL << 40; + priv->base.dma_bits = 40; + priv->base.pgt_bits = 27 - 12; + priv->base.spg_shift = 12; + priv->base.lpg_shift = 17; + priv->base.create = gf100_vm_create; + priv->base.map_pgt = gf100_vm_map_pgt; + priv->base.map = gf100_vm_map; + priv->base.map_sg = gf100_vm_map_sg; + priv->base.unmap = gf100_vm_unmap; + priv->base.flush = gf100_vm_flush; + return 0; +} + +struct nvkm_oclass +gf100_mmu_oclass = { + .handle = NV_SUBDEV(MMU, 0xc0), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_mmu_ctor, + .dtor = _nvkm_mmu_dtor, + .init = _nvkm_mmu_init, + .fini = _nvkm_mmu_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv04.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv04.c new file mode 100644 index 000000000..fe93ea271 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv04.c @@ -0,0 +1,151 @@ +/* + * 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 + +#define NV04_PDMA_SIZE (128 * 1024 * 1024) +#define NV04_PDMA_PAGE ( 4 * 1024) + +/******************************************************************************* + * VM map/unmap callbacks + ******************************************************************************/ + +static void +nv04_vm_map_sg(struct nvkm_vma *vma, struct nvkm_gpuobj *pgt, + struct nvkm_mem *mem, u32 pte, u32 cnt, dma_addr_t *list) +{ + pte = 0x00008 + (pte * 4); + while (cnt) { + u32 page = PAGE_SIZE / NV04_PDMA_PAGE; + u32 phys = (u32)*list++; + while (cnt && page--) { + nv_wo32(pgt, pte, phys | 3); + phys += NV04_PDMA_PAGE; + pte += 4; + cnt -= 1; + } + } +} + +static void +nv04_vm_unmap(struct nvkm_gpuobj *pgt, u32 pte, u32 cnt) +{ + pte = 0x00008 + (pte * 4); + while (cnt--) { + nv_wo32(pgt, pte, 0x00000000); + pte += 4; + } +} + +static void +nv04_vm_flush(struct nvkm_vm *vm) +{ +} + +/******************************************************************************* + * VM object + ******************************************************************************/ + +int +nv04_vm_create(struct nvkm_mmu *mmu, u64 offset, u64 length, u64 mmstart, + struct nvkm_vm **pvm) +{ + return -EINVAL; +} + +/******************************************************************************* + * MMU subdev + ******************************************************************************/ + +static int +nv04_mmu_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv04_mmu_priv *priv; + struct nvkm_gpuobj *dma; + int ret; + + ret = nvkm_mmu_create(parent, engine, oclass, "PCIGART", + "pcigart", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.create = nv04_vm_create; + priv->base.limit = NV04_PDMA_SIZE; + priv->base.dma_bits = 32; + priv->base.pgt_bits = 32 - 12; + priv->base.spg_shift = 12; + priv->base.lpg_shift = 12; + priv->base.map_sg = nv04_vm_map_sg; + priv->base.unmap = nv04_vm_unmap; + priv->base.flush = nv04_vm_flush; + + ret = nvkm_vm_create(&priv->base, 0, NV04_PDMA_SIZE, 0, 4096, + &priv->vm); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(nv_object(priv), NULL, + (NV04_PDMA_SIZE / NV04_PDMA_PAGE) * 4 + 8, + 16, NVOBJ_FLAG_ZERO_ALLOC, + &priv->vm->pgt[0].obj[0]); + dma = priv->vm->pgt[0].obj[0]; + priv->vm->pgt[0].refcount[0] = 1; + if (ret) + return ret; + + nv_wo32(dma, 0x00000, 0x0002103d); /* PCI, RW, PT, !LN */ + nv_wo32(dma, 0x00004, NV04_PDMA_SIZE - 1); + return 0; +} + +void +nv04_mmu_dtor(struct nvkm_object *object) +{ + struct nv04_mmu_priv *priv = (void *)object; + if (priv->vm) { + nvkm_gpuobj_ref(NULL, &priv->vm->pgt[0].obj[0]); + nvkm_vm_ref(NULL, &priv->vm, NULL); + } + if (priv->nullp) { + pci_free_consistent(nv_device(priv)->pdev, 16 * 1024, + priv->nullp, priv->null); + } + nvkm_mmu_destroy(&priv->base); +} + +struct nvkm_oclass +nv04_mmu_oclass = { + .handle = NV_SUBDEV(MMU, 0x04), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_mmu_ctor, + .dtor = nv04_mmu_dtor, + .init = _nvkm_mmu_init, + .fini = _nvkm_mmu_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv04.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv04.h new file mode 100644 index 000000000..7bf6f4b38 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv04.h @@ -0,0 +1,19 @@ +#ifndef __NV04_MMU_PRIV__ +#define __NV04_MMU_PRIV__ + +#include + +struct nv04_mmu_priv { + struct nvkm_mmu base; + struct nvkm_vm *vm; + dma_addr_t null; + void *nullp; +}; + +static inline struct nv04_mmu_priv * +nv04_mmu(void *obj) +{ + return (void *)nvkm_mmu(obj); +} + +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv41.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv41.c new file mode 100644 index 000000000..61ee3ab11 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv41.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 + */ +#include "nv04.h" + +#include +#include +#include +#include + +#define NV41_GART_SIZE (512 * 1024 * 1024) +#define NV41_GART_PAGE ( 4 * 1024) + +/******************************************************************************* + * VM map/unmap callbacks + ******************************************************************************/ + +static void +nv41_vm_map_sg(struct nvkm_vma *vma, struct nvkm_gpuobj *pgt, + struct nvkm_mem *mem, u32 pte, u32 cnt, dma_addr_t *list) +{ + pte = pte * 4; + while (cnt) { + u32 page = PAGE_SIZE / NV41_GART_PAGE; + u64 phys = (u64)*list++; + while (cnt && page--) { + nv_wo32(pgt, pte, (phys >> 7) | 1); + phys += NV41_GART_PAGE; + pte += 4; + cnt -= 1; + } + } +} + +static void +nv41_vm_unmap(struct nvkm_gpuobj *pgt, u32 pte, u32 cnt) +{ + pte = pte * 4; + while (cnt--) { + nv_wo32(pgt, pte, 0x00000000); + pte += 4; + } +} + +static void +nv41_vm_flush(struct nvkm_vm *vm) +{ + struct nv04_mmu_priv *priv = (void *)vm->mmu; + + mutex_lock(&nv_subdev(priv)->mutex); + nv_wr32(priv, 0x100810, 0x00000022); + if (!nv_wait(priv, 0x100810, 0x00000020, 0x00000020)) { + nv_warn(priv, "flush timeout, 0x%08x\n", + nv_rd32(priv, 0x100810)); + } + nv_wr32(priv, 0x100810, 0x00000000); + mutex_unlock(&nv_subdev(priv)->mutex); +} + +/******************************************************************************* + * MMU subdev + ******************************************************************************/ + +static int +nv41_mmu_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_device *device = nv_device(parent); + struct nv04_mmu_priv *priv; + int ret; + + if (pci_find_capability(device->pdev, PCI_CAP_ID_AGP) || + !nvkm_boolopt(device->cfgopt, "NvPCIE", true)) { + return nvkm_object_ctor(parent, engine, &nv04_mmu_oclass, + data, size, pobject); + } + + ret = nvkm_mmu_create(parent, engine, oclass, "PCIEGART", + "pciegart", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.create = nv04_vm_create; + priv->base.limit = NV41_GART_SIZE; + priv->base.dma_bits = 39; + priv->base.pgt_bits = 32 - 12; + priv->base.spg_shift = 12; + priv->base.lpg_shift = 12; + priv->base.map_sg = nv41_vm_map_sg; + priv->base.unmap = nv41_vm_unmap; + priv->base.flush = nv41_vm_flush; + + ret = nvkm_vm_create(&priv->base, 0, NV41_GART_SIZE, 0, 4096, + &priv->vm); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(nv_object(priv), NULL, + (NV41_GART_SIZE / NV41_GART_PAGE) * 4, 16, + NVOBJ_FLAG_ZERO_ALLOC, + &priv->vm->pgt[0].obj[0]); + priv->vm->pgt[0].refcount[0] = 1; + if (ret) + return ret; + + return 0; +} + +static int +nv41_mmu_init(struct nvkm_object *object) +{ + struct nv04_mmu_priv *priv = (void *)object; + struct nvkm_gpuobj *dma = priv->vm->pgt[0].obj[0]; + int ret; + + ret = nvkm_mmu_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, 0x100800, dma->addr | 0x00000002); + nv_mask(priv, 0x10008c, 0x00000100, 0x00000100); + nv_wr32(priv, 0x100820, 0x00000000); + return 0; +} + +struct nvkm_oclass +nv41_mmu_oclass = { + .handle = NV_SUBDEV(MMU, 0x41), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv41_mmu_ctor, + .dtor = nv04_mmu_dtor, + .init = nv41_mmu_init, + .fini = _nvkm_mmu_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv44.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv44.c new file mode 100644 index 000000000..b90ded188 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv44.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 "nv04.h" + +#include +#include +#include +#include + +#define NV44_GART_SIZE (512 * 1024 * 1024) +#define NV44_GART_PAGE ( 4 * 1024) + +/******************************************************************************* + * VM map/unmap callbacks + ******************************************************************************/ + +static void +nv44_vm_fill(struct nvkm_gpuobj *pgt, dma_addr_t null, + dma_addr_t *list, u32 pte, u32 cnt) +{ + u32 base = (pte << 2) & ~0x0000000f; + u32 tmp[4]; + + tmp[0] = nv_ro32(pgt, base + 0x0); + tmp[1] = nv_ro32(pgt, base + 0x4); + tmp[2] = nv_ro32(pgt, base + 0x8); + tmp[3] = nv_ro32(pgt, base + 0xc); + + while (cnt--) { + u32 addr = list ? (*list++ >> 12) : (null >> 12); + switch (pte++ & 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; + } + } + + nv_wo32(pgt, base + 0x0, tmp[0]); + nv_wo32(pgt, base + 0x4, tmp[1]); + nv_wo32(pgt, base + 0x8, tmp[2]); + nv_wo32(pgt, base + 0xc, tmp[3] | 0x40000000); +} + +static void +nv44_vm_map_sg(struct nvkm_vma *vma, struct nvkm_gpuobj *pgt, + struct nvkm_mem *mem, u32 pte, u32 cnt, dma_addr_t *list) +{ + struct nv04_mmu_priv *priv = (void *)vma->vm->mmu; + u32 tmp[4]; + int i; + + if (pte & 3) { + u32 max = 4 - (pte & 3); + u32 part = (cnt > max) ? max : cnt; + nv44_vm_fill(pgt, priv->null, list, pte, part); + pte += part; + list += part; + cnt -= part; + } + + while (cnt >= 4) { + for (i = 0; i < 4; i++) + tmp[i] = *list++ >> 12; + nv_wo32(pgt, pte++ * 4, tmp[0] >> 0 | tmp[1] << 27); + nv_wo32(pgt, pte++ * 4, tmp[1] >> 5 | tmp[2] << 22); + nv_wo32(pgt, pte++ * 4, tmp[2] >> 10 | tmp[3] << 17); + nv_wo32(pgt, pte++ * 4, tmp[3] >> 15 | 0x40000000); + cnt -= 4; + } + + if (cnt) + nv44_vm_fill(pgt, priv->null, list, pte, cnt); +} + +static void +nv44_vm_unmap(struct nvkm_gpuobj *pgt, u32 pte, u32 cnt) +{ + struct nv04_mmu_priv *priv = (void *)nvkm_mmu(pgt); + + if (pte & 3) { + u32 max = 4 - (pte & 3); + u32 part = (cnt > max) ? max : cnt; + nv44_vm_fill(pgt, priv->null, NULL, pte, part); + pte += part; + cnt -= part; + } + + while (cnt >= 4) { + nv_wo32(pgt, pte++ * 4, 0x00000000); + nv_wo32(pgt, pte++ * 4, 0x00000000); + nv_wo32(pgt, pte++ * 4, 0x00000000); + nv_wo32(pgt, pte++ * 4, 0x00000000); + cnt -= 4; + } + + if (cnt) + nv44_vm_fill(pgt, priv->null, NULL, pte, cnt); +} + +static void +nv44_vm_flush(struct nvkm_vm *vm) +{ + struct nv04_mmu_priv *priv = (void *)vm->mmu; + nv_wr32(priv, 0x100814, priv->base.limit - NV44_GART_PAGE); + nv_wr32(priv, 0x100808, 0x00000020); + if (!nv_wait(priv, 0x100808, 0x00000001, 0x00000001)) + nv_error(priv, "timeout: 0x%08x\n", nv_rd32(priv, 0x100808)); + nv_wr32(priv, 0x100808, 0x00000000); +} + +/******************************************************************************* + * MMU subdev + ******************************************************************************/ + +static int +nv44_mmu_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_device *device = nv_device(parent); + struct nv04_mmu_priv *priv; + int ret; + + if (pci_find_capability(device->pdev, PCI_CAP_ID_AGP) || + !nvkm_boolopt(device->cfgopt, "NvPCIE", true)) { + return nvkm_object_ctor(parent, engine, &nv04_mmu_oclass, + data, size, pobject); + } + + ret = nvkm_mmu_create(parent, engine, oclass, "PCIEGART", + "pciegart", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.create = nv04_vm_create; + priv->base.limit = NV44_GART_SIZE; + priv->base.dma_bits = 39; + priv->base.pgt_bits = 32 - 12; + priv->base.spg_shift = 12; + priv->base.lpg_shift = 12; + priv->base.map_sg = nv44_vm_map_sg; + priv->base.unmap = nv44_vm_unmap; + priv->base.flush = nv44_vm_flush; + + priv->nullp = pci_alloc_consistent(device->pdev, 16 * 1024, &priv->null); + if (!priv->nullp) { + nv_error(priv, "unable to allocate dummy pages\n"); + return -ENOMEM; + } + + ret = nvkm_vm_create(&priv->base, 0, NV44_GART_SIZE, 0, 4096, + &priv->vm); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(nv_object(priv), NULL, + (NV44_GART_SIZE / NV44_GART_PAGE) * 4, + 512 * 1024, NVOBJ_FLAG_ZERO_ALLOC, + &priv->vm->pgt[0].obj[0]); + priv->vm->pgt[0].refcount[0] = 1; + if (ret) + return ret; + + return 0; +} + +static int +nv44_mmu_init(struct nvkm_object *object) +{ + struct nv04_mmu_priv *priv = (void *)object; + struct nvkm_gpuobj *gart = priv->vm->pgt[0].obj[0]; + u32 addr; + int ret; + + ret = nvkm_mmu_init(&priv->base); + if (ret) + return ret; + + /* 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 = nv_rd32(priv, 0x10020c); + addr -= ((gart->addr >> 19) + 1) << 19; + + nv_wr32(priv, 0x100850, 0x80000000); + nv_wr32(priv, 0x100818, priv->null); + nv_wr32(priv, 0x100804, NV44_GART_SIZE); + nv_wr32(priv, 0x100850, 0x00008000); + nv_mask(priv, 0x10008c, 0x00000200, 0x00000200); + nv_wr32(priv, 0x100820, 0x00000000); + nv_wr32(priv, 0x10082c, 0x00000001); + nv_wr32(priv, 0x100800, addr | 0x00000010); + return 0; +} + +struct nvkm_oclass +nv44_mmu_oclass = { + .handle = NV_SUBDEV(MMU, 0x44), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv44_mmu_ctor, + .dtor = nv04_mmu_dtor, + .init = nv44_mmu_init, + .fini = _nvkm_mmu_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv50.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv50.c new file mode 100644 index 000000000..b83550fa7 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv50.c @@ -0,0 +1,241 @@ +/* + * 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 +#include +#include +#include + +#include +#include + +struct nv50_mmu_priv { + struct nvkm_mmu base; +}; + +static void +nv50_vm_map_pgt(struct nvkm_gpuobj *pgd, u32 pde, struct nvkm_gpuobj *pgt[2]) +{ + u64 phys = 0xdeadcafe00000000ULL; + u32 coverage = 0; + + if (pgt[0]) { + phys = 0x00000003 | pgt[0]->addr; /* present, 4KiB pages */ + coverage = (pgt[0]->size >> 3) << 12; + } else + if (pgt[1]) { + phys = 0x00000001 | pgt[1]->addr; /* present */ + coverage = (pgt[1]->size >> 3) << 16; + } + + if (phys & 1) { + if (coverage <= 32 * 1024 * 1024) + phys |= 0x60; + else if (coverage <= 64 * 1024 * 1024) + phys |= 0x40; + else if (coverage <= 128 * 1024 * 1024) + phys |= 0x20; + } + + nv_wo32(pgd, (pde * 8) + 0, lower_32_bits(phys)); + nv_wo32(pgd, (pde * 8) + 4, upper_32_bits(phys)); +} + +static inline u64 +vm_addr(struct nvkm_vma *vma, u64 phys, u32 memtype, u32 target) +{ + phys |= 1; /* present */ + phys |= (u64)memtype << 40; + phys |= target << 4; + if (vma->access & NV_MEM_ACCESS_SYS) + phys |= (1 << 6); + if (!(vma->access & NV_MEM_ACCESS_WO)) + phys |= (1 << 3); + return phys; +} + +static void +nv50_vm_map(struct nvkm_vma *vma, struct nvkm_gpuobj *pgt, + struct nvkm_mem *mem, u32 pte, u32 cnt, u64 phys, u64 delta) +{ + u32 comp = (mem->memtype & 0x180) >> 7; + u32 block, target; + int i; + + /* IGPs don't have real VRAM, re-target to stolen system memory */ + target = 0; + if (nvkm_fb(vma->vm->mmu)->ram->stolen) { + phys += nvkm_fb(vma->vm->mmu)->ram->stolen; + target = 3; + } + + phys = vm_addr(vma, phys, mem->memtype, target); + pte <<= 3; + cnt <<= 3; + + while (cnt) { + u32 offset_h = upper_32_bits(phys); + u32 offset_l = lower_32_bits(phys); + + for (i = 7; i >= 0; i--) { + block = 1 << (i + 3); + if (cnt >= block && !(pte & (block - 1))) + break; + } + offset_l |= (i << 7); + + phys += block << (vma->node->type - 3); + cnt -= block; + if (comp) { + u32 tag = mem->tag->offset + ((delta >> 16) * comp); + offset_h |= (tag << 17); + delta += block << (vma->node->type - 3); + } + + while (block) { + nv_wo32(pgt, pte + 0, offset_l); + nv_wo32(pgt, pte + 4, offset_h); + pte += 8; + block -= 8; + } + } +} + +static void +nv50_vm_map_sg(struct nvkm_vma *vma, struct nvkm_gpuobj *pgt, + struct nvkm_mem *mem, u32 pte, u32 cnt, dma_addr_t *list) +{ + u32 target = (vma->access & NV_MEM_ACCESS_NOSNOOP) ? 3 : 2; + pte <<= 3; + while (cnt--) { + u64 phys = vm_addr(vma, (u64)*list++, mem->memtype, target); + nv_wo32(pgt, pte + 0, lower_32_bits(phys)); + nv_wo32(pgt, pte + 4, upper_32_bits(phys)); + pte += 8; + } +} + +static void +nv50_vm_unmap(struct nvkm_gpuobj *pgt, u32 pte, u32 cnt) +{ + pte <<= 3; + while (cnt--) { + nv_wo32(pgt, pte + 0, 0x00000000); + nv_wo32(pgt, pte + 4, 0x00000000); + pte += 8; + } +} + +static void +nv50_vm_flush(struct nvkm_vm *vm) +{ + struct nv50_mmu_priv *priv = (void *)vm->mmu; + struct nvkm_bar *bar = nvkm_bar(priv); + struct nvkm_engine *engine; + int i, vme; + + bar->flush(bar); + + mutex_lock(&nv_subdev(priv)->mutex); + for (i = 0; i < NVDEV_SUBDEV_NR; i++) { + if (!atomic_read(&vm->engref[i])) + continue; + + /* unfortunate hw bug workaround... */ + engine = nvkm_engine(priv, i); + if (engine && engine->tlb_flush) { + engine->tlb_flush(engine); + continue; + } + + switch (i) { + case NVDEV_ENGINE_GR : vme = 0x00; break; + case NVDEV_ENGINE_VP : + case NVDEV_ENGINE_MSPDEC: vme = 0x01; break; + case NVDEV_SUBDEV_BAR : vme = 0x06; break; + case NVDEV_ENGINE_MSPPP : + case NVDEV_ENGINE_MPEG : vme = 0x08; break; + case NVDEV_ENGINE_BSP : + case NVDEV_ENGINE_MSVLD : vme = 0x09; break; + case NVDEV_ENGINE_CIPHER: + case NVDEV_ENGINE_SEC : vme = 0x0a; break; + case NVDEV_ENGINE_CE0 : vme = 0x0d; break; + default: + continue; + } + + nv_wr32(priv, 0x100c80, (vme << 16) | 1); + if (!nv_wait(priv, 0x100c80, 0x00000001, 0x00000000)) + nv_error(priv, "vm flush timeout: engine %d\n", vme); + } + mutex_unlock(&nv_subdev(priv)->mutex); +} + +static int +nv50_vm_create(struct nvkm_mmu *mmu, u64 offset, u64 length, + u64 mm_offset, struct nvkm_vm **pvm) +{ + u32 block = (1 << (mmu->pgt_bits + 12)); + if (block > length) + block = length; + + return nvkm_vm_create(mmu, offset, length, mm_offset, block, pvm); +} + +static int +nv50_mmu_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv50_mmu_priv *priv; + int ret; + + ret = nvkm_mmu_create(parent, engine, oclass, "VM", "vm", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.limit = 1ULL << 40; + priv->base.dma_bits = 40; + priv->base.pgt_bits = 29 - 12; + priv->base.spg_shift = 12; + priv->base.lpg_shift = 16; + priv->base.create = nv50_vm_create; + priv->base.map_pgt = nv50_vm_map_pgt; + priv->base.map = nv50_vm_map; + priv->base.map_sg = nv50_vm_map_sg; + priv->base.unmap = nv50_vm_unmap; + priv->base.flush = nv50_vm_flush; + return 0; +} + +struct nvkm_oclass +nv50_mmu_oclass = { + .handle = NV_SUBDEV(MMU, 0x50), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_mmu_ctor, + .dtor = _nvkm_mmu_dtor, + .init = _nvkm_mmu_init, + .fini = _nvkm_mmu_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/Kbuild new file mode 100644 index 000000000..1a479e050 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/Kbuild @@ -0,0 +1,3 @@ +nvkm-y += nvkm/subdev/mxm/base.o +nvkm-y += nvkm/subdev/mxm/mxms.o +nvkm-y += nvkm/subdev/mxm/nv50.o diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/base.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/base.c new file mode 100644 index 000000000..0ca9dcabb --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/base.c @@ -0,0 +1,271 @@ +/* + * 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 +#include + +static bool +mxm_shadow_rom_fetch(struct nvkm_i2c_port *i2c, u8 addr, + u8 offset, u8 size, u8 *data) +{ + struct i2c_msg msgs[] = { + { .addr = addr, .flags = 0, .len = 1, .buf = &offset }, + { .addr = addr, .flags = I2C_M_RD, .len = size, .buf = data, }, + }; + + return i2c_transfer(&i2c->adapter, msgs, 2) == 2; +} + +static bool +mxm_shadow_rom(struct nvkm_mxm *mxm, u8 version) +{ + struct nvkm_bios *bios = nvkm_bios(mxm); + struct nvkm_i2c *i2c = nvkm_i2c(mxm); + struct nvkm_i2c_port *port = NULL; + u8 i2cidx, mxms[6], addr, size; + + i2cidx = mxm_ddc_map(bios, 1 /* LVDS_DDC */) & 0x0f; + if (i2cidx < 0x0f) + port = i2c->find(i2c, i2cidx); + if (!port) + return false; + + addr = 0x54; + if (!mxm_shadow_rom_fetch(port, addr, 0, 6, mxms)) { + addr = 0x56; + if (!mxm_shadow_rom_fetch(port, 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(port, 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_device *device = nv_device(mxm); + static char muid[] = { + 0x00, 0xA4, 0x04, 0x40, 0x7D, 0x91, 0xF2, 0x4C, + 0xB8, 0x9C, 0x79, 0xB6, 0x2F, 0xD5, 0x56, 0x65 + }; + u32 mxms_args[] = { 0x00000000 }; + union acpi_object 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(nv_device_base(device)); + 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) { + nv_debug(mxm, "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) { + nv_debug(mxm, "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) +{ + 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)) { + nv_debug(mxm, "WMMX MXMI returned %d\n", status); + return 0x00; + } + + obj = retn.pointer; + if (obj->type == ACPI_TYPE_INTEGER) { + version = obj->integer.value; + nv_debug(mxm, "WMMX MXMI version %d.%d\n", + (version >> 4), version & 0x0f); + } else { + version = 0; + nv_debug(mxm, "WMMX MXMI returned non-integer\n"); + } + + kfree(obj); + return version; +} + +static bool +mxm_shadow_wmi(struct nvkm_mxm *mxm, u8 version) +{ + 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)) { + nv_debug(mxm, "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)) { + nv_debug(mxm, "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 { + nv_debug(mxm, "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; +} + +int +nvkm_mxm_create_(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, int length, void **pobject) +{ + struct nvkm_device *device = nv_device(parent); + struct nvkm_bios *bios = nvkm_bios(device); + struct nvkm_mxm *mxm; + u8 ver, len; + u16 data; + int ret; + + ret = nvkm_subdev_create_(parent, engine, oclass, 0, "MXM", "mxm", + length, pobject); + mxm = *pobject; + if (ret) + return ret; + + data = mxm_table(bios, &ver, &len); + if (!data || !(ver = nv_ro08(bios, data))) { + nv_debug(mxm, "no VBIOS data, nothing to do\n"); + return 0; + } + + nv_info(mxm, "BIOS version %d.%d\n", ver >> 4, ver & 0x0f); + + if (mxm_shadow(mxm, ver)) { + nv_info(mxm, "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 + } + + nv_info(mxm, "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/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/mxms.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/mxms.c new file mode 100644 index 000000000..a9b1d63fe --- /dev/null +++ b/kernel/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) le16_to_cpu(*(u16 *)&(x)) +#define ROM32(x) le32_to_cpu(*(u32 *)&(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; + } + + nv_debug(mxm, "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) { + nv_debug(mxm, "checksum invalid\n"); + return false; + } + return true; +} + +bool +mxms_valid(struct nvkm_mxm *mxm) +{ + u8 *mxms = mxms_data(mxm); + if (*(u32 *)mxms != 0x5f4d584d) { + nv_debug(mxm, "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) +{ + 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: + nv_debug(mxm, "unknown descriptor type %d\n", type); + return false; + } + + if (nv_subdev(mxm)->debug >= NV_DBG_DEBUG && (exec == NULL)) { + static const char * mxms_desc_name[] = { + "ODS", "SCCS", "TS", "IPS", + "GSD", "VSS", "BCS", "FCS", + }; + u8 *dump = desc; + int i, j; + + nv_debug(mxm, "%4s: ", mxms_desc_name[type]); + for (j = headerlen - 1; j >= 0; j--) + pr_cont("%02x", dump[j]); + pr_cont("\n"); + dump += headerlen; + + for (i = 0; i < entries; i++, dump += recordlen) { + nv_debug(mxm, " "); + for (j = recordlen - 1; j >= 0; j--) + pr_cont("%02x", dump[j]); + pr_cont("\n"); + } + } + + 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/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/mxms.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/mxms.h new file mode 100644 index 000000000..4ef804012 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/mxms.h @@ -0,0 +1,22 @@ +#ifndef __NVMXM_MXMS_H__ +#define __NVMXM_MXMS_H__ +#include + +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/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/nv50.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/nv50.c new file mode 100644 index 000000000..f20e4ca87 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/nv50.c @@ -0,0 +1,231 @@ +/* + * 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 nv50_mxm_priv { + struct nvkm_mxm base; +}; + +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 = nvkm_bios(mxm); + 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)) { + nv_debug(mxm, "disable %d: 0x%08x 0x%08x\n", + idx, ctx.outp[0], ctx.outp[1]); + ctx.outp[0] |= 0x0000000f; + return 0; + } + + /* modify the output's ddc/aux port, there's a pointer to a table + * with the mapping from mxm ddc/aux port to dcb i2c_index in the + * vbios mxm table + */ + i2cidx = mxm_ddc_map(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; + 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) +{ + u64 desc = *(u64 *)data; + if ((desc & 0xf0) != 0xf0) + nv_info(mxm, "unmatched output device 0x%016llx\n", desc); + return true; +} + +static void +mxm_dcb_sanitise(struct nvkm_mxm *mxm) +{ + struct nvkm_bios *bios = nvkm_bios(mxm); + u8 ver, hdr, cnt, len; + u16 dcb = dcb_table(bios, &ver, &hdr, &cnt, &len); + if (dcb == 0x0000 || ver != 0x40) { + nv_debug(mxm, "unsupported DCB version\n"); + return; + } + + dcb_outp_foreach(bios, mxm, mxm_dcb_sanitise_entry); + mxms_foreach(mxm, 0x01, mxm_show_unmatched, NULL); +} + +static int +nv50_mxm_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv50_mxm_priv *priv; + int ret; + + ret = nvkm_mxm_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + if (priv->base.action & MXM_SANITISE_DCB) + mxm_dcb_sanitise(&priv->base); + return 0; +} + +struct nvkm_oclass +nv50_mxm_oclass = { + .handle = NV_SUBDEV(MXM, 0x50), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_mxm_ctor, + .dtor = _nvkm_mxm_dtor, + .init = _nvkm_mxm_init, + .fini = _nvkm_mxm_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/Kbuild new file mode 100644 index 000000000..7081d6a9b --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/Kbuild @@ -0,0 +1,9 @@ +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/gf110.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 diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/base.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/base.c new file mode 100644 index 000000000..054b2d2ee --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/base.c @@ -0,0 +1,268 @@ +/* + * 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 + +void +nvkm_pmu_pgob(struct nvkm_pmu *pmu, bool enable) +{ + const struct nvkm_pmu_impl *impl = (void *)nv_oclass(pmu); + if (impl->pgob) + impl->pgob(pmu, enable); +} + +static int +nvkm_pmu_send(struct nvkm_pmu *pmu, u32 reply[2], + u32 process, u32 message, u32 data0, u32 data1) +{ + struct nvkm_subdev *subdev = nv_subdev(pmu); + u32 addr; + + /* wait for a free slot in the fifo */ + addr = nv_rd32(pmu, 0x10a4a0); + if (!nv_wait_ne(pmu, 0x10a4b0, 0xffffffff, addr ^ 8)) + 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) { + mutex_lock(&subdev->mutex); + pmu->recv.message = message; + pmu->recv.process = process; + } + + /* acquire data segment access */ + do { + nv_wr32(pmu, 0x10a580, 0x00000001); + } while (nv_rd32(pmu, 0x10a580) != 0x00000001); + + /* write the packet */ + nv_wr32(pmu, 0x10a1c0, 0x01000000 | (((addr & 0x07) << 4) + + pmu->send.base)); + nv_wr32(pmu, 0x10a1c4, process); + nv_wr32(pmu, 0x10a1c4, message); + nv_wr32(pmu, 0x10a1c4, data0); + nv_wr32(pmu, 0x10a1c4, data1); + nv_wr32(pmu, 0x10a4a0, (addr + 1) & 0x0f); + + /* release data segment access */ + nv_wr32(pmu, 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(&subdev->mutex); + } + + return 0; +} + +static void +nvkm_pmu_recv(struct work_struct *work) +{ + struct nvkm_pmu *pmu = container_of(work, struct nvkm_pmu, recv.work); + u32 process, message, data0, data1; + + /* nothing to do if GET == PUT */ + u32 addr = nv_rd32(pmu, 0x10a4cc); + if (addr == nv_rd32(pmu, 0x10a4c8)) + return; + + /* acquire data segment access */ + do { + nv_wr32(pmu, 0x10a580, 0x00000002); + } while (nv_rd32(pmu, 0x10a580) != 0x00000002); + + /* read the packet */ + nv_wr32(pmu, 0x10a1c0, 0x02000000 | (((addr & 0x07) << 4) + + pmu->recv.base)); + process = nv_rd32(pmu, 0x10a1c4); + message = nv_rd32(pmu, 0x10a1c4); + data0 = nv_rd32(pmu, 0x10a1c4); + data1 = nv_rd32(pmu, 0x10a1c4); + nv_wr32(pmu, 0x10a4cc, (addr + 1) & 0x0f); + + /* release data segment access */ + nv_wr32(pmu, 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. + */ + nv_warn(pmu, "%c%c%c%c 0x%08x 0x%08x 0x%08x 0x%08x\n", + (char)((process & 0x000000ff) >> 0), + (char)((process & 0x0000ff00) >> 8), + (char)((process & 0x00ff0000) >> 16), + (char)((process & 0xff000000) >> 24), + process, message, data0, data1); +} + +static void +nvkm_pmu_intr(struct nvkm_subdev *subdev) +{ + struct nvkm_pmu *pmu = (void *)subdev; + u32 disp = nv_rd32(pmu, 0x10a01c); + u32 intr = nv_rd32(pmu, 0x10a008) & disp & ~(disp >> 16); + + if (intr & 0x00000020) { + u32 stat = nv_rd32(pmu, 0x10a16c); + if (stat & 0x80000000) { + nv_error(pmu, "UAS fault at 0x%06x addr 0x%08x\n", + stat & 0x00ffffff, nv_rd32(pmu, 0x10a168)); + nv_wr32(pmu, 0x10a16c, 0x00000000); + intr &= ~0x00000020; + } + } + + if (intr & 0x00000040) { + schedule_work(&pmu->recv.work); + nv_wr32(pmu, 0x10a004, 0x00000040); + intr &= ~0x00000040; + } + + if (intr & 0x00000080) { + nv_info(pmu, "wr32 0x%06x 0x%08x\n", nv_rd32(pmu, 0x10a7a0), + nv_rd32(pmu, 0x10a7a4)); + nv_wr32(pmu, 0x10a004, 0x00000080); + intr &= ~0x00000080; + } + + if (intr) { + nv_error(pmu, "intr 0x%08x\n", intr); + nv_wr32(pmu, 0x10a004, intr); + } +} + +int +_nvkm_pmu_fini(struct nvkm_object *object, bool suspend) +{ + struct nvkm_pmu *pmu = (void *)object; + + nv_wr32(pmu, 0x10a014, 0x00000060); + flush_work(&pmu->recv.work); + + return nvkm_subdev_fini(&pmu->base, suspend); +} + +int +_nvkm_pmu_init(struct nvkm_object *object) +{ + const struct nvkm_pmu_impl *impl = (void *)object->oclass; + struct nvkm_pmu *pmu = (void *)object; + int ret, i; + + ret = nvkm_subdev_init(&pmu->base); + if (ret) + return ret; + + nv_subdev(pmu)->intr = nvkm_pmu_intr; + pmu->message = nvkm_pmu_send; + pmu->pgob = nvkm_pmu_pgob; + + /* prevent previous ucode from running, wait for idle, reset */ + nv_wr32(pmu, 0x10a014, 0x0000ffff); /* INTR_EN_CLR = ALL */ + nv_wait(pmu, 0x10a04c, 0xffffffff, 0x00000000); + nv_mask(pmu, 0x000200, 0x00002000, 0x00000000); + nv_mask(pmu, 0x000200, 0x00002000, 0x00002000); + nv_rd32(pmu, 0x000200); + nv_wait(pmu, 0x10a10c, 0x00000006, 0x00000000); + + /* upload data segment */ + nv_wr32(pmu, 0x10a1c0, 0x01000000); + for (i = 0; i < impl->data.size / 4; i++) + nv_wr32(pmu, 0x10a1c4, impl->data.data[i]); + + /* upload code segment */ + nv_wr32(pmu, 0x10a180, 0x01000000); + for (i = 0; i < impl->code.size / 4; i++) { + if ((i & 0x3f) == 0) + nv_wr32(pmu, 0x10a188, i >> 6); + nv_wr32(pmu, 0x10a184, impl->code.data[i]); + } + + /* start it running */ + nv_wr32(pmu, 0x10a10c, 0x00000000); + nv_wr32(pmu, 0x10a104, 0x00000000); + nv_wr32(pmu, 0x10a100, 0x00000002); + + /* wait for valid host->pmu ring configuration */ + if (!nv_wait_ne(pmu, 0x10a4d0, 0xffffffff, 0x00000000)) + return -EBUSY; + pmu->send.base = nv_rd32(pmu, 0x10a4d0) & 0x0000ffff; + pmu->send.size = nv_rd32(pmu, 0x10a4d0) >> 16; + + /* wait for valid pmu->host ring configuration */ + if (!nv_wait_ne(pmu, 0x10a4dc, 0xffffffff, 0x00000000)) + return -EBUSY; + pmu->recv.base = nv_rd32(pmu, 0x10a4dc) & 0x0000ffff; + pmu->recv.size = nv_rd32(pmu, 0x10a4dc) >> 16; + + nv_wr32(pmu, 0x10a010, 0x000000e0); + return 0; +} + +int +nvkm_pmu_create_(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, int length, void **pobject) +{ + struct nvkm_pmu *pmu; + int ret; + + ret = nvkm_subdev_create_(parent, engine, oclass, 0, "PMU", + "pmu", length, pobject); + pmu = *pobject; + if (ret) + return ret; + + INIT_WORK(&pmu->recv.work, nvkm_pmu_recv); + init_waitqueue_head(&pmu->recv.wait); + return 0; +} + +int +_nvkm_pmu_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_pmu *pmu; + int ret = nvkm_pmu_create(parent, engine, oclass, &pmu); + *pobject = nv_object(pmu); + return ret; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/arith.fuc b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/arith.fuc new file mode 100644 index 000000000..214a6d9e0 --- /dev/null +++ b/kernel/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/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf100.fuc3 b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf100.fuc3 new file mode 100644 index 000000000..37e8407b7 --- /dev/null +++ b/kernel/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/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf100.fuc3.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf100.fuc3.h new file mode 100644 index 000000000..302557c52 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf100.fuc3.h @@ -0,0 +1,1865 @@ +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, + 0x00000512, + 0x000004af, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x584d454d, + 0x0000075e, + 0x00000750, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x46524550, + 0x00000762, + 0x00000760, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x5f433249, + 0x00000b92, + 0x00000a35, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x54534554, + 0x00000bbb, + 0x00000b94, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x454c4449, + 0x00000bc7, + 0x00000bc5, + 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, + 0x00000551, +/* 0x037c: memx_func_next */ + 0x00000002, + 0x00000000, + 0x000005db, + 0x00000003, + 0x00000002, + 0x000006a5, + 0x00040004, + 0x00000000, + 0x000006c1, + 0x00010005, + 0x00000000, + 0x000006de, + 0x00010006, + 0x00000000, + 0x00000663, + 0x00000007, + 0x00000000, + 0x000006e9, +/* 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, +}; + +uint32_t gf100_pmu_code[] = { + 0x039e0ef5, +/* 0x0004: rd32 */ + 0x07a007f1, + 0xd00604b6, + 0x04bd000e, + 0xf001d7f0, + 0x07f101d3, + 0x04b607ac, + 0x000dd006, +/* 0x0022: rd32_wait */ + 0xd7f104bd, + 0xd4b607ac, + 0x00ddcf06, + 0x7000d4f1, + 0xf1f21bf4, + 0xb607a4d7, + 0xddcf06d4, +/* 0x003f: wr32 */ + 0xf100f800, + 0xb607a007, + 0x0ed00604, + 0xf104bd00, + 0xb607a407, + 0x0dd00604, + 0xf004bd00, + 0xd5f002d7, + 0x01d3f0f0, + 0x07ac07f1, + 0xd00604b6, + 0x04bd000d, +/* 0x006c: wr32_wait */ + 0x07acd7f1, + 0xcf06d4b6, + 0xd4f100dd, + 0x1bf47000, +/* 0x007f: nsec */ + 0xf900f8f2, + 0xf080f990, + 0x84b62c87, + 0x0088cf06, +/* 0x008c: nsec_loop */ + 0xb62c97f0, + 0x99cf0694, + 0x0298bb00, + 0xf4069eb8, + 0x80fcf11e, + 0x00f890fc, +/* 0x00a4: wait */ + 0x80f990f9, + 0xb62c87f0, + 0x88cf0684, +/* 0x00b1: wait_loop */ + 0x02eeb900, + 0xb90421f4, + 0xadfd02da, + 0x06acb804, + 0xf0150bf4, + 0x94b62c97, + 0x0099cf06, + 0xb80298bb, + 0x1ef4069b, +/* 0x00d5: wait_done */ + 0xfc80fcdf, +/* 0x00db: intr_watchdog */ + 0x9800f890, + 0x96b003e9, + 0x2a0bf400, + 0xbb9a0a98, + 0x1cf4029a, + 0x01d7f00f, + 0x02dd21f5, + 0x0ef494bd, +/* 0x00f9: intr_watchdog_next_time */ + 0x9b0a9815, + 0xf400a6b0, + 0x9ab8090b, + 0x061cf406, +/* 0x0108: intr_watchdog_next_time_set */ +/* 0x010b: intr_watchdog_next_proc */ + 0x809b0980, + 0xe0b603e9, + 0x68e6b158, + 0xc61bf402, +/* 0x011a: intr */ + 0x00f900f8, + 0x80f904bd, + 0xa0f990f9, + 0xc0f9b0f9, + 0xe0f9d0f9, + 0xf7f0f0f9, + 0x0188fe00, + 0x87f180f9, + 0x84b605d0, + 0x0088cf06, + 0xf10180b6, + 0xb605d007, + 0x08d00604, + 0xf004bd00, + 0x84b60887, + 0x0088cf06, + 0xf40289c4, + 0x0080230b, + 0x58e7f09b, + 0x98db21f4, + 0x96b09b09, + 0x110bf400, + 0xb63407f0, + 0x09d00604, + 0x8004bd00, +/* 0x017e: intr_skip_watchdog */ + 0x89e49a09, + 0x0bf40800, + 0x8897f148, + 0x0694b606, + 0xc40099cf, + 0x0bf4029a, + 0xc0c7f12c, + 0x06c4b604, + 0xf900cccf, + 0x48e7f1c0, + 0x53e3f14f, + 0x00d7f054, + 0x034221f5, + 0x07f1c0fc, + 0x04b604c0, + 0x000cd006, +/* 0x01be: intr_subintr_skip_fifo */ + 0x07f104bd, + 0x04b60688, + 0x0009d006, +/* 0x01ca: intr_skip_subintr */ + 0x89c404bd, + 0x070bf420, + 0xffbfa4f1, +/* 0x01d4: intr_skip_pause */ + 0xf44089c4, + 0xa4f1070b, +/* 0x01de: intr_skip_user0 */ + 0x07f0ffbf, + 0x0604b604, + 0xbd0008d0, + 0xfe80fc04, + 0xf0fc0088, + 0xd0fce0fc, + 0xb0fcc0fc, + 0x90fca0fc, + 0x00fc80fc, + 0xf80032f4, +/* 0x0205: ticks_from_ns */ + 0xf9c0f901, + 0xcbd7f1b0, + 0x00d3f000, + 0x041321f5, + 0x03e8ccec, + 0xf400b4b0, + 0xeeec120b, + 0xd7f103e8, + 0xd3f000cb, + 0x1321f500, +/* 0x022d: ticks_from_ns_quit */ + 0x02ceb904, + 0xc0fcb0fc, +/* 0x0236: ticks_from_us */ + 0xc0f900f8, + 0xd7f1b0f9, + 0xd3f000cb, + 0x1321f500, + 0x02ceb904, + 0xf400b4b0, + 0xe4bd050b, +/* 0x0250: ticks_from_us_quit */ + 0xc0fcb0fc, +/* 0x0256: ticks_to_us */ + 0xd7f100f8, + 0xd3f000cb, + 0xecedff00, +/* 0x0262: timer */ + 0x90f900f8, + 0x32f480f9, + 0x03f89810, + 0xf40086b0, + 0x84bd651c, + 0xb63807f0, + 0x08d00604, + 0xf004bd00, + 0x84b63487, + 0x0088cf06, + 0xbb9a0998, + 0xe9bb0298, + 0x03fe8000, + 0xb60887f0, + 0x88cf0684, + 0x0284f000, + 0xf0261bf4, + 0x84b63487, + 0x0088cf06, + 0xf406e0b8, + 0xe8b8090b, + 0x111cf406, +/* 0x02b8: timer_reset */ + 0xb63407f0, + 0x0ed00604, + 0x8004bd00, +/* 0x02c6: timer_enable */ + 0x87f09a0e, + 0x3807f001, + 0xd00604b6, + 0x04bd0008, +/* 0x02d4: timer_done */ + 0xfc1031f4, + 0xf890fc80, +/* 0x02dd: send_proc */ + 0xf980f900, + 0x05e89890, + 0xf004e998, + 0x89b80486, + 0x2a0bf406, + 0x940398c4, + 0x80b60488, + 0x008ebb18, + 0x8000fa98, + 0x8d80008a, + 0x028c8001, + 0xb6038b80, + 0x94f00190, + 0x04e98007, +/* 0x0317: send_done */ + 0xfc0231f4, + 0xf880fc90, +/* 0x031d: find */ + 0xf080f900, + 0x31f45887, +/* 0x0325: find_loop */ + 0x008a9801, + 0xf406aeb8, + 0x80b6100b, + 0x6886b158, + 0xf01bf402, +/* 0x033b: find_done */ + 0xb90132f4, + 0x80fc028e, +/* 0x0342: send */ + 0x21f500f8, + 0x01f4031d, +/* 0x034b: recv */ + 0xf900f897, + 0x9880f990, + 0xe99805e8, + 0x0132f404, + 0xf40689b8, + 0x89c43d0b, + 0x0180b603, + 0x800784f0, + 0xea9805e8, + 0xfef0f902, + 0xf0f9018f, + 0x9402efb9, + 0xe9bb0499, + 0x18e0b600, + 0x9803eb98, + 0xed9802ec, + 0x00ee9801, + 0xf0fca5f9, + 0xf400f8fe, + 0xf0fc0131, +/* 0x0398: recv_done */ + 0x90fc80fc, +/* 0x039e: init */ + 0x17f100f8, + 0x14b60108, + 0x0011cf06, + 0x010911e7, + 0xfe0814b6, + 0x17f10014, + 0x13f000e0, + 0x1c07f000, + 0xd00604b6, + 0x04bd0001, + 0xf0ff17f0, + 0x04b61407, + 0x0001d006, + 0x17f004bd, + 0x0015f102, + 0x1007f008, + 0xd00604b6, + 0x04bd0001, + 0x011a17f1, + 0xfe0013f0, + 0x31f40010, + 0x0117f010, + 0xb63807f0, + 0x01d00604, + 0xf004bd00, +/* 0x0402: init_proc */ + 0xf19858f7, + 0x0016b001, + 0xf9fa0bf4, + 0x58f0b615, +/* 0x0413: mulu32_32_64 */ + 0xf9f20ef4, + 0xf920f910, + 0x9540f930, + 0xd29510e1, + 0xbdc4bd10, + 0xc0edffb4, + 0xb9301dff, + 0x34f10234, + 0x34b6ffff, + 0x1045b610, + 0xbb00c3bb, + 0xe2ff01b4, + 0x0234b930, + 0xffff34f1, + 0xb61034b6, + 0xc3bb1045, + 0x01b4bb00, + 0xbb3012ff, + 0x40fc00b3, + 0x20fc30fc, + 0x00f810fc, +/* 0x0464: host_send */ + 0x04b017f1, + 0xcf0614b6, + 0x27f10011, + 0x24b604a0, + 0x0022cf06, + 0xf40612b8, + 0x1ec4320b, + 0x04ee9407, + 0x0270e0b7, + 0x9803eb98, + 0xed9802ec, + 0x00ee9801, + 0x034221f5, + 0xc40110b6, + 0x07f10f1e, + 0x04b604b0, + 0x000ed006, + 0x0ef404bd, +/* 0x04ad: host_send_done */ +/* 0x04af: host_recv */ + 0xf100f8ba, + 0xf14e4917, + 0xb8525413, + 0x0bf406e1, +/* 0x04bd: host_recv_wait */ + 0xcc17f1aa, + 0x0614b604, + 0xf10011cf, + 0xb604c827, + 0x22cf0624, + 0x0816f000, + 0xf40612b8, + 0x23c4e60b, + 0x0434b607, + 0x02f030b7, + 0x80033b80, + 0x3d80023c, + 0x003e8001, + 0xf00120b6, + 0x07f10f24, + 0x04b604c8, + 0x0002d006, + 0x27f004bd, + 0x0007f040, + 0xd00604b6, + 0x04bd0002, +/* 0x0512: host_init */ + 0x17f100f8, + 0x14b60080, + 0x7015f110, + 0xd007f102, + 0x0604b604, + 0xbd0001d0, + 0x8017f104, + 0x1014b600, + 0x02f015f1, + 0x04dc07f1, + 0xd00604b6, + 0x04bd0001, + 0xf10117f0, + 0xb604c407, + 0x01d00604, + 0xf804bd00, +/* 0x0551: memx_func_enter */ + 0x2067f100, + 0x5d77f116, + 0xff73f1f5, + 0x026eb9ff, + 0xb90421f4, + 0x87fd02d8, + 0xf960f904, + 0xfcd0fc80, + 0x3f21f4e0, + 0xfffe77f1, + 0xffff73f1, + 0xf4026eb9, + 0xd8b90421, + 0x0487fd02, + 0x80f960f9, + 0xe0fcd0fc, + 0xf13f21f4, + 0xb926f067, + 0x21f4026e, + 0x02d8b904, + 0xf90487fd, + 0xfc80f960, + 0xf4e0fcd0, + 0x67f03f21, + 0xe007f104, + 0x0604b607, + 0xbd0006d0, +/* 0x05bd: memx_func_enter_wait */ + 0xc067f104, + 0x0664b607, + 0xf00066cf, + 0x0bf40464, + 0x2c67f0f3, + 0xcf0664b6, + 0x06800066, +/* 0x05db: memx_func_leave */ + 0xf000f8f1, + 0x64b62c67, + 0x0066cf06, + 0xf0f20680, + 0x07f10467, + 0x04b607e4, + 0x0006d006, +/* 0x05f6: memx_func_leave_wait */ + 0x67f104bd, + 0x64b607c0, + 0x0066cf06, + 0xf40464f0, + 0x67f1f31b, + 0x77f126f0, + 0x73f00001, + 0x026eb900, + 0xb90421f4, + 0x87fd02d8, + 0xf960f905, + 0xfcd0fc80, + 0x3f21f4e0, + 0x162067f1, + 0xf4026eb9, + 0xd8b90421, + 0x0587fd02, + 0x80f960f9, + 0xe0fcd0fc, + 0xf13f21f4, + 0xf00aa277, + 0x6eb90073, + 0x0421f402, + 0xfd02d8b9, + 0x60f90587, + 0xd0fc80f9, + 0x21f4e0fc, +/* 0x0663: memx_func_wait_vblank */ + 0x9800f83f, + 0x66b00016, + 0x130bf400, + 0xf40166b0, + 0x0ef4060b, +/* 0x0675: memx_func_wait_vblank_head1 */ + 0x2077f12e, + 0x070ef400, +/* 0x067c: memx_func_wait_vblank_head0 */ + 0x000877f1, +/* 0x0680: memx_func_wait_vblank_0 */ + 0x07c467f1, + 0xcf0664b6, + 0x67fd0066, + 0xf31bf404, +/* 0x0690: memx_func_wait_vblank_1 */ + 0x07c467f1, + 0xcf0664b6, + 0x67fd0066, + 0xf30bf404, +/* 0x06a0: memx_func_wait_vblank_fini */ + 0xf80410b6, +/* 0x06a5: memx_func_wr32 */ + 0x00169800, + 0xb6011598, + 0x60f90810, + 0xd0fc50f9, + 0x21f4e0fc, + 0x0242b63f, + 0xf8e91bf4, +/* 0x06c1: memx_func_wait */ + 0x2c87f000, + 0xcf0684b6, + 0x1e980088, + 0x011d9800, + 0x98021c98, + 0x10b6031b, + 0xa421f410, +/* 0x06de: memx_func_delay */ + 0x1e9800f8, + 0x0410b600, + 0xf87f21f4, +/* 0x06e9: memx_func_train */ +/* 0x06eb: memx_exec */ + 0xf900f800, + 0xb9d0f9e0, + 0xb2b902c1, +/* 0x06f5: memx_exec_next */ + 0x00139802, + 0xe70410b6, + 0xe701f034, + 0xb601e033, + 0x30f00132, + 0xde35980c, + 0x12b855f9, + 0xe41ef406, + 0x98f10b98, + 0xcbbbf20c, + 0xc4b7f102, + 0x06b4b607, + 0xfc00bbcf, + 0xf5e0fcd0, + 0xf8034221, +/* 0x0731: memx_info */ + 0x01c67000, +/* 0x0737: memx_info_data */ + 0xf10e0bf4, + 0xf103ccc7, + 0xf40800b7, +/* 0x0742: memx_info_train */ + 0xc7f10b0e, + 0xb7f10bcc, +/* 0x074a: memx_info_send */ + 0x21f50100, + 0x00f80342, +/* 0x0750: memx_recv */ + 0xf401d6b0, + 0xd6b0980b, + 0xd80bf400, +/* 0x075e: memx_init */ + 0x00f800f8, +/* 0x0760: perf_recv */ +/* 0x0762: perf_init */ + 0x00f800f8, +/* 0x0764: i2c_drive_scl */ + 0xf40036b0, + 0x07f1110b, + 0x04b607e0, + 0x0001d006, + 0x00f804bd, +/* 0x0778: i2c_drive_scl_lo */ + 0x07e407f1, + 0xd00604b6, + 0x04bd0001, +/* 0x0786: i2c_drive_sda */ + 0x36b000f8, + 0x110bf400, + 0x07e007f1, + 0xd00604b6, + 0x04bd0002, +/* 0x079a: i2c_drive_sda_lo */ + 0x07f100f8, + 0x04b607e4, + 0x0002d006, + 0x00f804bd, +/* 0x07a8: i2c_sense_scl */ + 0xf10132f4, + 0xb607c437, + 0x33cf0634, + 0x0431fd00, + 0xf4060bf4, +/* 0x07be: i2c_sense_scl_done */ + 0x00f80131, +/* 0x07c0: i2c_sense_sda */ + 0xf10132f4, + 0xb607c437, + 0x33cf0634, + 0x0432fd00, + 0xf4060bf4, +/* 0x07d6: i2c_sense_sda_done */ + 0x00f80131, +/* 0x07d8: i2c_raise_scl */ + 0x47f140f9, + 0x37f00898, + 0x6421f501, +/* 0x07e5: i2c_raise_scl_wait */ + 0xe8e7f107, + 0x7f21f403, + 0x07a821f5, + 0xb60901f4, + 0x1bf40142, +/* 0x07f9: i2c_raise_scl_done */ + 0xf840fcef, +/* 0x07fd: i2c_start */ + 0xa821f500, + 0x0d11f407, + 0x07c021f5, + 0xf40611f4, +/* 0x080e: i2c_start_rep */ + 0x37f0300e, + 0x6421f500, + 0x0137f007, + 0x078621f5, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0xd821f550, + 0x0464b607, +/* 0x083b: i2c_start_send */ + 0xf01f11f4, + 0x21f50037, + 0xe7f10786, + 0x21f41388, + 0x0037f07f, + 0x076421f5, + 0x1388e7f1, +/* 0x0857: i2c_start_out */ + 0xf87f21f4, +/* 0x0859: i2c_stop */ + 0x0037f000, + 0x076421f5, + 0xf50037f0, + 0xf1078621, + 0xf403e8e7, + 0x37f07f21, + 0x6421f501, + 0x88e7f107, + 0x7f21f413, + 0xf50137f0, + 0xf1078621, + 0xf41388e7, + 0x00f87f21, +/* 0x088c: i2c_bitw */ + 0x078621f5, + 0x03e8e7f1, + 0xbb7f21f4, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x07d821f5, + 0xf40464b6, + 0xe7f11811, + 0x21f41388, + 0x0037f07f, + 0x076421f5, + 0x1388e7f1, +/* 0x08cb: i2c_bitw_out */ + 0xf87f21f4, +/* 0x08cd: i2c_bitr */ + 0x0137f000, + 0x078621f5, + 0x03e8e7f1, + 0xbb7f21f4, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x07d821f5, + 0xf40464b6, + 0x21f51b11, + 0x37f007c0, + 0x6421f500, + 0x88e7f107, + 0x7f21f413, + 0xf4013cf0, +/* 0x0912: i2c_bitr_done */ + 0x00f80131, +/* 0x0914: i2c_get_byte */ + 0xf00057f0, +/* 0x091a: i2c_get_byte_next */ + 0x54b60847, + 0x0076bb01, + 0xf90465b6, + 0x04659450, + 0xbd0256bb, + 0x0475fd50, + 0x21f550fc, + 0x64b608cd, + 0x2b11f404, + 0xb60553fd, + 0x1bf40142, + 0x0137f0d8, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0x8c21f550, + 0x0464b608, +/* 0x0964: i2c_get_byte_done */ +/* 0x0966: i2c_put_byte */ + 0x47f000f8, +/* 0x0969: i2c_put_byte_next */ + 0x0142b608, + 0xbb3854ff, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x088c21f5, + 0xf40464b6, + 0x46b03411, + 0xd81bf400, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0xcd21f550, + 0x0464b608, + 0xbb0f11f4, + 0x36b00076, + 0x061bf401, +/* 0x09bf: i2c_put_byte_done */ + 0xf80132f4, +/* 0x09c1: i2c_addr */ + 0x0076bb00, + 0xf90465b6, + 0x04659450, + 0xbd0256bb, + 0x0475fd50, + 0x21f550fc, + 0x64b607fd, + 0x2911f404, + 0x012ec3e7, + 0xfd0134b6, + 0x76bb0553, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0xf550fc04, + 0xb6096621, +/* 0x0a06: i2c_addr_done */ + 0x00f80464, +/* 0x0a08: i2c_acquire_addr */ + 0xb6f8cec7, + 0xe0b702e4, + 0xee980d1c, +/* 0x0a17: i2c_acquire */ + 0xf500f800, + 0xf40a0821, + 0xd9f00421, + 0x3f21f403, +/* 0x0a26: i2c_release */ + 0x21f500f8, + 0x21f40a08, + 0x03daf004, + 0xf83f21f4, +/* 0x0a35: i2c_recv */ + 0x0132f400, + 0xb6f8c1c7, + 0x16b00214, + 0x3a1ff528, + 0xf413a001, + 0x0032980c, + 0x0ccc13a0, + 0xf4003198, + 0xd0f90231, + 0xd0f9e0f9, + 0x000067f1, + 0x100063f1, + 0xbb016792, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x0a1721f5, + 0xfc0464b6, + 0x00d6b0d0, + 0x00b31bf5, + 0xbb0057f0, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x09c121f5, + 0xf50464b6, + 0xc700d011, + 0x76bbe0c5, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0xf550fc04, + 0xb6096621, + 0x11f50464, + 0x57f000ad, + 0x0076bb01, + 0xf90465b6, + 0x04659450, + 0xbd0256bb, + 0x0475fd50, + 0x21f550fc, + 0x64b609c1, + 0x8a11f504, + 0x0076bb00, + 0xf90465b6, + 0x04659450, + 0xbd0256bb, + 0x0475fd50, + 0x21f550fc, + 0x64b60914, + 0x6a11f404, + 0xbbe05bcb, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x085921f5, + 0xb90464b6, + 0x74bd025b, +/* 0x0b3b: i2c_recv_not_rd08 */ + 0xb0430ef4, + 0x1bf401d6, + 0x0057f03d, + 0x09c121f5, + 0xc73311f4, + 0x21f5e0c5, + 0x11f40966, + 0x0057f029, + 0x09c121f5, + 0xc71f11f4, + 0x21f5e0b5, + 0x11f40966, + 0x5921f515, + 0xc774bd08, + 0x1bf408c5, + 0x0232f409, +/* 0x0b7b: i2c_recv_not_wr08 */ +/* 0x0b7b: i2c_recv_done */ + 0xc7030ef4, + 0x21f5f8ce, + 0xe0fc0a26, + 0x12f4d0fc, + 0x027cb90a, + 0x034221f5, +/* 0x0b90: i2c_recv_exit */ +/* 0x0b92: i2c_init */ + 0x00f800f8, +/* 0x0b94: test_recv */ + 0x05d817f1, + 0xcf0614b6, + 0x10b60011, + 0xd807f101, + 0x0604b605, + 0xbd0001d0, + 0x00e7f104, + 0x4fe3f1d9, + 0x6221f513, +/* 0x0bbb: test_init */ + 0xf100f802, + 0xf50800e7, + 0xf8026221, +/* 0x0bc5: idle_recv */ +/* 0x0bc7: idle */ + 0xf400f800, + 0x17f10031, + 0x14b605d4, + 0x0011cf06, + 0xf10110b6, + 0xb605d407, + 0x01d00604, +/* 0x0be3: idle_loop */ + 0xf004bd00, + 0x32f45817, +/* 0x0be9: idle_proc */ +/* 0x0be9: idle_proc_exec */ + 0xb910f902, + 0x21f5021e, + 0x10fc034b, + 0xf40911f4, + 0x0ef40231, +/* 0x0bfd: idle_proc_next */ + 0x5810b6ef, + 0xf4061fb8, + 0x02f4e61b, + 0x0028f4dd, + 0x00bb0ef4, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 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/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf110.fuc4 b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf110.fuc4 new file mode 100644 index 000000000..ae9c3f18a --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf110.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 #gf110_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 #gf110_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/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf110.fuc4.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf110.fuc4.h new file mode 100644 index 000000000..a0c499e45 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf110.fuc4.h @@ -0,0 +1,1795 @@ +uint32_t gf110_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, + 0x0000049d, + 0x00000446, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x584d454d, + 0x0000068b, + 0x0000067d, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x46524550, + 0x0000068f, + 0x0000068d, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x5f433249, + 0x00000aaa, + 0x0000094d, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x54534554, + 0x00000acd, + 0x00000aac, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x454c4449, + 0x00000ad9, + 0x00000ad7, + 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, + 0x000004d3, +/* 0x037c: memx_func_next */ + 0x00000002, + 0x00000000, + 0x00000554, + 0x00000003, + 0x00000002, + 0x000005d8, + 0x00040004, + 0x00000000, + 0x000005f4, + 0x00010005, + 0x00000000, + 0x0000060e, + 0x00010006, + 0x00000000, + 0x000005d3, + 0x00000007, + 0x00000000, + 0x00000619, +/* 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, +}; + +uint32_t gf110_pmu_code[] = { + 0x034d0ef5, +/* 0x0004: rd32 */ + 0x07a007f1, + 0xbd000ed0, + 0x01d7f004, + 0xf101d3f0, + 0xd007ac07, + 0x04bd000d, +/* 0x001c: rd32_wait */ + 0x07acd7f1, + 0xf100ddcf, + 0xf47000d4, + 0xd7f1f51b, + 0xddcf07a4, +/* 0x0033: wr32 */ + 0xf100f800, + 0xd007a007, + 0x04bd000e, + 0x07a407f1, + 0xbd000dd0, + 0x02d7f004, + 0xf0f0d5f0, + 0x07f101d3, + 0x0dd007ac, +/* 0x0057: wr32_wait */ + 0xf104bd00, + 0xcf07acd7, + 0xd4f100dd, + 0x1bf47000, +/* 0x0067: nsec */ + 0xf900f8f5, + 0xf080f990, + 0x88cf2c87, +/* 0x0071: nsec_loop */ + 0x2c97f000, + 0xbb0099cf, + 0x9eb80298, + 0xf41ef406, + 0x90fc80fc, +/* 0x0086: wait */ + 0x90f900f8, + 0x87f080f9, + 0x0088cf2c, +/* 0x0090: wait_loop */ + 0xf402eeb9, + 0xdab90421, + 0x04adfd02, + 0xf406acb8, + 0x97f0120b, + 0x0099cf2c, + 0xb80298bb, + 0x1ef4069b, +/* 0x00b1: wait_done */ + 0xfc80fce2, +/* 0x00b7: intr_watchdog */ + 0x9800f890, + 0x96b003e9, + 0x2a0bf400, + 0xbb9a0a98, + 0x1cf4029a, + 0x01d7f00f, + 0x028c21f5, + 0x0ef494bd, +/* 0x00d5: intr_watchdog_next_time */ + 0x9b0a9815, + 0xf400a6b0, + 0x9ab8090b, + 0x061cf406, +/* 0x00e4: intr_watchdog_next_time_set */ +/* 0x00e7: intr_watchdog_next_proc */ + 0x809b0980, + 0xe0b603e9, + 0x68e6b158, + 0xc61bf402, +/* 0x00f6: intr */ + 0x00f900f8, + 0x80f904bd, + 0xa0f990f9, + 0xc0f9b0f9, + 0xe0f9d0f9, + 0xf7f0f0f9, + 0x0188fe00, + 0x87f180f9, + 0x88cf05d0, + 0x0180b600, + 0x05d007f1, + 0xbd0008d0, + 0x0887f004, + 0xc40088cf, + 0x0bf40289, + 0x9b008020, + 0xf458e7f0, + 0x0998b721, + 0x0096b09b, + 0xf00e0bf4, + 0x09d03407, + 0x8004bd00, +/* 0x014e: intr_skip_watchdog */ + 0x89e49a09, + 0x0bf40800, + 0x8897f13c, + 0x0099cf06, + 0xf4029ac4, + 0xc7f1260b, + 0xcccf04c0, + 0xf1c0f900, + 0xf14f48e7, + 0xf05453e3, + 0x21f500d7, + 0xc0fc02f1, + 0x04c007f1, + 0xbd000cd0, +/* 0x0185: intr_subintr_skip_fifo */ + 0x8807f104, + 0x0009d006, +/* 0x018e: intr_skip_subintr */ + 0x89c404bd, + 0x070bf420, + 0xffbfa4f1, +/* 0x0198: intr_skip_pause */ + 0xf44089c4, + 0xa4f1070b, +/* 0x01a2: intr_skip_user0 */ + 0x07f0ffbf, + 0x0008d004, + 0x80fc04bd, + 0xfc0088fe, + 0xfce0fcf0, + 0xfcc0fcd0, + 0xfca0fcb0, + 0xfc80fc90, + 0x0032f400, +/* 0x01c6: ticks_from_ns */ + 0xc0f901f8, + 0xd7f1b0f9, + 0xd3f00144, + 0xb321f500, + 0xe8ccec03, + 0x00b4b003, + 0xec120bf4, + 0xf103e8ee, + 0xf00144d7, + 0x21f500d3, +/* 0x01ee: ticks_from_ns_quit */ + 0xceb903b3, + 0xfcb0fc02, +/* 0x01f7: ticks_from_us */ + 0xf900f8c0, + 0xf1b0f9c0, + 0xf00144d7, + 0x21f500d3, + 0xceb903b3, + 0x00b4b002, + 0xbd050bf4, +/* 0x0211: ticks_from_us_quit */ + 0xfcb0fce4, +/* 0x0217: ticks_to_us */ + 0xf100f8c0, + 0xf00144d7, + 0xedff00d3, +/* 0x0223: timer */ + 0xf900f8ec, + 0xf480f990, + 0xf8981032, + 0x0086b003, + 0xbd531cf4, + 0x3807f084, + 0xbd0008d0, + 0x3487f004, + 0x980088cf, + 0x98bb9a09, + 0x00e9bb02, + 0xf003fe80, + 0x88cf0887, + 0x0284f000, + 0xf0201bf4, + 0x88cf3487, + 0x06e0b800, + 0xb8090bf4, + 0x1cf406e8, +/* 0x026d: timer_reset */ + 0x3407f00e, + 0xbd000ed0, + 0x9a0e8004, +/* 0x0278: timer_enable */ + 0xf00187f0, + 0x08d03807, +/* 0x0283: timer_done */ + 0xf404bd00, + 0x80fc1031, + 0x00f890fc, +/* 0x028c: send_proc */ + 0x90f980f9, + 0x9805e898, + 0x86f004e9, + 0x0689b804, + 0xc42a0bf4, + 0x88940398, + 0x1880b604, + 0x98008ebb, + 0x8a8000fa, + 0x018d8000, + 0x80028c80, + 0x90b6038b, + 0x0794f001, + 0xf404e980, +/* 0x02c6: send_done */ + 0x90fc0231, + 0x00f880fc, +/* 0x02cc: find */ + 0x87f080f9, + 0x0131f458, +/* 0x02d4: find_loop */ + 0xb8008a98, + 0x0bf406ae, + 0x5880b610, + 0x026886b1, + 0xf4f01bf4, +/* 0x02ea: find_done */ + 0x8eb90132, + 0xf880fc02, +/* 0x02f1: send */ + 0xcc21f500, + 0x9701f402, +/* 0x02fa: recv */ + 0x90f900f8, + 0xe89880f9, + 0x04e99805, + 0xb80132f4, + 0x0bf40689, + 0x0389c43d, + 0xf00180b6, + 0xe8800784, + 0x02ea9805, + 0x8ffef0f9, + 0xb9f0f901, + 0x999402ef, + 0x00e9bb04, + 0x9818e0b6, + 0xec9803eb, + 0x01ed9802, + 0xf900ee98, + 0xfef0fca5, + 0x31f400f8, +/* 0x0347: recv_done */ + 0xfcf0fc01, + 0xf890fc80, +/* 0x034d: init */ + 0x0817f100, + 0x0011cf01, + 0x010911e7, + 0xfe0814b6, + 0x17f10014, + 0x13f000e0, + 0x1c07f000, + 0xbd0001d0, + 0xff17f004, + 0xd01407f0, + 0x04bd0001, + 0xf10217f0, + 0xf0080015, + 0x01d01007, + 0xf104bd00, + 0xf000f617, + 0x10fe0013, + 0x1031f400, + 0xf00117f0, + 0x01d03807, + 0xf004bd00, +/* 0x03a2: init_proc */ + 0xf19858f7, + 0x0016b001, + 0xf9fa0bf4, + 0x58f0b615, +/* 0x03b3: mulu32_32_64 */ + 0xf9f20ef4, + 0xf920f910, + 0x9540f930, + 0xd29510e1, + 0xbdc4bd10, + 0xc0edffb4, + 0xb9301dff, + 0x34f10234, + 0x34b6ffff, + 0x1045b610, + 0xbb00c3bb, + 0xe2ff01b4, + 0x0234b930, + 0xffff34f1, + 0xb61034b6, + 0xc3bb1045, + 0x01b4bb00, + 0xbb3012ff, + 0x40fc00b3, + 0x20fc30fc, + 0x00f810fc, +/* 0x0404: host_send */ + 0x04b017f1, + 0xf10011cf, + 0xcf04a027, + 0x12b80022, + 0x2f0bf406, + 0x94071ec4, + 0xe0b704ee, + 0xeb980270, + 0x02ec9803, + 0x9801ed98, + 0x21f500ee, + 0x10b602f1, + 0x0f1ec401, + 0x04b007f1, + 0xbd000ed0, + 0xc30ef404, +/* 0x0444: host_send_done */ +/* 0x0446: host_recv */ + 0x17f100f8, + 0x13f14e49, + 0xe1b85254, + 0xb30bf406, +/* 0x0454: host_recv_wait */ + 0x04cc17f1, + 0xf10011cf, + 0xcf04c827, + 0x16f00022, + 0x0612b808, + 0xc4ec0bf4, + 0x34b60723, + 0xf030b704, + 0x033b8002, + 0x80023c80, + 0x3e80013d, + 0x0120b600, + 0xf10f24f0, + 0xd004c807, + 0x04bd0002, + 0xf04027f0, + 0x02d00007, + 0xf804bd00, +/* 0x049d: host_init */ + 0x8017f100, + 0x1014b600, + 0x027015f1, + 0x04d007f1, + 0xbd0001d0, + 0x8017f104, + 0x1014b600, + 0x02f015f1, + 0x04dc07f1, + 0xbd0001d0, + 0x0117f004, + 0x04c407f1, + 0xbd0001d0, +/* 0x04d3: memx_func_enter */ + 0xf100f804, + 0xf1162067, + 0xf1f55d77, + 0xb9ffff73, + 0x21f4026e, + 0x02d8b904, + 0xf90487fd, + 0xfc80f960, + 0xf4e0fcd0, + 0x77f13321, + 0x73f1fffe, + 0x6eb9ffff, + 0x0421f402, + 0xfd02d8b9, + 0x60f90487, + 0xd0fc80f9, + 0x21f4e0fc, + 0xf067f133, + 0x026eb926, + 0xb90421f4, + 0x87fd02d8, + 0xf960f904, + 0xfcd0fc80, + 0x3321f4e0, + 0xf10467f0, + 0xd007e007, + 0x04bd0006, +/* 0x053c: memx_func_enter_wait */ + 0x07c067f1, + 0xf00066cf, + 0x0bf40464, + 0x2c67f0f6, + 0x800066cf, + 0x00f8f106, +/* 0x0554: memx_func_leave */ + 0xcf2c67f0, + 0x06800066, + 0x0467f0f2, + 0x07e407f1, + 0xbd0006d0, +/* 0x0569: memx_func_leave_wait */ + 0xc067f104, + 0x0066cf07, + 0xf40464f0, + 0x67f1f61b, + 0x77f126f0, + 0x73f00001, + 0x026eb900, + 0xb90421f4, + 0x87fd02d8, + 0xf960f905, + 0xfcd0fc80, + 0x3321f4e0, + 0x162067f1, + 0xf4026eb9, + 0xd8b90421, + 0x0587fd02, + 0x80f960f9, + 0xe0fcd0fc, + 0xf13321f4, + 0xf00aa277, + 0x6eb90073, + 0x0421f402, + 0xfd02d8b9, + 0x60f90587, + 0xd0fc80f9, + 0x21f4e0fc, +/* 0x05d3: memx_func_wait_vblank */ + 0xb600f833, + 0x00f80410, +/* 0x05d8: memx_func_wr32 */ + 0x98001698, + 0x10b60115, + 0xf960f908, + 0xfcd0fc50, + 0x3321f4e0, + 0xf40242b6, + 0x00f8e91b, +/* 0x05f4: memx_func_wait */ + 0xcf2c87f0, + 0x1e980088, + 0x011d9800, + 0x98021c98, + 0x10b6031b, + 0x8621f410, +/* 0x060e: memx_func_delay */ + 0x1e9800f8, + 0x0410b600, + 0xf86721f4, +/* 0x0619: memx_func_train */ +/* 0x061b: memx_exec */ + 0xf900f800, + 0xb9d0f9e0, + 0xb2b902c1, +/* 0x0625: memx_exec_next */ + 0x00139802, + 0xe70410b6, + 0xe701f034, + 0xb601e033, + 0x30f00132, + 0xde35980c, + 0x12b855f9, + 0xe41ef406, + 0x98f10b98, + 0xcbbbf20c, + 0xc4b7f102, + 0x00bbcf07, + 0xe0fcd0fc, + 0x02f121f5, +/* 0x065e: memx_info */ + 0xc67000f8, + 0x0e0bf401, +/* 0x0664: memx_info_data */ + 0x03ccc7f1, + 0x0800b7f1, +/* 0x066f: memx_info_train */ + 0xf10b0ef4, + 0xf10bccc7, +/* 0x0677: memx_info_send */ + 0xf50100b7, + 0xf802f121, +/* 0x067d: memx_recv */ + 0x01d6b000, + 0xb09b0bf4, + 0x0bf400d6, +/* 0x068b: memx_init */ + 0xf800f8d8, +/* 0x068d: perf_recv */ +/* 0x068f: perf_init */ + 0xf800f800, +/* 0x0691: i2c_drive_scl */ + 0x0036b000, + 0xf10e0bf4, + 0xd007e007, + 0x04bd0001, +/* 0x06a2: i2c_drive_scl_lo */ + 0x07f100f8, + 0x01d007e4, + 0xf804bd00, +/* 0x06ad: i2c_drive_sda */ + 0x0036b000, + 0xf10e0bf4, + 0xd007e007, + 0x04bd0002, +/* 0x06be: i2c_drive_sda_lo */ + 0x07f100f8, + 0x02d007e4, + 0xf804bd00, +/* 0x06c9: i2c_sense_scl */ + 0x0132f400, + 0x07c437f1, + 0xfd0033cf, + 0x0bf40431, + 0x0131f406, +/* 0x06dc: i2c_sense_scl_done */ +/* 0x06de: i2c_sense_sda */ + 0x32f400f8, + 0xc437f101, + 0x0033cf07, + 0xf40432fd, + 0x31f4060b, +/* 0x06f1: i2c_sense_sda_done */ +/* 0x06f3: i2c_raise_scl */ + 0xf900f801, + 0x9847f140, + 0x0137f008, + 0x069121f5, +/* 0x0700: i2c_raise_scl_wait */ + 0x03e8e7f1, + 0xf56721f4, + 0xf406c921, + 0x42b60901, + 0xef1bf401, +/* 0x0714: i2c_raise_scl_done */ + 0x00f840fc, +/* 0x0718: i2c_start */ + 0x06c921f5, + 0xf50d11f4, + 0xf406de21, + 0x0ef40611, +/* 0x0729: i2c_start_rep */ + 0x0037f030, + 0x069121f5, + 0xf50137f0, + 0xbb06ad21, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x06f321f5, + 0xf40464b6, +/* 0x0756: i2c_start_send */ + 0x37f01f11, + 0xad21f500, + 0x88e7f106, + 0x6721f413, + 0xf50037f0, + 0xf1069121, + 0xf41388e7, +/* 0x0772: i2c_start_out */ + 0x00f86721, +/* 0x0774: i2c_stop */ + 0xf50037f0, + 0xf0069121, + 0x21f50037, + 0xe7f106ad, + 0x21f403e8, + 0x0137f067, + 0x069121f5, + 0x1388e7f1, + 0xf06721f4, + 0x21f50137, + 0xe7f106ad, + 0x21f41388, +/* 0x07a7: i2c_bitw */ + 0xf500f867, + 0xf106ad21, + 0xf403e8e7, + 0x76bb6721, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0xf550fc04, + 0xb606f321, + 0x11f40464, + 0x88e7f118, + 0x6721f413, + 0xf50037f0, + 0xf1069121, + 0xf41388e7, +/* 0x07e6: i2c_bitw_out */ + 0x00f86721, +/* 0x07e8: i2c_bitr */ + 0xf50137f0, + 0xf106ad21, + 0xf403e8e7, + 0x76bb6721, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0xf550fc04, + 0xb606f321, + 0x11f40464, + 0xde21f51b, + 0x0037f006, + 0x069121f5, + 0x1388e7f1, + 0xf06721f4, + 0x31f4013c, +/* 0x082d: i2c_bitr_done */ +/* 0x082f: i2c_get_byte */ + 0xf000f801, + 0x47f00057, +/* 0x0835: i2c_get_byte_next */ + 0x0154b608, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0xe821f550, + 0x0464b607, + 0xfd2b11f4, + 0x42b60553, + 0xd81bf401, + 0xbb0137f0, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x07a721f5, +/* 0x087f: i2c_get_byte_done */ + 0xf80464b6, +/* 0x0881: i2c_put_byte */ + 0x0847f000, +/* 0x0884: i2c_put_byte_next */ + 0xff0142b6, + 0x76bb3854, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0xf550fc04, + 0xb607a721, + 0x11f40464, + 0x0046b034, + 0xbbd81bf4, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x07e821f5, + 0xf40464b6, + 0x76bb0f11, + 0x0136b000, + 0xf4061bf4, +/* 0x08da: i2c_put_byte_done */ + 0x00f80132, +/* 0x08dc: i2c_addr */ + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0x1821f550, + 0x0464b607, + 0xe72911f4, + 0xb6012ec3, + 0x53fd0134, + 0x0076bb05, + 0xf90465b6, + 0x04659450, + 0xbd0256bb, + 0x0475fd50, + 0x21f550fc, + 0x64b60881, +/* 0x0921: i2c_addr_done */ +/* 0x0923: i2c_acquire_addr */ + 0xc700f804, + 0xe4b6f8ce, + 0x14e0b705, +/* 0x092f: i2c_acquire */ + 0xf500f8d0, + 0xf4092321, + 0xd9f00421, + 0x3321f403, +/* 0x093e: i2c_release */ + 0x21f500f8, + 0x21f40923, + 0x03daf004, + 0xf83321f4, +/* 0x094d: i2c_recv */ + 0x0132f400, + 0xb6f8c1c7, + 0x16b00214, + 0x3a1ff528, + 0xf413a001, + 0x0032980c, + 0x0ccc13a0, + 0xf4003198, + 0xd0f90231, + 0xd0f9e0f9, + 0x000067f1, + 0x100063f1, + 0xbb016792, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x092f21f5, + 0xfc0464b6, + 0x00d6b0d0, + 0x00b31bf5, + 0xbb0057f0, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x08dc21f5, + 0xf50464b6, + 0xc700d011, + 0x76bbe0c5, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0xf550fc04, + 0xb6088121, + 0x11f50464, + 0x57f000ad, + 0x0076bb01, + 0xf90465b6, + 0x04659450, + 0xbd0256bb, + 0x0475fd50, + 0x21f550fc, + 0x64b608dc, + 0x8a11f504, + 0x0076bb00, + 0xf90465b6, + 0x04659450, + 0xbd0256bb, + 0x0475fd50, + 0x21f550fc, + 0x64b6082f, + 0x6a11f404, + 0xbbe05bcb, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x077421f5, + 0xb90464b6, + 0x74bd025b, +/* 0x0a53: i2c_recv_not_rd08 */ + 0xb0430ef4, + 0x1bf401d6, + 0x0057f03d, + 0x08dc21f5, + 0xc73311f4, + 0x21f5e0c5, + 0x11f40881, + 0x0057f029, + 0x08dc21f5, + 0xc71f11f4, + 0x21f5e0b5, + 0x11f40881, + 0x7421f515, + 0xc774bd07, + 0x1bf408c5, + 0x0232f409, +/* 0x0a93: i2c_recv_not_wr08 */ +/* 0x0a93: i2c_recv_done */ + 0xc7030ef4, + 0x21f5f8ce, + 0xe0fc093e, + 0x12f4d0fc, + 0x027cb90a, + 0x02f121f5, +/* 0x0aa8: i2c_recv_exit */ +/* 0x0aaa: i2c_init */ + 0x00f800f8, +/* 0x0aac: test_recv */ + 0x05d817f1, + 0xb60011cf, + 0x07f10110, + 0x01d005d8, + 0xf104bd00, + 0xf1d900e7, + 0xf5134fe3, + 0xf8022321, +/* 0x0acd: test_init */ + 0x00e7f100, + 0x2321f508, +/* 0x0ad7: idle_recv */ + 0xf800f802, +/* 0x0ad9: idle */ + 0x0031f400, + 0x05d417f1, + 0xb60011cf, + 0x07f10110, + 0x01d005d4, +/* 0x0aef: idle_loop */ + 0xf004bd00, + 0x32f45817, +/* 0x0af5: idle_proc */ +/* 0x0af5: idle_proc_exec */ + 0xb910f902, + 0x21f5021e, + 0x10fc02fa, + 0xf40911f4, + 0x0ef40231, +/* 0x0b09: 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, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gk208.fuc5 b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gk208.fuc5 new file mode 100644 index 000000000..093dc8188 --- /dev/null +++ b/kernel/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/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gk208.fuc5.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gk208.fuc5.h new file mode 100644 index 000000000..fe4f63dee --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gk208.fuc5.h @@ -0,0 +1,1731 @@ +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, + 0x00000453, + 0x00000404, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x584d454d, + 0x0000062d, + 0x0000061f, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x46524550, + 0x00000631, + 0x0000062f, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x5f433249, + 0x00000a35, + 0x000008dc, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x54534554, + 0x00000a56, + 0x00000a37, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x454c4449, + 0x00000a61, + 0x00000a5f, + 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, + 0x00000483, +/* 0x037c: memx_func_next */ + 0x00000002, + 0x00000000, + 0x00000500, + 0x00000003, + 0x00000002, + 0x00000580, + 0x00040004, + 0x00000000, + 0x0000059d, + 0x00010005, + 0x00000000, + 0x000005b7, + 0x00010006, + 0x00000000, + 0x0000057b, + 0x00000007, + 0x00000000, + 0x000005c3, +/* 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, +}; + +uint32_t gk208_pmu_code[] = { + 0x031c0ef5, +/* 0x0004: rd32 */ + 0xf607a040, + 0x04bd000e, + 0xd3f0010d, + 0x07ac4001, + 0xbd000df6, +/* 0x0019: rd32_wait */ + 0x07ac4d04, + 0xf100ddcf, + 0xf47000d4, + 0xa44df61b, + 0x00ddcf07, +/* 0x002e: wr32 */ + 0xa04000f8, + 0x000ef607, + 0xa44004bd, + 0x000df607, + 0x020d04bd, + 0xf0f0d5f0, + 0xac4001d3, + 0x000df607, +/* 0x004e: wr32_wait */ + 0xac4d04bd, + 0x00ddcf07, + 0x7000d4f1, + 0xf8f61bf4, +/* 0x005d: nsec */ + 0xf990f900, + 0xcf2c0880, +/* 0x0066: nsec_loop */ + 0x2c090088, + 0xbb0099cf, + 0x9ea60298, + 0xfcf61ef4, + 0xf890fc80, +/* 0x0079: wait */ + 0xf990f900, + 0xcf2c0880, +/* 0x0082: wait_loop */ + 0xeeb20088, + 0x0000047e, + 0xadfddab2, + 0xf4aca604, + 0x2c09100b, + 0xbb0099cf, + 0x9ba60298, +/* 0x009f: wait_done */ + 0xfce61ef4, + 0xf890fc80, +/* 0x00a5: intr_watchdog */ + 0x03e99800, + 0xf40096b0, + 0x0a98280b, + 0x029abb9a, + 0x0d0e1cf4, + 0x02617e01, + 0xf494bd00, +/* 0x00c2: intr_watchdog_next_time */ + 0x0a98140e, + 0x00a6b09b, + 0xa6080bf4, + 0x061cf49a, +/* 0x00d0: intr_watchdog_next_time_set */ +/* 0x00d3: intr_watchdog_next_proc */ + 0xb59b09b5, + 0xe0b603e9, + 0x68e6b158, + 0xc81bf402, +/* 0x00e2: intr */ + 0x00f900f8, + 0x80f904bd, + 0xa0f990f9, + 0xc0f9b0f9, + 0xe0f9d0f9, + 0x000ff0f9, + 0xf90188fe, + 0x04504880, + 0xb60088cf, + 0x50400180, + 0x0008f604, + 0x080804bd, + 0xc40088cf, + 0x0bf40289, + 0x9b00b51f, + 0xa57e580e, + 0x09980000, + 0x0096b09b, + 0x000d0bf4, + 0x0009f634, + 0x09b504bd, +/* 0x0135: intr_skip_watchdog */ + 0x0089e49a, + 0x360bf408, + 0xcf068849, + 0x9ac40099, + 0x220bf402, + 0xcf04c04c, + 0xc0f900cc, + 0xf14f484e, + 0x0d5453e3, + 0x02c27e00, + 0x40c0fc00, + 0x0cf604c0, +/* 0x0167: intr_subintr_skip_fifo */ + 0x4004bd00, + 0x09f60688, +/* 0x016f: intr_skip_subintr */ + 0xc404bd00, + 0x0bf42089, + 0xbfa4f107, +/* 0x0179: intr_skip_pause */ + 0x4089c4ff, + 0xf1070bf4, +/* 0x0183: intr_skip_user0 */ + 0x00ffbfa4, + 0x0008f604, + 0x80fc04bd, + 0xfc0088fe, + 0xfce0fcf0, + 0xfcc0fcd0, + 0xfca0fcb0, + 0xfc80fc90, + 0x0032f400, +/* 0x01a6: ticks_from_ns */ + 0xc0f901f8, + 0xd7f1b0f9, + 0xd3f00144, + 0x7721f500, + 0xe8ccec03, + 0x00b4b003, + 0xec120bf4, + 0xf103e8ee, + 0xf00144d7, + 0x21f500d3, +/* 0x01ce: ticks_from_ns_quit */ + 0xceb20377, + 0xc0fcb0fc, +/* 0x01d6: ticks_from_us */ + 0xc0f900f8, + 0xd7f1b0f9, + 0xd3f00144, + 0x7721f500, + 0xb0ceb203, + 0x0bf400b4, +/* 0x01ef: ticks_from_us_quit */ + 0xfce4bd05, + 0xf8c0fcb0, +/* 0x01f5: ticks_to_us */ + 0x44d7f100, + 0x00d3f001, + 0xf8ecedff, +/* 0x0201: timer */ + 0xf990f900, + 0x1032f480, + 0xb003f898, + 0x1cf40086, + 0x0084bd4a, + 0x0008f638, + 0x340804bd, + 0x980088cf, + 0x98bb9a09, + 0x00e9bb02, + 0x0803feb5, + 0x0088cf08, + 0xf40284f0, + 0x34081c1b, + 0xa60088cf, + 0x080bf4e0, + 0x1cf4e8a6, +/* 0x0245: timer_reset */ + 0xf634000d, + 0x04bd000e, +/* 0x024f: timer_enable */ + 0x089a0eb5, + 0xf6380001, + 0x04bd0008, +/* 0x0258: timer_done */ + 0xfc1031f4, + 0xf890fc80, +/* 0x0261: send_proc */ + 0xf980f900, + 0x05e89890, + 0xf004e998, + 0x89a60486, + 0xc42a0bf4, + 0x88940398, + 0x1880b604, + 0x98008ebb, + 0x8ab500fa, + 0x018db500, + 0xb5028cb5, + 0x90b6038b, + 0x0794f001, + 0xf404e9b5, +/* 0x029a: send_done */ + 0x90fc0231, + 0x00f880fc, +/* 0x02a0: find */ + 0x580880f9, +/* 0x02a7: find_loop */ + 0x980131f4, + 0xaea6008a, + 0xb6100bf4, + 0x86b15880, + 0x1bf40268, + 0x0132f4f1, +/* 0x02bc: find_done */ + 0x80fc8eb2, +/* 0x02c2: send */ + 0xa07e00f8, + 0x01f40002, +/* 0x02cb: recv */ + 0xf900f89b, + 0x9880f990, + 0xe99805e8, + 0x0132f404, + 0x0bf489a6, + 0x0389c43c, + 0xf00180b6, + 0xe8b50784, + 0x02ea9805, + 0x8ffef0f9, + 0xb2f0f901, + 0x049994ef, + 0xb600e9bb, + 0xeb9818e0, + 0x02ec9803, + 0x9801ed98, + 0xa5f900ee, + 0xf8fef0fc, + 0x0131f400, +/* 0x0316: recv_done */ + 0x80fcf0fc, + 0x00f890fc, +/* 0x031c: init */ + 0xcf010841, + 0x11e70011, + 0x14b60109, + 0x0014fe08, + 0xf000e041, + 0x1c000013, + 0xbd0001f6, + 0x00ff0104, + 0x0001f614, + 0x020104bd, + 0x080015f1, + 0x01f61000, + 0x4104bd00, + 0x13f000e2, + 0x0010fe00, + 0x011031f4, + 0xf6380001, + 0x04bd0001, +/* 0x0366: init_proc */ + 0xf198580f, + 0x0016b001, + 0xf9fa0bf4, + 0x58f0b615, +/* 0x0377: mulu32_32_64 */ + 0xf9f20ef4, + 0xf920f910, + 0x9540f930, + 0xd29510e1, + 0xbdc4bd10, + 0xc0edffb4, + 0xb2301dff, + 0xff34f134, + 0x1034b6ff, + 0xbb1045b6, + 0xb4bb00c3, + 0x30e2ff01, + 0x34f134b2, + 0x34b6ffff, + 0x1045b610, + 0xbb00c3bb, + 0x12ff01b4, + 0x00b3bb30, + 0x30fc40fc, + 0x10fc20fc, +/* 0x03c6: host_send */ + 0xb04100f8, + 0x0011cf04, + 0xcf04a042, + 0x12a60022, + 0xc42e0bf4, + 0xee94071e, + 0x70e0b704, + 0x03eb9802, + 0x9802ec98, + 0xee9801ed, + 0x02c27e00, + 0x0110b600, + 0x400f1ec4, + 0x0ef604b0, + 0xf404bd00, +/* 0x0402: host_send_done */ + 0x00f8c70e, +/* 0x0404: host_recv */ + 0xf14e4941, + 0xa6525413, + 0xb90bf4e1, +/* 0x0410: host_recv_wait */ + 0xcf04cc41, + 0xc8420011, + 0x0022cf04, + 0xa60816f0, + 0xef0bf412, + 0xb60723c4, + 0x30b70434, + 0x3bb502f0, + 0x023cb503, + 0xb5013db5, + 0x20b6003e, + 0x0f24f001, + 0xf604c840, + 0x04bd0002, + 0x00004002, + 0xbd0002f6, +/* 0x0453: host_init */ + 0x4100f804, + 0x14b60080, + 0x7015f110, + 0x04d04002, + 0xbd0001f6, + 0x00804104, + 0xf11014b6, + 0x4002f015, + 0x01f604dc, + 0x0104bd00, + 0x04c44001, + 0xbd0001f6, +/* 0x0483: memx_func_enter */ + 0xf100f804, + 0xf1162067, + 0xf1f55d77, + 0xb2ffff73, + 0x00047e6e, + 0xfdd8b200, + 0x60f90487, + 0xd0fc80f9, + 0x2e7ee0fc, + 0x77f10000, + 0x73f1fffe, + 0x6eb2ffff, + 0x0000047e, + 0x87fdd8b2, + 0xf960f904, + 0xfcd0fc80, + 0x002e7ee0, + 0xf067f100, + 0x7e6eb226, + 0xb2000004, + 0x0487fdd8, + 0x80f960f9, + 0xe0fcd0fc, + 0x00002e7e, + 0xe0400406, + 0x0006f607, +/* 0x04ea: memx_func_enter_wait */ + 0xc04604bd, + 0x0066cf07, + 0xf40464f0, + 0x2c06f70b, + 0xb50066cf, + 0x00f8f106, +/* 0x0500: memx_func_leave */ + 0x66cf2c06, + 0xf206b500, + 0xe4400406, + 0x0006f607, +/* 0x0512: memx_func_leave_wait */ + 0xc04604bd, + 0x0066cf07, + 0xf40464f0, + 0x67f1f71b, + 0x77f126f0, + 0x73f00001, + 0x7e6eb200, + 0xb2000004, + 0x0587fdd8, + 0x80f960f9, + 0xe0fcd0fc, + 0x00002e7e, + 0x162067f1, + 0x047e6eb2, + 0xd8b20000, + 0xf90587fd, + 0xfc80f960, + 0x7ee0fcd0, + 0xf100002e, + 0xf00aa277, + 0x6eb20073, + 0x0000047e, + 0x87fdd8b2, + 0xf960f905, + 0xfcd0fc80, + 0x002e7ee0, +/* 0x057b: memx_func_wait_vblank */ + 0xb600f800, + 0x00f80410, +/* 0x0580: memx_func_wr32 */ + 0x98001698, + 0x10b60115, + 0xf960f908, + 0xfcd0fc50, + 0x002e7ee0, + 0x0242b600, + 0xf8e81bf4, +/* 0x059d: memx_func_wait */ + 0xcf2c0800, + 0x1e980088, + 0x011d9800, + 0x98021c98, + 0x10b6031b, + 0x00797e10, +/* 0x05b7: memx_func_delay */ + 0x9800f800, + 0x10b6001e, + 0x005d7e04, +/* 0x05c3: memx_func_train */ + 0xf800f800, +/* 0x05c5: memx_exec */ + 0xf9e0f900, + 0xb2c1b2d0, +/* 0x05cd: memx_exec_next */ + 0x001398b2, + 0xe70410b6, + 0xe701f034, + 0xb601e033, + 0x30f00132, + 0xde35980c, + 0x12a655f9, + 0x98e51ef4, + 0x0c98f10b, + 0x02cbbbf2, + 0xcf07c44b, + 0xd0fc00bb, + 0xc27ee0fc, + 0x00f80002, +/* 0x0604: memx_info */ + 0xf401c670, +/* 0x060a: memx_info_data */ + 0xcc4c0c0b, + 0x08004b03, +/* 0x0613: memx_info_train */ + 0x4c090ef4, + 0x004b0bcc, +/* 0x0619: memx_info_send */ + 0x02c27e01, +/* 0x061f: memx_recv */ + 0xb000f800, + 0x0bf401d6, + 0x00d6b0a3, + 0xf8dc0bf4, +/* 0x062d: memx_init */ +/* 0x062f: perf_recv */ + 0xf800f800, +/* 0x0631: perf_init */ +/* 0x0633: i2c_drive_scl */ + 0xb000f800, + 0x0bf40036, + 0x07e0400d, + 0xbd0001f6, +/* 0x0643: i2c_drive_scl_lo */ + 0x4000f804, + 0x01f607e4, + 0xf804bd00, +/* 0x064d: i2c_drive_sda */ + 0x0036b000, + 0x400d0bf4, + 0x02f607e0, + 0xf804bd00, +/* 0x065d: i2c_drive_sda_lo */ + 0x07e44000, + 0xbd0002f6, +/* 0x0667: i2c_sense_scl */ + 0xf400f804, + 0xc4430132, + 0x0033cf07, + 0xf40431fd, + 0x31f4060b, +/* 0x0679: i2c_sense_scl_done */ +/* 0x067b: i2c_sense_sda */ + 0xf400f801, + 0xc4430132, + 0x0033cf07, + 0xf40432fd, + 0x31f4060b, +/* 0x068d: i2c_sense_sda_done */ +/* 0x068f: i2c_raise_scl */ + 0xf900f801, + 0x08984440, + 0x337e0103, +/* 0x069a: i2c_raise_scl_wait */ + 0xe84e0006, + 0x005d7e03, + 0x06677e00, + 0x0901f400, + 0xf40142b6, +/* 0x06ae: i2c_raise_scl_done */ + 0x40fcef1b, +/* 0x06b2: i2c_start */ + 0x677e00f8, + 0x11f40006, + 0x067b7e0d, + 0x0611f400, +/* 0x06c3: i2c_start_rep */ + 0x032e0ef4, + 0x06337e00, + 0x7e010300, + 0xbb00064d, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x00068f7e, + 0xf40464b6, +/* 0x06ee: i2c_start_send */ + 0x00031d11, + 0x00064d7e, + 0x7e13884e, + 0x0300005d, + 0x06337e00, + 0x13884e00, + 0x00005d7e, +/* 0x0708: i2c_start_out */ +/* 0x070a: i2c_stop */ + 0x000300f8, + 0x0006337e, + 0x4d7e0003, + 0xe84e0006, + 0x005d7e03, + 0x7e010300, + 0x4e000633, + 0x5d7e1388, + 0x01030000, + 0x00064d7e, + 0x7e13884e, + 0xf800005d, +/* 0x0739: i2c_bitw */ + 0x064d7e00, + 0x03e84e00, + 0x00005d7e, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0x068f7e50, + 0x0464b600, + 0x4e1711f4, + 0x5d7e1388, + 0x00030000, + 0x0006337e, + 0x7e13884e, +/* 0x0777: i2c_bitw_out */ + 0xf800005d, +/* 0x0779: i2c_bitr */ + 0x7e010300, + 0x4e00064d, + 0x5d7e03e8, + 0x76bb0000, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0x7e50fc04, + 0xb600068f, + 0x11f40464, + 0x067b7e1a, + 0x7e000300, + 0x4e000633, + 0x5d7e1388, + 0x3cf00000, + 0x0131f401, +/* 0x07bc: i2c_bitr_done */ +/* 0x07be: i2c_get_byte */ + 0x000500f8, +/* 0x07c2: i2c_get_byte_next */ + 0x54b60804, + 0x0076bb01, + 0xf90465b6, + 0x04659450, + 0xbd0256bb, + 0x0475fd50, + 0x797e50fc, + 0x64b60007, + 0x2a11f404, + 0xb60553fd, + 0x1bf40142, + 0xbb0103d8, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x0007397e, +/* 0x080b: i2c_get_byte_done */ + 0xf80464b6, +/* 0x080d: i2c_put_byte */ +/* 0x080f: i2c_put_byte_next */ + 0xb6080400, + 0x54ff0142, + 0x0076bb38, + 0xf90465b6, + 0x04659450, + 0xbd0256bb, + 0x0475fd50, + 0x397e50fc, + 0x64b60007, + 0x3411f404, + 0xf40046b0, + 0x76bbd81b, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0x7e50fc04, + 0xb6000779, + 0x11f40464, + 0x0076bb0f, + 0xf40136b0, + 0x32f4061b, +/* 0x0865: i2c_put_byte_done */ +/* 0x0867: i2c_addr */ + 0xbb00f801, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x0006b27e, + 0xf40464b6, + 0xc3e72911, + 0x34b6012e, + 0x0553fd01, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0x080d7e50, + 0x0464b600, +/* 0x08ac: i2c_addr_done */ +/* 0x08ae: i2c_acquire_addr */ + 0xcec700f8, + 0x05e4b6f8, + 0xd014e0b7, +/* 0x08ba: i2c_acquire */ + 0xae7e00f8, + 0x047e0008, + 0xd9f00000, + 0x002e7e03, +/* 0x08cb: i2c_release */ + 0x7e00f800, + 0x7e0008ae, + 0xf0000004, + 0x2e7e03da, + 0x00f80000, +/* 0x08dc: i2c_recv */ + 0xc70132f4, + 0x14b6f8c1, + 0x2816b002, + 0x01371ff5, + 0x0cf413b8, + 0x00329800, + 0x0ccc13b8, + 0x00319800, + 0xf90231f4, + 0xf9e0f9d0, + 0x0067f1d0, + 0x0063f100, + 0x01679210, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0x08ba7e50, + 0x0464b600, + 0xd6b0d0fc, + 0xb01bf500, + 0xbb000500, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x0008677e, + 0xf50464b6, + 0xc700cc11, + 0x76bbe0c5, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0x7e50fc04, + 0xb600080d, + 0x11f50464, + 0x010500a9, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0x08677e50, + 0x0464b600, + 0x008711f5, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0x07be7e50, + 0x0464b600, + 0xcb6711f4, + 0x76bbe05b, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0x7e50fc04, + 0xb600070a, + 0x5bb20464, + 0x0ef474bd, +/* 0x09e1: i2c_recv_not_rd08 */ + 0x01d6b041, + 0x053b1bf4, + 0x08677e00, + 0x3211f400, + 0x7ee0c5c7, + 0xf400080d, + 0x00052811, + 0x0008677e, + 0xc71f11f4, + 0x0d7ee0b5, + 0x11f40008, + 0x070a7e15, + 0xc774bd00, + 0x1bf408c5, + 0x0232f409, +/* 0x0a1f: i2c_recv_not_wr08 */ +/* 0x0a1f: i2c_recv_done */ + 0xc7030ef4, + 0xcb7ef8ce, + 0xe0fc0008, + 0x12f4d0fc, + 0x7e7cb209, +/* 0x0a33: i2c_recv_exit */ + 0xf80002c2, +/* 0x0a35: i2c_init */ +/* 0x0a37: test_recv */ + 0x4100f800, + 0x11cf0458, + 0x0110b600, + 0xf6045840, + 0x04bd0001, + 0xd900e7f1, + 0x134fe3f1, + 0x0002017e, +/* 0x0a56: test_init */ + 0x004e00f8, + 0x02017e08, +/* 0x0a5f: idle_recv */ + 0xf800f800, +/* 0x0a61: idle */ + 0x0031f400, + 0xcf045441, + 0x10b60011, + 0x04544001, + 0xbd0001f6, +/* 0x0a75: idle_loop */ + 0xf4580104, +/* 0x0a7a: idle_proc */ +/* 0x0a7a: idle_proc_exec */ + 0x10f90232, + 0xcb7e1eb2, + 0x10fc0002, + 0xf40911f4, + 0x0ef40231, +/* 0x0a8d: idle_proc_next */ + 0x5810b6f0, + 0x1bf41fa6, + 0xe002f4e8, + 0xf40028f4, + 0x0000c60e, + 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/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gt215.fuc3 b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gt215.fuc3 new file mode 100644 index 000000000..393049fc8 --- /dev/null +++ b/kernel/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/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gt215.fuc3.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gt215.fuc3.h new file mode 100644 index 000000000..2686f8fad --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gt215.fuc3.h @@ -0,0 +1,1868 @@ +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, + 0x00000512, + 0x000004af, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x584d454d, + 0x00000842, + 0x00000834, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x46524550, + 0x00000846, + 0x00000844, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x5f433249, + 0x00000c76, + 0x00000b19, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x54534554, + 0x00000c9f, + 0x00000c78, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x454c4449, + 0x00000cab, + 0x00000ca9, + 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, + 0x00000551, +/* 0x037c: memx_func_next */ + 0x00000002, + 0x00000000, + 0x000005a8, + 0x00000003, + 0x00000002, + 0x0000063a, + 0x00040004, + 0x00000000, + 0x00000656, + 0x00010005, + 0x00000000, + 0x00000673, + 0x00010006, + 0x00000000, + 0x000005f8, + 0x00000007, + 0x00000000, + 0x0000067e, +/* 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, +}; + +uint32_t gt215_pmu_code[] = { + 0x039e0ef5, +/* 0x0004: rd32 */ + 0x07a007f1, + 0xd00604b6, + 0x04bd000e, + 0xf001d7f0, + 0x07f101d3, + 0x04b607ac, + 0x000dd006, +/* 0x0022: rd32_wait */ + 0xd7f104bd, + 0xd4b607ac, + 0x00ddcf06, + 0x7000d4f1, + 0xf1f21bf4, + 0xb607a4d7, + 0xddcf06d4, +/* 0x003f: wr32 */ + 0xf100f800, + 0xb607a007, + 0x0ed00604, + 0xf104bd00, + 0xb607a407, + 0x0dd00604, + 0xf004bd00, + 0xd5f002d7, + 0x01d3f0f0, + 0x07ac07f1, + 0xd00604b6, + 0x04bd000d, +/* 0x006c: wr32_wait */ + 0x07acd7f1, + 0xcf06d4b6, + 0xd4f100dd, + 0x1bf47000, +/* 0x007f: nsec */ + 0xf900f8f2, + 0xf080f990, + 0x84b62c87, + 0x0088cf06, +/* 0x008c: nsec_loop */ + 0xb62c97f0, + 0x99cf0694, + 0x0298bb00, + 0xf4069eb8, + 0x80fcf11e, + 0x00f890fc, +/* 0x00a4: wait */ + 0x80f990f9, + 0xb62c87f0, + 0x88cf0684, +/* 0x00b1: wait_loop */ + 0x02eeb900, + 0xb90421f4, + 0xadfd02da, + 0x06acb804, + 0xf0150bf4, + 0x94b62c97, + 0x0099cf06, + 0xb80298bb, + 0x1ef4069b, +/* 0x00d5: wait_done */ + 0xfc80fcdf, +/* 0x00db: intr_watchdog */ + 0x9800f890, + 0x96b003e9, + 0x2a0bf400, + 0xbb9a0a98, + 0x1cf4029a, + 0x01d7f00f, + 0x02dd21f5, + 0x0ef494bd, +/* 0x00f9: intr_watchdog_next_time */ + 0x9b0a9815, + 0xf400a6b0, + 0x9ab8090b, + 0x061cf406, +/* 0x0108: intr_watchdog_next_time_set */ +/* 0x010b: intr_watchdog_next_proc */ + 0x809b0980, + 0xe0b603e9, + 0x68e6b158, + 0xc61bf402, +/* 0x011a: intr */ + 0x00f900f8, + 0x80f904bd, + 0xa0f990f9, + 0xc0f9b0f9, + 0xe0f9d0f9, + 0xf7f0f0f9, + 0x0188fe00, + 0x87f180f9, + 0x84b605d0, + 0x0088cf06, + 0xf10180b6, + 0xb605d007, + 0x08d00604, + 0xf004bd00, + 0x84b60887, + 0x0088cf06, + 0xf40289c4, + 0x0080230b, + 0x58e7f09b, + 0x98db21f4, + 0x96b09b09, + 0x110bf400, + 0xb63407f0, + 0x09d00604, + 0x8004bd00, +/* 0x017e: intr_skip_watchdog */ + 0x89e49a09, + 0x0bf40800, + 0x8897f148, + 0x0694b606, + 0xc40099cf, + 0x0bf4029a, + 0xc0c7f12c, + 0x06c4b604, + 0xf900cccf, + 0x48e7f1c0, + 0x53e3f14f, + 0x00d7f054, + 0x034221f5, + 0x07f1c0fc, + 0x04b604c0, + 0x000cd006, +/* 0x01be: intr_subintr_skip_fifo */ + 0x07f104bd, + 0x04b60688, + 0x0009d006, +/* 0x01ca: intr_skip_subintr */ + 0x89c404bd, + 0x070bf420, + 0xffbfa4f1, +/* 0x01d4: intr_skip_pause */ + 0xf44089c4, + 0xa4f1070b, +/* 0x01de: intr_skip_user0 */ + 0x07f0ffbf, + 0x0604b604, + 0xbd0008d0, + 0xfe80fc04, + 0xf0fc0088, + 0xd0fce0fc, + 0xb0fcc0fc, + 0x90fca0fc, + 0x00fc80fc, + 0xf80032f4, +/* 0x0205: ticks_from_ns */ + 0xf9c0f901, + 0xcbd7f1b0, + 0x00d3f000, + 0x041321f5, + 0x03e8ccec, + 0xf400b4b0, + 0xeeec120b, + 0xd7f103e8, + 0xd3f000cb, + 0x1321f500, +/* 0x022d: ticks_from_ns_quit */ + 0x02ceb904, + 0xc0fcb0fc, +/* 0x0236: ticks_from_us */ + 0xc0f900f8, + 0xd7f1b0f9, + 0xd3f000cb, + 0x1321f500, + 0x02ceb904, + 0xf400b4b0, + 0xe4bd050b, +/* 0x0250: ticks_from_us_quit */ + 0xc0fcb0fc, +/* 0x0256: ticks_to_us */ + 0xd7f100f8, + 0xd3f000cb, + 0xecedff00, +/* 0x0262: timer */ + 0x90f900f8, + 0x32f480f9, + 0x03f89810, + 0xf40086b0, + 0x84bd651c, + 0xb63807f0, + 0x08d00604, + 0xf004bd00, + 0x84b63487, + 0x0088cf06, + 0xbb9a0998, + 0xe9bb0298, + 0x03fe8000, + 0xb60887f0, + 0x88cf0684, + 0x0284f000, + 0xf0261bf4, + 0x84b63487, + 0x0088cf06, + 0xf406e0b8, + 0xe8b8090b, + 0x111cf406, +/* 0x02b8: timer_reset */ + 0xb63407f0, + 0x0ed00604, + 0x8004bd00, +/* 0x02c6: timer_enable */ + 0x87f09a0e, + 0x3807f001, + 0xd00604b6, + 0x04bd0008, +/* 0x02d4: timer_done */ + 0xfc1031f4, + 0xf890fc80, +/* 0x02dd: send_proc */ + 0xf980f900, + 0x05e89890, + 0xf004e998, + 0x89b80486, + 0x2a0bf406, + 0x940398c4, + 0x80b60488, + 0x008ebb18, + 0x8000fa98, + 0x8d80008a, + 0x028c8001, + 0xb6038b80, + 0x94f00190, + 0x04e98007, +/* 0x0317: send_done */ + 0xfc0231f4, + 0xf880fc90, +/* 0x031d: find */ + 0xf080f900, + 0x31f45887, +/* 0x0325: find_loop */ + 0x008a9801, + 0xf406aeb8, + 0x80b6100b, + 0x6886b158, + 0xf01bf402, +/* 0x033b: find_done */ + 0xb90132f4, + 0x80fc028e, +/* 0x0342: send */ + 0x21f500f8, + 0x01f4031d, +/* 0x034b: recv */ + 0xf900f897, + 0x9880f990, + 0xe99805e8, + 0x0132f404, + 0xf40689b8, + 0x89c43d0b, + 0x0180b603, + 0x800784f0, + 0xea9805e8, + 0xfef0f902, + 0xf0f9018f, + 0x9402efb9, + 0xe9bb0499, + 0x18e0b600, + 0x9803eb98, + 0xed9802ec, + 0x00ee9801, + 0xf0fca5f9, + 0xf400f8fe, + 0xf0fc0131, +/* 0x0398: recv_done */ + 0x90fc80fc, +/* 0x039e: init */ + 0x17f100f8, + 0x14b60108, + 0x0011cf06, + 0x010911e7, + 0xfe0814b6, + 0x17f10014, + 0x13f000e0, + 0x1c07f000, + 0xd00604b6, + 0x04bd0001, + 0xf0ff17f0, + 0x04b61407, + 0x0001d006, + 0x17f004bd, + 0x0015f102, + 0x1007f008, + 0xd00604b6, + 0x04bd0001, + 0x011a17f1, + 0xfe0013f0, + 0x31f40010, + 0x0117f010, + 0xb63807f0, + 0x01d00604, + 0xf004bd00, +/* 0x0402: init_proc */ + 0xf19858f7, + 0x0016b001, + 0xf9fa0bf4, + 0x58f0b615, +/* 0x0413: mulu32_32_64 */ + 0xf9f20ef4, + 0xf920f910, + 0x9540f930, + 0xd29510e1, + 0xbdc4bd10, + 0xc0edffb4, + 0xb9301dff, + 0x34f10234, + 0x34b6ffff, + 0x1045b610, + 0xbb00c3bb, + 0xe2ff01b4, + 0x0234b930, + 0xffff34f1, + 0xb61034b6, + 0xc3bb1045, + 0x01b4bb00, + 0xbb3012ff, + 0x40fc00b3, + 0x20fc30fc, + 0x00f810fc, +/* 0x0464: host_send */ + 0x04b017f1, + 0xcf0614b6, + 0x27f10011, + 0x24b604a0, + 0x0022cf06, + 0xf40612b8, + 0x1ec4320b, + 0x04ee9407, + 0x0270e0b7, + 0x9803eb98, + 0xed9802ec, + 0x00ee9801, + 0x034221f5, + 0xc40110b6, + 0x07f10f1e, + 0x04b604b0, + 0x000ed006, + 0x0ef404bd, +/* 0x04ad: host_send_done */ +/* 0x04af: host_recv */ + 0xf100f8ba, + 0xf14e4917, + 0xb8525413, + 0x0bf406e1, +/* 0x04bd: host_recv_wait */ + 0xcc17f1aa, + 0x0614b604, + 0xf10011cf, + 0xb604c827, + 0x22cf0624, + 0x0816f000, + 0xf40612b8, + 0x23c4e60b, + 0x0434b607, + 0x02f030b7, + 0x80033b80, + 0x3d80023c, + 0x003e8001, + 0xf00120b6, + 0x07f10f24, + 0x04b604c8, + 0x0002d006, + 0x27f004bd, + 0x0007f040, + 0xd00604b6, + 0x04bd0002, +/* 0x0512: host_init */ + 0x17f100f8, + 0x14b60080, + 0x7015f110, + 0xd007f102, + 0x0604b604, + 0xbd0001d0, + 0x8017f104, + 0x1014b600, + 0x02f015f1, + 0x04dc07f1, + 0xd00604b6, + 0x04bd0001, + 0xf10117f0, + 0xb604c407, + 0x01d00604, + 0xf804bd00, +/* 0x0551: memx_func_enter */ + 0x1087f100, + 0x028eb916, + 0xb90421f4, + 0x67f102d7, + 0x63f1fffc, + 0x76fdffff, + 0x0267f104, + 0x0576fd00, + 0x70f980f9, + 0xe0fcd0fc, + 0xf03f21f4, + 0x07f10467, + 0x04b607e0, + 0x0006d006, +/* 0x058a: memx_func_enter_wait */ + 0x67f104bd, + 0x64b607c0, + 0x0066cf06, + 0xf40464f0, + 0x67f0f30b, + 0x0664b62c, + 0x800066cf, + 0x00f8f106, +/* 0x05a8: memx_func_leave */ + 0xb62c67f0, + 0x66cf0664, + 0xf2068000, + 0xf10467f0, + 0xb607e407, + 0x06d00604, +/* 0x05c3: memx_func_leave_wait */ + 0xf104bd00, + 0xb607c067, + 0x66cf0664, + 0x0464f000, + 0xf1f31bf4, + 0xb9161087, + 0x21f4028e, + 0x02d7b904, + 0xffcc67f1, + 0xffff63f1, + 0xf90476fd, + 0xfc70f980, + 0xf4e0fcd0, + 0x00f83f21, +/* 0x05f8: memx_func_wait_vblank */ + 0xb0001698, + 0x0bf40066, + 0x0166b013, + 0xf4060bf4, +/* 0x060a: memx_func_wait_vblank_head1 */ + 0x77f12e0e, + 0x0ef40020, +/* 0x0611: memx_func_wait_vblank_head0 */ + 0x0877f107, +/* 0x0615: memx_func_wait_vblank_0 */ + 0xc467f100, + 0x0664b607, + 0xfd0066cf, + 0x1bf40467, +/* 0x0625: memx_func_wait_vblank_1 */ + 0xc467f1f3, + 0x0664b607, + 0xfd0066cf, + 0x0bf40467, +/* 0x0635: memx_func_wait_vblank_fini */ + 0x0410b6f3, +/* 0x063a: memx_func_wr32 */ + 0x169800f8, + 0x01159800, + 0xf90810b6, + 0xfc50f960, + 0xf4e0fcd0, + 0x42b63f21, + 0xe91bf402, +/* 0x0656: memx_func_wait */ + 0x87f000f8, + 0x0684b62c, + 0x980088cf, + 0x1d98001e, + 0x021c9801, + 0xb6031b98, + 0x21f41010, +/* 0x0673: memx_func_delay */ + 0x9800f8a4, + 0x10b6001e, + 0x7f21f404, +/* 0x067e: memx_func_train */ + 0x57f100f8, + 0x77f10003, + 0x97f10000, + 0x93f00000, + 0x029eb970, + 0xb90421f4, + 0xe7f102d8, + 0x21f42710, +/* 0x069d: memx_func_train_loop_outer */ + 0x0158e07f, + 0x0083f101, + 0xe097f102, + 0x1193f011, + 0x80f990f9, + 0xe0fcd0fc, + 0xf93f21f4, + 0x0067f150, +/* 0x06bd: memx_func_train_loop_inner */ + 0x1187f100, + 0x9068ff11, + 0xfd109894, + 0x97f10589, + 0x93f00720, + 0xf990f910, + 0xfcd0fc80, + 0x3f21f4e0, + 0x008097f1, + 0xb91093f0, + 0x21f4029e, + 0x02d8b904, + 0xf92088c5, + 0xfc80f990, + 0xf4e0fcd0, + 0x97f13f21, + 0x93f0053c, + 0x0287f110, + 0x0083f130, + 0xf990f980, + 0xfcd0fc80, + 0x3f21f4e0, + 0x0560e7f1, + 0xf110e3f0, + 0xf10000d7, + 0x908000d3, + 0xb7f100dc, + 0xb3f08480, + 0xa421f41e, + 0x000057f1, + 0xffff97f1, + 0x830093f1, +/* 0x073c: memx_func_train_loop_4x */ + 0x0080a7f1, + 0xb910a3f0, + 0x21f402ae, + 0x02d8b904, + 0xffdfb7f1, + 0xffffb3f1, + 0xf9048bfd, + 0xfc80f9a0, + 0xf4e0fcd0, + 0xa7f13f21, + 0xa3f0053c, + 0x0287f110, + 0x0083f130, + 0xf9a0f980, + 0xfcd0fc80, + 0x3f21f4e0, + 0x0560e7f1, + 0xf110e3f0, + 0xf10000d7, + 0xb98000d3, + 0xb7f102dc, + 0xb3f02710, + 0xa421f400, + 0xf402eeb9, + 0xddb90421, + 0x949dff02, + 0x700150b6, + 0x1ef40456, + 0xcc7aa092, + 0x00a9800b, + 0xb60160b6, + 0x66700470, + 0x001ef510, + 0xb650fcff, + 0x56700150, + 0xd41ef507, +/* 0x07cf: memx_exec */ + 0xf900f8fe, + 0xb9d0f9e0, + 0xb2b902c1, +/* 0x07d9: memx_exec_next */ + 0x00139802, + 0xe70410b6, + 0xe701f034, + 0xb601e033, + 0x30f00132, + 0xde35980c, + 0x12b855f9, + 0xe41ef406, + 0x98f10b98, + 0xcbbbf20c, + 0xc4b7f102, + 0x06b4b607, + 0xfc00bbcf, + 0xf5e0fcd0, + 0xf8034221, +/* 0x0815: memx_info */ + 0x01c67000, +/* 0x081b: memx_info_data */ + 0xf10e0bf4, + 0xf103ccc7, + 0xf40800b7, +/* 0x0826: memx_info_train */ + 0xc7f10b0e, + 0xb7f10bcc, +/* 0x082e: memx_info_send */ + 0x21f50100, + 0x00f80342, +/* 0x0834: memx_recv */ + 0xf401d6b0, + 0xd6b0980b, + 0xd80bf400, +/* 0x0842: memx_init */ + 0x00f800f8, +/* 0x0844: perf_recv */ +/* 0x0846: perf_init */ + 0x00f800f8, +/* 0x0848: i2c_drive_scl */ + 0xf40036b0, + 0x07f1110b, + 0x04b607e0, + 0x0001d006, + 0x00f804bd, +/* 0x085c: i2c_drive_scl_lo */ + 0x07e407f1, + 0xd00604b6, + 0x04bd0001, +/* 0x086a: i2c_drive_sda */ + 0x36b000f8, + 0x110bf400, + 0x07e007f1, + 0xd00604b6, + 0x04bd0002, +/* 0x087e: i2c_drive_sda_lo */ + 0x07f100f8, + 0x04b607e4, + 0x0002d006, + 0x00f804bd, +/* 0x088c: i2c_sense_scl */ + 0xf10132f4, + 0xb607c437, + 0x33cf0634, + 0x0431fd00, + 0xf4060bf4, +/* 0x08a2: i2c_sense_scl_done */ + 0x00f80131, +/* 0x08a4: i2c_sense_sda */ + 0xf10132f4, + 0xb607c437, + 0x33cf0634, + 0x0432fd00, + 0xf4060bf4, +/* 0x08ba: i2c_sense_sda_done */ + 0x00f80131, +/* 0x08bc: i2c_raise_scl */ + 0x47f140f9, + 0x37f00898, + 0x4821f501, +/* 0x08c9: i2c_raise_scl_wait */ + 0xe8e7f108, + 0x7f21f403, + 0x088c21f5, + 0xb60901f4, + 0x1bf40142, +/* 0x08dd: i2c_raise_scl_done */ + 0xf840fcef, +/* 0x08e1: i2c_start */ + 0x8c21f500, + 0x0d11f408, + 0x08a421f5, + 0xf40611f4, +/* 0x08f2: i2c_start_rep */ + 0x37f0300e, + 0x4821f500, + 0x0137f008, + 0x086a21f5, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0xbc21f550, + 0x0464b608, +/* 0x091f: i2c_start_send */ + 0xf01f11f4, + 0x21f50037, + 0xe7f1086a, + 0x21f41388, + 0x0037f07f, + 0x084821f5, + 0x1388e7f1, +/* 0x093b: i2c_start_out */ + 0xf87f21f4, +/* 0x093d: i2c_stop */ + 0x0037f000, + 0x084821f5, + 0xf50037f0, + 0xf1086a21, + 0xf403e8e7, + 0x37f07f21, + 0x4821f501, + 0x88e7f108, + 0x7f21f413, + 0xf50137f0, + 0xf1086a21, + 0xf41388e7, + 0x00f87f21, +/* 0x0970: i2c_bitw */ + 0x086a21f5, + 0x03e8e7f1, + 0xbb7f21f4, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x08bc21f5, + 0xf40464b6, + 0xe7f11811, + 0x21f41388, + 0x0037f07f, + 0x084821f5, + 0x1388e7f1, +/* 0x09af: i2c_bitw_out */ + 0xf87f21f4, +/* 0x09b1: i2c_bitr */ + 0x0137f000, + 0x086a21f5, + 0x03e8e7f1, + 0xbb7f21f4, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x08bc21f5, + 0xf40464b6, + 0x21f51b11, + 0x37f008a4, + 0x4821f500, + 0x88e7f108, + 0x7f21f413, + 0xf4013cf0, +/* 0x09f6: i2c_bitr_done */ + 0x00f80131, +/* 0x09f8: i2c_get_byte */ + 0xf00057f0, +/* 0x09fe: i2c_get_byte_next */ + 0x54b60847, + 0x0076bb01, + 0xf90465b6, + 0x04659450, + 0xbd0256bb, + 0x0475fd50, + 0x21f550fc, + 0x64b609b1, + 0x2b11f404, + 0xb60553fd, + 0x1bf40142, + 0x0137f0d8, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0x7021f550, + 0x0464b609, +/* 0x0a48: i2c_get_byte_done */ +/* 0x0a4a: i2c_put_byte */ + 0x47f000f8, +/* 0x0a4d: i2c_put_byte_next */ + 0x0142b608, + 0xbb3854ff, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x097021f5, + 0xf40464b6, + 0x46b03411, + 0xd81bf400, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0xb121f550, + 0x0464b609, + 0xbb0f11f4, + 0x36b00076, + 0x061bf401, +/* 0x0aa3: i2c_put_byte_done */ + 0xf80132f4, +/* 0x0aa5: i2c_addr */ + 0x0076bb00, + 0xf90465b6, + 0x04659450, + 0xbd0256bb, + 0x0475fd50, + 0x21f550fc, + 0x64b608e1, + 0x2911f404, + 0x012ec3e7, + 0xfd0134b6, + 0x76bb0553, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0xf550fc04, + 0xb60a4a21, +/* 0x0aea: i2c_addr_done */ + 0x00f80464, +/* 0x0aec: i2c_acquire_addr */ + 0xb6f8cec7, + 0xe0b702e4, + 0xee980d1c, +/* 0x0afb: i2c_acquire */ + 0xf500f800, + 0xf40aec21, + 0xd9f00421, + 0x3f21f403, +/* 0x0b0a: i2c_release */ + 0x21f500f8, + 0x21f40aec, + 0x03daf004, + 0xf83f21f4, +/* 0x0b19: i2c_recv */ + 0x0132f400, + 0xb6f8c1c7, + 0x16b00214, + 0x3a1ff528, + 0xf413a001, + 0x0032980c, + 0x0ccc13a0, + 0xf4003198, + 0xd0f90231, + 0xd0f9e0f9, + 0x000067f1, + 0x100063f1, + 0xbb016792, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x0afb21f5, + 0xfc0464b6, + 0x00d6b0d0, + 0x00b31bf5, + 0xbb0057f0, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x0aa521f5, + 0xf50464b6, + 0xc700d011, + 0x76bbe0c5, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0xf550fc04, + 0xb60a4a21, + 0x11f50464, + 0x57f000ad, + 0x0076bb01, + 0xf90465b6, + 0x04659450, + 0xbd0256bb, + 0x0475fd50, + 0x21f550fc, + 0x64b60aa5, + 0x8a11f504, + 0x0076bb00, + 0xf90465b6, + 0x04659450, + 0xbd0256bb, + 0x0475fd50, + 0x21f550fc, + 0x64b609f8, + 0x6a11f404, + 0xbbe05bcb, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x093d21f5, + 0xb90464b6, + 0x74bd025b, +/* 0x0c1f: i2c_recv_not_rd08 */ + 0xb0430ef4, + 0x1bf401d6, + 0x0057f03d, + 0x0aa521f5, + 0xc73311f4, + 0x21f5e0c5, + 0x11f40a4a, + 0x0057f029, + 0x0aa521f5, + 0xc71f11f4, + 0x21f5e0b5, + 0x11f40a4a, + 0x3d21f515, + 0xc774bd09, + 0x1bf408c5, + 0x0232f409, +/* 0x0c5f: i2c_recv_not_wr08 */ +/* 0x0c5f: i2c_recv_done */ + 0xc7030ef4, + 0x21f5f8ce, + 0xe0fc0b0a, + 0x12f4d0fc, + 0x027cb90a, + 0x034221f5, +/* 0x0c74: i2c_recv_exit */ +/* 0x0c76: i2c_init */ + 0x00f800f8, +/* 0x0c78: test_recv */ + 0x05d817f1, + 0xcf0614b6, + 0x10b60011, + 0xd807f101, + 0x0604b605, + 0xbd0001d0, + 0x00e7f104, + 0x4fe3f1d9, + 0x6221f513, +/* 0x0c9f: test_init */ + 0xf100f802, + 0xf50800e7, + 0xf8026221, +/* 0x0ca9: idle_recv */ +/* 0x0cab: idle */ + 0xf400f800, + 0x17f10031, + 0x14b605d4, + 0x0011cf06, + 0xf10110b6, + 0xb605d407, + 0x01d00604, +/* 0x0cc7: idle_loop */ + 0xf004bd00, + 0x32f45817, +/* 0x0ccd: idle_proc */ +/* 0x0ccd: idle_proc_exec */ + 0xb910f902, + 0x21f5021e, + 0x10fc034b, + 0xf40911f4, + 0x0ef40231, +/* 0x0ce1: idle_proc_next */ + 0x5810b6ef, + 0xf4061fb8, + 0x02f4e61b, + 0x0028f4dd, + 0x00bb0ef4, + 0x00000000, + 0x00000000, + 0x00000000, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/host.fuc b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/host.fuc new file mode 100644 index 000000000..c2bb616a8 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/host.fuc @@ -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 + */ + +#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 + mov $r1 (PROC_KERN & 0x0000ffff) + sethi $r1 (PROC_KERN & 0xffff0000) + 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/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/i2c_.fuc b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/i2c_.fuc new file mode 100644 index 000000000..757dda700 --- /dev/null +++ b/kernel/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/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/idle.fuc b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/idle.fuc new file mode 100644 index 000000000..98f1c3738 --- /dev/null +++ b/kernel/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/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/kernel.fuc b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/kernel.fuc new file mode 100644 index 000000000..5cf5be63c --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/kernel.fuc @@ -0,0 +1,556 @@ +/* + * 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) + mov $r13 NV_PPWR_MMIO_CTRL_OP_RD + sethi $r13 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) + mov $r13 NV_PPWR_MMIO_CTRL_OP_WR + or $r13 NV_PPWR_MMIO_CTRL_MASK_B32_0 + sethi $r13 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 + mov $r14 (PROC_HOST & 0x0000ffff) + sethi $r14 (PROC_HOST & 0xffff0000) + 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: + and $r9 $r8 NV_PPWR_INTR_PAUSE + bra z #intr_skip_pause + and $r10 0xffbf + + intr_skip_pause: + and $r9 $r8 NV_PPWR_INTR_USER0 + bra z #intr_skip_user0 + and $r10 0xffbf + + intr_skip_user0: + 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 + mov $r1 0x0010 + sethi $r1 NV_PPWR_UAS_CONFIG_ENABLE + nv_iowrs(NV_PPWR_UAS_CONFIG, $r1) +#endif + + // route all interrupts except user0/1 and pause to fuc + mov $r1 0x00e0 + sethi $r1 0x00000000 + 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 + mov $r1 #intr + sethi $r1 0x00000000 + 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/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/macros.fuc b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/macros.fuc new file mode 100644 index 000000000..96fc984da --- /dev/null +++ b/kernel/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 NV_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) /* +*/ movw $r0 addr /* +*/ st size D[$r0] reg /* +*/ clear b32 $r0 + +#define ld(size, reg, addr) /* +*/ movw $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/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/memx.fuc b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/memx.fuc new file mode 100644 index 000000000..ec03f9a42 --- /dev/null +++ b/kernel/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 + movw $r8 0x1610 + nv_rd32($r7, $r8) + imm32($r6, 0xfffffffc) + and $r7 $r6 + movw $r6 0x2 + or $r7 $r6 + nv_wr32($r8, $r7) +#else + movw $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) + + movw $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 + movw $r8 0x1610 + nv_rd32($r7, $r8) + imm32($r6, 0xffffffcc) + and $r7 $r6 + nv_wr32($r8, $r7) +#else + movw $r6 0x0026f0 + imm32($r7, 0x00000001) + nv_rd32($r8, $r6) + or $r8 $r7 + nv_wr32($r6, $r8) + + movw $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: + movw $r7 0x20 + bra #memx_func_wait_vblank_0 + + memx_func_wait_vblank_head0: + movw $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) + movw $r5 0x3 + movw $r7 0x0 + +// Read random memory to wake up... things + imm32($r9, 0x700000) + nv_rd32($r8,$r9) + movw $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 + + movw $r6 0x0 + memx_func_train_loop_inner: + movw $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 + movw $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/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/os.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/os.h new file mode 100644 index 000000000..c8b06cb77 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/os.h @@ -0,0 +1,52 @@ +#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/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/perf.fuc b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/perf.fuc new file mode 100644 index 000000000..38eadf705 --- /dev/null +++ b/kernel/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/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/test.fuc b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/test.fuc new file mode 100644 index 000000000..0c3a71bf5 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/test.fuc @@ -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 + */ + +#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) + mov $r14 -0x2700 /* 0xd900, envyas grrr! */ + sethi $r14 0x134f0000 + call(timer) + ret + +// description +// +// $r15 - current (test) +// $r0 - zero +test_init: + mov $r14 0x800 + call(timer) + ret +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gf100.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gf100.c new file mode 100644 index 000000000..78a4ea010 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gf100.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 "priv.h" +#include "fuc/gf100.fuc3.h" + +struct nvkm_oclass * +gf100_pmu_oclass = &(struct nvkm_pmu_impl) { + .base.handle = NV_SUBDEV(PMU, 0xc0), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_pmu_ctor, + .dtor = _nvkm_pmu_dtor, + .init = _nvkm_pmu_init, + .fini = _nvkm_pmu_fini, + }, + .code.data = gf100_pmu_code, + .code.size = sizeof(gf100_pmu_code), + .data.data = gf100_pmu_data, + .data.size = sizeof(gf100_pmu_data), +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gf110.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gf110.c new file mode 100644 index 000000000..6b3a23839 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gf110.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 "priv.h" +#include "fuc/gf110.fuc4.h" + +struct nvkm_oclass * +gf110_pmu_oclass = &(struct nvkm_pmu_impl) { + .base.handle = NV_SUBDEV(PMU, 0xd0), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_pmu_ctor, + .dtor = _nvkm_pmu_dtor, + .init = _nvkm_pmu_init, + .fini = _nvkm_pmu_fini, + }, + .code.data = gf110_pmu_code, + .code.size = sizeof(gf110_pmu_code), + .data.data = gf110_pmu_data, + .data.size = sizeof(gf110_pmu_data), +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk104.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk104.c new file mode 100644 index 000000000..28fdb8ea9 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk104.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 + */ +#define gf110_pmu_code gk104_pmu_code +#define gf110_pmu_data gk104_pmu_data +#include "priv.h" +#include "fuc/gf110.fuc4.h" + +static void +gk104_pmu_pgob(struct nvkm_pmu *pmu, bool enable) +{ + nv_mask(pmu, 0x000200, 0x00001000, 0x00000000); + nv_rd32(pmu, 0x000200); + nv_mask(pmu, 0x000200, 0x08000000, 0x08000000); + msleep(50); + + nv_mask(pmu, 0x10a78c, 0x00000002, 0x00000002); + nv_mask(pmu, 0x10a78c, 0x00000001, 0x00000001); + nv_mask(pmu, 0x10a78c, 0x00000001, 0x00000000); + + nv_mask(pmu, 0x020004, 0xc0000000, enable ? 0xc0000000 : 0x40000000); + msleep(50); + + nv_mask(pmu, 0x10a78c, 0x00000002, 0x00000000); + nv_mask(pmu, 0x10a78c, 0x00000001, 0x00000001); + nv_mask(pmu, 0x10a78c, 0x00000001, 0x00000000); + + nv_mask(pmu, 0x000200, 0x08000000, 0x00000000); + nv_mask(pmu, 0x000200, 0x00001000, 0x00001000); + nv_rd32(pmu, 0x000200); +} + +struct nvkm_oclass * +gk104_pmu_oclass = &(struct nvkm_pmu_impl) { + .base.handle = NV_SUBDEV(PMU, 0xe4), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_pmu_ctor, + .dtor = _nvkm_pmu_dtor, + .init = _nvkm_pmu_init, + .fini = _nvkm_pmu_fini, + }, + .code.data = gk104_pmu_code, + .code.size = sizeof(gk104_pmu_code), + .data.data = gk104_pmu_data, + .data.size = sizeof(gk104_pmu_data), + .pgob = gk104_pmu_pgob, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk110.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk110.c new file mode 100644 index 000000000..89bb94b0a --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk110.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 portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT 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 gf110_pmu_code gk110_pmu_code +#define gf110_pmu_data gk110_pmu_data +#include "priv.h" +#include "fuc/gf110.fuc4.h" + +#include + +void +gk110_pmu_pgob(struct nvkm_pmu *pmu, bool enable) +{ + 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; + + nv_mask(pmu, 0x000200, 0x00001000, 0x00000000); + nv_rd32(pmu, 0x000200); + nv_mask(pmu, 0x000200, 0x08000000, 0x08000000); + msleep(50); + + nv_mask(pmu, 0x10a78c, 0x00000002, 0x00000002); + nv_mask(pmu, 0x10a78c, 0x00000001, 0x00000001); + nv_mask(pmu, 0x10a78c, 0x00000001, 0x00000000); + + nv_mask(pmu, 0x0206b4, 0x00000000, 0x00000000); + for (i = 0; i < ARRAY_SIZE(magic); i++) { + nv_wr32(pmu, magic[i].addr, magic[i].data); + nv_wait(pmu, magic[i].addr, 0x80000000, 0x00000000); + } + + nv_mask(pmu, 0x10a78c, 0x00000002, 0x00000000); + nv_mask(pmu, 0x10a78c, 0x00000001, 0x00000001); + nv_mask(pmu, 0x10a78c, 0x00000001, 0x00000000); + + nv_mask(pmu, 0x000200, 0x08000000, 0x00000000); + nv_mask(pmu, 0x000200, 0x00001000, 0x00001000); + nv_rd32(pmu, 0x000200); +} + +struct nvkm_oclass * +gk110_pmu_oclass = &(struct nvkm_pmu_impl) { + .base.handle = NV_SUBDEV(PMU, 0xf0), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_pmu_ctor, + .dtor = _nvkm_pmu_dtor, + .init = _nvkm_pmu_init, + .fini = _nvkm_pmu_fini, + }, + .code.data = gk110_pmu_code, + .code.size = sizeof(gk110_pmu_code), + .data.data = gk110_pmu_data, + .data.size = sizeof(gk110_pmu_data), + .pgob = gk110_pmu_pgob, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk208.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk208.c new file mode 100644 index 000000000..b14134ef9 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk208.c @@ -0,0 +1,41 @@ +/* + * 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" + +struct nvkm_oclass * +gk208_pmu_oclass = &(struct nvkm_pmu_impl) { + .base.handle = NV_SUBDEV(PMU, 0x00), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_pmu_ctor, + .dtor = _nvkm_pmu_dtor, + .init = _nvkm_pmu_init, + .fini = _nvkm_pmu_fini, + }, + .code.data = gk208_pmu_code, + .code.size = sizeof(gk208_pmu_code), + .data.data = gk208_pmu_data, + .data.size = sizeof(gk208_pmu_data), + .pgob = gk110_pmu_pgob, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk20a.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk20a.c new file mode 100644 index 000000000..594f746e6 --- /dev/null +++ b/kernel/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. + */ +#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_priv { + struct nvkm_pmu base; + struct nvkm_alarm alarm; + struct gk20a_pmu_dvfs_data *data; +}; + +struct gk20a_pmu_dvfs_dev_status { + unsigned long total; + unsigned long busy; + int cur_state; +}; + +static int +gk20a_pmu_dvfs_target(struct gk20a_pmu_priv *priv, int *state) +{ + struct nvkm_clk *clk = nvkm_clk(priv); + + return nvkm_clk_astate(clk, *state, 0, false); +} + +static int +gk20a_pmu_dvfs_get_cur_state(struct gk20a_pmu_priv *priv, int *state) +{ + struct nvkm_clk *clk = nvkm_clk(priv); + + *state = clk->pstate; + return 0; +} + +static int +gk20a_pmu_dvfs_get_target_state(struct gk20a_pmu_priv *priv, + int *state, int load) +{ + struct gk20a_pmu_dvfs_data *data = priv->data; + struct nvkm_clk *clk = nvkm_clk(priv); + 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); + } + + nv_trace(priv, "cur level = %d, new level = %d\n", cur_level, level); + + *state = level; + + if (level == cur_level) + return 0; + else + return 1; +} + +static int +gk20a_pmu_dvfs_get_dev_status(struct gk20a_pmu_priv *priv, + struct gk20a_pmu_dvfs_dev_status *status) +{ + status->busy = nv_rd32(priv, 0x10a508 + (BUSY_SLOT * 0x10)); + status->total= nv_rd32(priv, 0x10a508 + (CLK_SLOT * 0x10)); + return 0; +} + +static void +gk20a_pmu_dvfs_reset_dev_status(struct gk20a_pmu_priv *priv) +{ + nv_wr32(priv, 0x10a508 + (BUSY_SLOT * 0x10), 0x80000000); + nv_wr32(priv, 0x10a508 + (CLK_SLOT * 0x10), 0x80000000); +} + +static void +gk20a_pmu_dvfs_work(struct nvkm_alarm *alarm) +{ + struct gk20a_pmu_priv *priv = + container_of(alarm, struct gk20a_pmu_priv, alarm); + struct gk20a_pmu_dvfs_data *data = priv->data; + struct gk20a_pmu_dvfs_dev_status status; + struct nvkm_clk *clk = nvkm_clk(priv); + struct nvkm_volt *volt = nvkm_volt(priv); + u32 utilization = 0; + int state, ret; + + /* + * 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; + + ret = gk20a_pmu_dvfs_get_dev_status(priv, &status); + if (ret) { + nv_warn(priv, "failed to get device status\n"); + goto resched; + } + + 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; + nv_trace(priv, "utilization = %d %%, avg_load = %d %%\n", + utilization, data->avg_load); + + ret = gk20a_pmu_dvfs_get_cur_state(priv, &state); + if (ret) { + nv_warn(priv, "failed to get current state\n"); + goto resched; + } + + if (gk20a_pmu_dvfs_get_target_state(priv, &state, data->avg_load)) { + nv_trace(priv, "set new state to %d\n", state); + gk20a_pmu_dvfs_target(priv, &state); + } + +resched: + gk20a_pmu_dvfs_reset_dev_status(priv); + nvkm_timer_alarm(priv, 100000000, alarm); +} + +static int +gk20a_pmu_fini(struct nvkm_object *object, bool suspend) +{ + struct nvkm_pmu *pmu = (void *)object; + struct gk20a_pmu_priv *priv = (void *)pmu; + + nvkm_timer_alarm_cancel(priv, &priv->alarm); + + return nvkm_subdev_fini(&pmu->base, suspend); +} + +static int +gk20a_pmu_init(struct nvkm_object *object) +{ + struct nvkm_pmu *pmu = (void *)object; + struct gk20a_pmu_priv *priv = (void *)pmu; + int ret; + + ret = nvkm_subdev_init(&pmu->base); + if (ret) + return ret; + + pmu->pgob = nvkm_pmu_pgob; + + /* init pwr perf counter */ + nv_wr32(pmu, 0x10a504 + (BUSY_SLOT * 0x10), 0x00200001); + nv_wr32(pmu, 0x10a50c + (BUSY_SLOT * 0x10), 0x00000002); + nv_wr32(pmu, 0x10a50c + (CLK_SLOT * 0x10), 0x00000003); + + nvkm_timer_alarm(pmu, 2000000000, &priv->alarm); + return ret; +} + +static struct gk20a_pmu_dvfs_data +gk20a_dvfs_data= { + .p_load_target = 70, + .p_load_max = 90, + .p_smooth = 1, +}; + +static int +gk20a_pmu_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gk20a_pmu_priv *priv; + int ret; + + ret = nvkm_pmu_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->data = &gk20a_dvfs_data; + + nvkm_alarm_init(&priv->alarm, gk20a_pmu_dvfs_work); + return 0; +} + +struct nvkm_oclass * +gk20a_pmu_oclass = &(struct nvkm_pmu_impl) { + .base.handle = NV_SUBDEV(PMU, 0xea), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gk20a_pmu_ctor, + .dtor = _nvkm_pmu_dtor, + .init = gk20a_pmu_init, + .fini = gk20a_pmu_fini, + }, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gt215.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gt215.c new file mode 100644 index 000000000..30aaeb21d --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gt215.c @@ -0,0 +1,49 @@ +/* + * 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" + +static int +gt215_pmu_init(struct nvkm_object *object) +{ + struct nvkm_pmu *pmu = (void *)object; + nv_mask(pmu, 0x022210, 0x00000001, 0x00000000); + nv_mask(pmu, 0x022210, 0x00000001, 0x00000001); + return nvkm_pmu_init(pmu); +} + +struct nvkm_oclass * +gt215_pmu_oclass = &(struct nvkm_pmu_impl) { + .base.handle = NV_SUBDEV(PMU, 0xa3), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_pmu_ctor, + .dtor = _nvkm_pmu_dtor, + .init = gt215_pmu_init, + .fini = _nvkm_pmu_fini, + }, + .code.data = gt215_pmu_code, + .code.size = sizeof(gt215_pmu_code), + .data.data = gt215_pmu_data, + .data.size = sizeof(gt215_pmu_data), +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/memx.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/memx.c new file mode 100644 index 000000000..b75c5b885 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/memx.c @@ -0,0 +1,200 @@ +#ifndef __NVKM_PMU_MEMX_H__ +#define __NVKM_PMU_MEMX_H__ +#include "priv.h" + +#include + +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_pmu *pmu = memx->pmu; + int i; + + if (memx->c.mthd) { + nv_wr32(pmu, 0x10a1c4, (memx->c.size << 16) | memx->c.mthd); + for (i = 0; i < memx->c.size; i++) + nv_wr32(pmu, 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_memx *memx; + u32 reply[2]; + int ret; + + ret = pmu->message(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 { + nv_wr32(pmu, 0x10a580, 0x00000003); + } while (nv_rd32(pmu, 0x10a580) != 0x00000003); + nv_wr32(pmu, 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; + u32 finish, reply[2]; + + /* flush the cache... */ + memx_out(memx); + + /* release data segment access */ + finish = nv_rd32(pmu, 0x10a1c0) & 0x00ffffff; + nv_wr32(pmu, 0x10a580, 0x00000000); + + /* call MEMX process to execute the script, and wait for reply */ + if (exec) { + pmu->message(pmu, reply, PROC_MEMX, MEMX_MSG_EXEC, + memx->base, finish); + } + + nv_debug(memx->pmu, "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) +{ + nv_debug(memx->pmu, "R[%06x] = 0x%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) +{ + nv_debug(memx->pmu, "R[%06x] & 0x%08x == 0x%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) +{ + nv_debug(memx->pmu, " 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_pmu *pmu = memx->pmu; + u32 heads, x, y, px = 0; + int i, head_sync; + + if (nv_device(pmu)->chipset < 0xd0) { + heads = nv_rd32(pmu, 0x610050); + for (i = 0; i < 2; i++) { + /* Heuristic: sync to head with biggest resolution */ + if (heads & (2 << (i << 3))) { + x = nv_rd32(pmu, 0x610b40 + (0x540 * i)); + y = (x & 0xffff0000) >> 16; + x &= 0x0000ffff; + if ((x * y) > px) { + px = (x * y); + head_sync = i; + } + } + } + } + + if (px == 0) { + nv_debug(memx->pmu, "WAIT VBLANK !NO ACTIVE HEAD\n"); + return; + } + + nv_debug(memx->pmu, "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) +{ + nv_debug(memx->pmu, " MEM TRAIN\n"); + memx_cmd(memx, MEMX_TRAIN, 0, NULL); +} + +int +nvkm_memx_train_result(struct nvkm_pmu *pmu, u32 *res, int rsize) +{ + u32 reply[2], base, size, i; + int ret; + + ret = pmu->message(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 */ + nv_wr32(pmu, 0x10a1c0, 0x02000000 | base); + + for (i = 0; i < size; i++) + res[i] = nv_rd32(pmu, 0x10a1c4); + + return 0; +} + +void +nvkm_memx_block(struct nvkm_memx *memx) +{ + nv_debug(memx->pmu, " HOST BLOCKED\n"); + memx_cmd(memx, MEMX_ENTER, 0, NULL); +} + +void +nvkm_memx_unblock(struct nvkm_memx *memx) +{ + nv_debug(memx->pmu, " HOST UNBLOCKED\n"); + memx_cmd(memx, MEMX_LEAVE, 0, NULL); +} +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/priv.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/priv.h new file mode 100644 index 000000000..799e7c8b8 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/priv.h @@ -0,0 +1,45 @@ +#ifndef __NVKM_PMU_PRIV_H__ +#define __NVKM_PMU_PRIV_H__ +#include +#include + +#define nvkm_pmu_create(p, e, o, d) \ + nvkm_pmu_create_((p), (e), (o), sizeof(**d), (void **)d) +#define nvkm_pmu_destroy(p) \ + nvkm_subdev_destroy(&(p)->base) +#define nvkm_pmu_init(p) ({ \ + struct nvkm_pmu *_pmu = (p); \ + _nvkm_pmu_init(nv_object(_pmu)); \ +}) +#define nvkm_pmu_fini(p,s) ({ \ + struct nvkm_pmu *_pmu = (p); \ + _nvkm_pmu_fini(nv_object(_pmu), (s)); \ +}) + +int nvkm_pmu_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, int, void **); + +int _nvkm_pmu_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); +#define _nvkm_pmu_dtor _nvkm_subdev_dtor +int _nvkm_pmu_init(struct nvkm_object *); +int _nvkm_pmu_fini(struct nvkm_object *, bool); +void nvkm_pmu_pgob(struct nvkm_pmu *pmu, bool enable); + +struct nvkm_pmu_impl { + struct nvkm_oclass base; + struct { + u32 *data; + u32 size; + } code; + struct { + u32 *data; + u32 size; + } data; + + void (*pgob)(struct nvkm_pmu *, bool); +}; + +void gk110_pmu_pgob(struct nvkm_pmu *, bool); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/Kbuild new file mode 100644 index 000000000..5837cf129 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/Kbuild @@ -0,0 +1,13 @@ +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/gf110.o +nvkm-y += nvkm/subdev/therm/gm107.o diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c new file mode 100644 index 000000000..ec327cb64 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c @@ -0,0 +1,367 @@ +/* + * 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 + +static int +nvkm_therm_update_trip(struct nvkm_therm *therm) +{ + struct nvkm_therm_priv *priv = (void *)therm; + struct nvbios_therm_trip_point *trip = priv->fan->bios.trip, + *cur_trip = NULL, + *last_trip = priv->last_trip; + u8 temp = therm->temp_get(therm); + u16 duty, i; + + /* look for the trip point corresponding to the current temperature */ + cur_trip = NULL; + for (i = 0; i < priv->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; + priv->last_trip = cur_trip; + } else { + duty = 0; + priv->last_trip = NULL; + } + + return duty; +} + +static int +nvkm_therm_update_linear(struct nvkm_therm *therm) +{ + struct nvkm_therm_priv *priv = (void *)therm; + u8 linear_min_temp = priv->fan->bios.linear_min_temp; + u8 linear_max_temp = priv->fan->bios.linear_max_temp; + u8 temp = therm->temp_get(therm); + u16 duty; + + /* handle the non-linear part first */ + if (temp < linear_min_temp) + return priv->fan->bios.min_duty; + else if (temp > linear_max_temp) + return priv->fan->bios.max_duty; + + /* we are in the linear zone */ + duty = (temp - linear_min_temp); + duty *= (priv->fan->bios.max_duty - priv->fan->bios.min_duty); + duty /= (linear_max_temp - linear_min_temp); + duty += priv->fan->bios.min_duty; + return duty; +} + +static void +nvkm_therm_update(struct nvkm_therm *therm, int mode) +{ + struct nvkm_timer *ptimer = nvkm_timer(therm); + struct nvkm_therm_priv *priv = (void *)therm; + unsigned long flags; + bool immd = true; + bool poll = true; + int duty = -1; + + spin_lock_irqsave(&priv->lock, flags); + if (mode < 0) + mode = priv->mode; + priv->mode = mode; + + switch (mode) { + case NVKM_THERM_CTRL_MANUAL: + ptimer->alarm_cancel(ptimer, &priv->alarm); + duty = nvkm_therm_fan_get(therm); + if (duty < 0) + duty = 100; + poll = false; + break; + case NVKM_THERM_CTRL_AUTO: + switch(priv->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 (priv->cstate) + duty = priv->cstate; + poll = false; + break; + } + immd = false; + break; + case NVKM_THERM_CTRL_NONE: + default: + ptimer->alarm_cancel(ptimer, &priv->alarm); + poll = false; + } + + if (list_empty(&priv->alarm.head) && poll) + ptimer->alarm(ptimer, 1000000000ULL, &priv->alarm); + spin_unlock_irqrestore(&priv->lock, flags); + + if (duty >= 0) { + nv_debug(therm, "FAN target request: %d%%\n", duty); + nvkm_therm_fan_set(therm, immd, duty); + } +} + +int +nvkm_therm_cstate(struct nvkm_therm *ptherm, int fan, int dir) +{ + struct nvkm_therm_priv *priv = (void *)ptherm; + if (!dir || (dir < 0 && fan < priv->cstate) || + (dir > 0 && fan > priv->cstate)) { + nv_debug(ptherm, "default fan speed -> %d%%\n", fan); + priv->cstate = fan; + nvkm_therm_update(ptherm, -1); + } + return 0; +} + +static void +nvkm_therm_alarm(struct nvkm_alarm *alarm) +{ + struct nvkm_therm_priv *priv = + container_of(alarm, struct nvkm_therm_priv, alarm); + nvkm_therm_update(&priv->base, -1); +} + +int +nvkm_therm_fan_mode(struct nvkm_therm *therm, int mode) +{ + struct nvkm_therm_priv *priv = (void *)therm; + struct nvkm_device *device = nv_device(therm); + 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 && device->card_type >= NV_C0 && + !nvkm_subdev(device, NVDEV_SUBDEV_PMU))) + return -EINVAL; + + /* do not allow automatic fan management if the thermal sensor is + * not available */ + if (mode == NVKM_THERM_CTRL_AUTO && therm->temp_get(therm) < 0) + return -EINVAL; + + if (priv->mode == mode) + return 0; + + nv_info(therm, "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) +{ + struct nvkm_therm_priv *priv = (void *)therm; + + switch (type) { + case NVKM_THERM_ATTR_FAN_MIN_DUTY: + return priv->fan->bios.min_duty; + case NVKM_THERM_ATTR_FAN_MAX_DUTY: + return priv->fan->bios.max_duty; + case NVKM_THERM_ATTR_FAN_MODE: + return priv->mode; + case NVKM_THERM_ATTR_THRS_FAN_BOOST: + return priv->bios_sensor.thrs_fan_boost.temp; + case NVKM_THERM_ATTR_THRS_FAN_BOOST_HYST: + return priv->bios_sensor.thrs_fan_boost.hysteresis; + case NVKM_THERM_ATTR_THRS_DOWN_CLK: + return priv->bios_sensor.thrs_down_clock.temp; + case NVKM_THERM_ATTR_THRS_DOWN_CLK_HYST: + return priv->bios_sensor.thrs_down_clock.hysteresis; + case NVKM_THERM_ATTR_THRS_CRITICAL: + return priv->bios_sensor.thrs_critical.temp; + case NVKM_THERM_ATTR_THRS_CRITICAL_HYST: + return priv->bios_sensor.thrs_critical.hysteresis; + case NVKM_THERM_ATTR_THRS_SHUTDOWN: + return priv->bios_sensor.thrs_shutdown.temp; + case NVKM_THERM_ATTR_THRS_SHUTDOWN_HYST: + return priv->bios_sensor.thrs_shutdown.hysteresis; + } + + return -EINVAL; +} + +int +nvkm_therm_attr_set(struct nvkm_therm *therm, + enum nvkm_therm_attr_type type, int value) +{ + struct nvkm_therm_priv *priv = (void *)therm; + + switch (type) { + case NVKM_THERM_ATTR_FAN_MIN_DUTY: + if (value < 0) + value = 0; + if (value > priv->fan->bios.max_duty) + value = priv->fan->bios.max_duty; + priv->fan->bios.min_duty = value; + return 0; + case NVKM_THERM_ATTR_FAN_MAX_DUTY: + if (value < 0) + value = 0; + if (value < priv->fan->bios.min_duty) + value = priv->fan->bios.min_duty; + priv->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: + priv->bios_sensor.thrs_fan_boost.temp = value; + priv->sensor.program_alarms(therm); + return 0; + case NVKM_THERM_ATTR_THRS_FAN_BOOST_HYST: + priv->bios_sensor.thrs_fan_boost.hysteresis = value; + priv->sensor.program_alarms(therm); + return 0; + case NVKM_THERM_ATTR_THRS_DOWN_CLK: + priv->bios_sensor.thrs_down_clock.temp = value; + priv->sensor.program_alarms(therm); + return 0; + case NVKM_THERM_ATTR_THRS_DOWN_CLK_HYST: + priv->bios_sensor.thrs_down_clock.hysteresis = value; + priv->sensor.program_alarms(therm); + return 0; + case NVKM_THERM_ATTR_THRS_CRITICAL: + priv->bios_sensor.thrs_critical.temp = value; + priv->sensor.program_alarms(therm); + return 0; + case NVKM_THERM_ATTR_THRS_CRITICAL_HYST: + priv->bios_sensor.thrs_critical.hysteresis = value; + priv->sensor.program_alarms(therm); + return 0; + case NVKM_THERM_ATTR_THRS_SHUTDOWN: + priv->bios_sensor.thrs_shutdown.temp = value; + priv->sensor.program_alarms(therm); + return 0; + case NVKM_THERM_ATTR_THRS_SHUTDOWN_HYST: + priv->bios_sensor.thrs_shutdown.hysteresis = value; + priv->sensor.program_alarms(therm); + return 0; + } + + return -EINVAL; +} + +int +_nvkm_therm_init(struct nvkm_object *object) +{ + struct nvkm_therm *therm = (void *)object; + struct nvkm_therm_priv *priv = (void *)therm; + int ret; + + ret = nvkm_subdev_init(&therm->base); + if (ret) + return ret; + + if (priv->suspend >= 0) { + /* restore the pwm value only when on manual or auto mode */ + if (priv->suspend > 0) + nvkm_therm_fan_set(therm, true, priv->fan->percent); + + nvkm_therm_fan_mode(therm, priv->suspend); + } + nvkm_therm_sensor_init(therm); + nvkm_therm_fan_init(therm); + return 0; +} + +int +_nvkm_therm_fini(struct nvkm_object *object, bool suspend) +{ + struct nvkm_therm *therm = (void *)object; + struct nvkm_therm_priv *priv = (void *)therm; + + nvkm_therm_fan_fini(therm, suspend); + nvkm_therm_sensor_fini(therm, suspend); + if (suspend) { + priv->suspend = priv->mode; + priv->mode = NVKM_THERM_CTRL_NONE; + } + + return nvkm_subdev_fini(&therm->base, suspend); +} + +int +nvkm_therm_create_(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, int length, void **pobject) +{ + struct nvkm_therm_priv *priv; + int ret; + + ret = nvkm_subdev_create_(parent, engine, oclass, 0, "PTHERM", + "therm", length, pobject); + priv = *pobject; + if (ret) + return ret; + + nvkm_alarm_init(&priv->alarm, nvkm_therm_alarm); + spin_lock_init(&priv->lock); + spin_lock_init(&priv->sensor.alarm_program_lock); + + priv->base.fan_get = nvkm_therm_fan_user_get; + priv->base.fan_set = nvkm_therm_fan_user_set; + priv->base.fan_sense = nvkm_therm_fan_sense; + priv->base.attr_get = nvkm_therm_attr_get; + priv->base.attr_set = nvkm_therm_attr_set; + priv->mode = priv->suspend = -1; /* undefined */ + return 0; +} + +int +nvkm_therm_preinit(struct nvkm_therm *therm) +{ + 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); + return 0; +} + +void +_nvkm_therm_dtor(struct nvkm_object *object) +{ + struct nvkm_therm_priv *priv = (void *)object; + kfree(priv->fan); + nvkm_subdev_destroy(&priv->base.base); +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c new file mode 100644 index 000000000..434fa745c --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c @@ -0,0 +1,282 @@ +/* + * 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_therm_priv *priv = (void *)therm; + struct nvkm_timer *ptimer = nvkm_timer(priv); + 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) { + nv_debug(therm, "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; + } + + nv_debug(therm, "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 (list_empty(&fan->alarm.head) && 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; + + ptimer->alarm(ptimer, 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) +{ + struct nvkm_therm_priv *priv = (void *)therm; + return priv->fan->get(therm); +} + +int +nvkm_therm_fan_set(struct nvkm_therm *therm, bool immediate, int percent) +{ + struct nvkm_therm_priv *priv = (void *)therm; + return nvkm_fan_update(priv->fan, immediate, percent); +} + +int +nvkm_therm_fan_sense(struct nvkm_therm *therm) +{ + struct nvkm_therm_priv *priv = (void *)therm; + struct nvkm_timer *ptimer = nvkm_timer(therm); + struct nvkm_gpio *gpio = nvkm_gpio(therm); + u32 cycles, cur, prev; + u64 start, end, tach; + + if (priv->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 = ptimer->read(ptimer); + prev = gpio->get(gpio, 0, priv->fan->tach.func, priv->fan->tach.line); + cycles = 0; + do { + usleep_range(500, 1000); /* supports 0 < rpm < 7500 */ + + cur = gpio->get(gpio, 0, priv->fan->tach.func, priv->fan->tach.line); + if (prev != cur) { + if (!start) + start = ptimer->read(ptimer); + cycles++; + prev = cur; + } + } while (cycles < 5 && ptimer->read(ptimer) - start < 250000000); + end = ptimer->read(ptimer); + + 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) +{ + struct nvkm_therm_priv *priv = (void *)therm; + + if (priv->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) +{ + struct nvkm_therm_priv *priv = (void *)therm; + + priv->fan->bios.pwm_freq = 0; + priv->fan->bios.min_duty = 0; + priv->fan->bios.max_duty = 100; + priv->fan->bios.bump_period = 500; + priv->fan->bios.slow_down_period = 2000; + priv->fan->bios.linear_min_temp = 40; + priv->fan->bios.linear_max_temp = 85; +} + +static void +nvkm_therm_fan_safety_checks(struct nvkm_therm *therm) +{ + struct nvkm_therm_priv *priv = (void *)therm; + + if (priv->fan->bios.min_duty > 100) + priv->fan->bios.min_duty = 100; + if (priv->fan->bios.max_duty > 100) + priv->fan->bios.max_duty = 100; + + if (priv->fan->bios.min_duty > priv->fan->bios.max_duty) + priv->fan->bios.min_duty = priv->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_therm_priv *priv = (void *)therm; + struct nvkm_timer *ptimer = nvkm_timer(therm); + + if (suspend) + ptimer->alarm_cancel(ptimer, &priv->fan->alarm); + return 0; +} + +int +nvkm_therm_fan_ctor(struct nvkm_therm *therm) +{ + struct nvkm_therm_priv *priv = (void *)therm; + struct nvkm_gpio *gpio = nvkm_gpio(therm); + struct nvkm_bios *bios = nvkm_bios(therm); + struct dcb_gpio_func func; + int ret; + + /* attempt to locate a drivable fan, and determine control method */ + ret = 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) { + nv_debug(therm, "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; + } + + nv_info(therm, "FAN control: %s\n", priv->fan->type); + + /* read the current speed, it is useful when resuming */ + priv->fan->percent = nvkm_therm_fan_get(therm); + + /* attempt to detect a tachometer connection */ + ret = gpio->find(gpio, 0, DCB_GPIO_FAN_SENSE, 0xff, &priv->fan->tach); + if (ret) + priv->fan->tach.func = DCB_GPIO_UNUSED; + + /* initialise fan bump/slow update handling */ + priv->fan->parent = therm; + nvkm_alarm_init(&priv->fan->alarm, nvkm_fan_alarm); + spin_lock_init(&priv->fan->lock); + + /* other random init... */ + nvkm_therm_fan_set_defaults(therm); + nvbios_perf_fan_parse(bios, &priv->fan->perf); + if (!nvbios_fan_parse(bios, &priv->fan->bios)) { + nv_debug(therm, "parsing the fan table failed\n"); + if (nvbios_therm_fan_parse(bios, &priv->fan->bios)) + nv_error(therm, "parsing both fan tables failed\n"); + } + nvkm_therm_fan_safety_checks(therm); + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fannil.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fannil.c new file mode 100644 index 000000000..534e5970e --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fannil.c @@ -0,0 +1,53 @@ +/* + * 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_therm_priv *tpriv = (void *)therm; + struct nvkm_fan *priv; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + tpriv->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/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fanpwm.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fanpwm.c new file mode 100644 index 000000000..bde5ceaeb --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fanpwm.c @@ -0,0 +1,113 @@ +/* + * 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 +#include + +struct nvkm_fanpwm_priv { + struct nvkm_fan base; + struct dcb_gpio_func func; +}; + +static int +nvkm_fanpwm_get(struct nvkm_therm *therm) +{ + struct nvkm_therm_priv *tpriv = (void *)therm; + struct nvkm_fanpwm_priv *priv = (void *)tpriv->fan; + struct nvkm_gpio *gpio = nvkm_gpio(therm); + int card_type = nv_device(therm)->card_type; + u32 divs, duty; + int ret; + + ret = therm->pwm_get(therm, priv->func.line, &divs, &duty); + if (ret == 0 && divs) { + divs = max(divs, duty); + if (card_type <= NV_40 || (priv->func.log[0] & 1)) + duty = divs - duty; + return (duty * 100) / divs; + } + + return gpio->get(gpio, 0, priv->func.func, priv->func.line) * 100; +} + +static int +nvkm_fanpwm_set(struct nvkm_therm *therm, int percent) +{ + struct nvkm_therm_priv *tpriv = (void *)therm; + struct nvkm_fanpwm_priv *priv = (void *)tpriv->fan; + int card_type = nv_device(therm)->card_type; + u32 divs, duty; + int ret; + + divs = priv->base.perf.pwm_divisor; + if (priv->base.bios.pwm_freq) { + divs = 1; + if (therm->pwm_clock) + divs = therm->pwm_clock(therm, priv->func.line); + divs /= priv->base.bios.pwm_freq; + } + + duty = ((divs * percent) + 99) / 100; + if (card_type <= NV_40 || (priv->func.log[0] & 1)) + duty = divs - duty; + + ret = therm->pwm_set(therm, priv->func.line, divs, duty); + if (ret == 0) + ret = therm->pwm_ctrl(therm, priv->func.line, true); + return ret; +} + +int +nvkm_fanpwm_create(struct nvkm_therm *therm, struct dcb_gpio_func *func) +{ + struct nvkm_device *device = nv_device(therm); + struct nvkm_therm_priv *tpriv = (void *)therm; + struct nvkm_bios *bios = nvkm_bios(therm); + struct nvkm_fanpwm_priv *priv; + struct nvbios_therm_fan fan; + u32 divs, duty; + + nvbios_fan_parse(bios, &fan); + + if (!nvkm_boolopt(device->cfgopt, "NvFanPWM", func->param) || + !therm->pwm_ctrl || fan.type == NVBIOS_THERM_FAN_TOGGLE || + therm->pwm_get(therm, func->line, &divs, &duty) == -ENODEV) + return -ENODEV; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + tpriv->fan = &priv->base; + if (!priv) + return -ENOMEM; + + priv->base.type = "PWM"; + priv->base.get = nvkm_fanpwm_get; + priv->base.set = nvkm_fanpwm_set; + priv->func = *func; + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c new file mode 100644 index 000000000..4ce041e81 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c @@ -0,0 +1,118 @@ +/* + * 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_priv { + 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_priv *priv, int percent) +{ + struct nvkm_therm_priv *tpriv = (void *)priv->base.parent; + struct nvkm_timer *ptimer = nvkm_timer(tpriv); + struct nvkm_gpio *gpio = nvkm_gpio(tpriv); + unsigned long flags; + int duty; + + spin_lock_irqsave(&priv->lock, flags); + if (percent < 0) + percent = priv->percent; + priv->percent = percent; + + duty = !gpio->get(gpio, 0, DCB_GPIO_FAN, 0xff); + gpio->set(gpio, 0, DCB_GPIO_FAN, 0xff, duty); + + if (list_empty(&priv->alarm.head) && percent != (duty * 100)) { + u64 next_change = (percent * priv->period_us) / 100; + if (!duty) + next_change = priv->period_us - next_change; + ptimer->alarm(ptimer, next_change * 1000, &priv->alarm); + } + spin_unlock_irqrestore(&priv->lock, flags); +} + +static void +nvkm_fantog_alarm(struct nvkm_alarm *alarm) +{ + struct nvkm_fantog_priv *priv = + container_of(alarm, struct nvkm_fantog_priv, alarm); + nvkm_fantog_update(priv, -1); +} + +static int +nvkm_fantog_get(struct nvkm_therm *therm) +{ + struct nvkm_therm_priv *tpriv = (void *)therm; + struct nvkm_fantog_priv *priv = (void *)tpriv->fan; + return priv->percent; +} + +static int +nvkm_fantog_set(struct nvkm_therm *therm, int percent) +{ + struct nvkm_therm_priv *tpriv = (void *)therm; + struct nvkm_fantog_priv *priv = (void *)tpriv->fan; + if (therm->pwm_ctrl) + therm->pwm_ctrl(therm, priv->func.line, false); + nvkm_fantog_update(priv, percent); + return 0; +} + +int +nvkm_fantog_create(struct nvkm_therm *therm, struct dcb_gpio_func *func) +{ + struct nvkm_therm_priv *tpriv = (void *)therm; + struct nvkm_fantog_priv *priv; + int ret; + + if (therm->pwm_ctrl) { + ret = therm->pwm_ctrl(therm, func->line, false); + if (ret) + return ret; + } + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + tpriv->fan = &priv->base; + if (!priv) + return -ENOMEM; + + priv->base.type = "toggle"; + priv->base.get = nvkm_fantog_get; + priv->base.set = nvkm_fantog_set; + nvkm_alarm_init(&priv->alarm, nvkm_fantog_alarm); + priv->period_us = 100000; /* 10Hz */ + priv->percent = 100; + priv->func = *func; + spin_lock_init(&priv->lock); + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/g84.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/g84.c new file mode 100644 index 000000000..85b5d0c18 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/g84.c @@ -0,0 +1,266 @@ +/* + * 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 + +struct g84_therm_priv { + struct nvkm_therm_priv base; +}; + +int +g84_temp_get(struct nvkm_therm *therm) +{ + struct nvkm_fuse *fuse = nvkm_fuse(therm); + + if (nv_ro32(fuse, 0x1a8) == 1) + return nv_rd32(therm, 0x20400); + else + return -ENODEV; +} + +void +g84_sensor_setup(struct nvkm_therm *therm) +{ + struct nvkm_fuse *fuse = nvkm_fuse(therm); + + /* enable temperature reading for cards with insane defaults */ + if (nv_ro32(fuse, 0x1a8) == 1) { + nv_mask(therm, 0x20008, 0x80008000, 0x80000000); + nv_mask(therm, 0x2000c, 0x80000003, 0x00000000); + mdelay(20); /* wait for the temperature to stabilize */ + } +} + +static void +g84_therm_program_alarms(struct nvkm_therm *therm) +{ + struct nvkm_therm_priv *priv = (void *)therm; + struct nvbios_therm_sensor *sensor = &priv->bios_sensor; + unsigned long flags; + + spin_lock_irqsave(&priv->sensor.alarm_program_lock, flags); + + /* enable RISING and FALLING IRQs for shutdown, THRS 0, 1, 2 and 4 */ + nv_wr32(therm, 0x20000, 0x000003ff); + + /* shutdown: The computer should be shutdown when reached */ + nv_wr32(therm, 0x20484, sensor->thrs_shutdown.hysteresis); + nv_wr32(therm, 0x20480, sensor->thrs_shutdown.temp); + + /* THRS_1 : fan boost*/ + nv_wr32(therm, 0x204c4, sensor->thrs_fan_boost.temp); + + /* THRS_2 : critical */ + nv_wr32(therm, 0x204c0, sensor->thrs_critical.temp); + + /* THRS_4 : down clock */ + nv_wr32(therm, 0x20414, sensor->thrs_down_clock.temp); + spin_unlock_irqrestore(&priv->sensor.alarm_program_lock, flags); + + nv_debug(therm, + "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) +{ + 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 = nv_rd32(therm, thrs_reg); + + /* program the next threshold */ + if (temp == thrs->temp) { + nv_wr32(therm, thrs_reg, thrs->temp - thrs->hysteresis); + new_state = NVKM_THERM_THRS_HIGHER; + } else { + nv_wr32(therm, thrs_reg, thrs->temp); + new_state = NVKM_THERM_THRS_LOWER; + } + + /* fix the state (in case someone reprogrammed the alarms) */ + cur = therm->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_subdev *subdev) +{ + struct nvkm_therm *therm = nvkm_therm(subdev); + struct nvkm_therm_priv *priv = (void *)therm; + struct nvbios_therm_sensor *sensor = &priv->bios_sensor; + unsigned long flags; + uint32_t intr; + + spin_lock_irqsave(&priv->sensor.alarm_program_lock, flags); + + intr = nv_rd32(therm, 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) + nv_error(therm, "unhandled intr 0x%08x\n", intr); + + /* ACK everything */ + nv_wr32(therm, 0x20100, 0xffffffff); + nv_wr32(therm, 0x1100, 0x10000); /* PBUS */ + + spin_unlock_irqrestore(&priv->sensor.alarm_program_lock, flags); +} + +static int +g84_therm_init(struct nvkm_object *object) +{ + struct g84_therm_priv *priv = (void *)object; + int ret; + + ret = nvkm_therm_init(&priv->base.base); + if (ret) + return ret; + + g84_sensor_setup(&priv->base.base); + return 0; +} + +static int +g84_therm_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct g84_therm_priv *priv; + int ret; + + ret = nvkm_therm_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.base.pwm_ctrl = nv50_fan_pwm_ctrl; + priv->base.base.pwm_get = nv50_fan_pwm_get; + priv->base.base.pwm_set = nv50_fan_pwm_set; + priv->base.base.pwm_clock = nv50_fan_pwm_clock; + priv->base.base.temp_get = g84_temp_get; + priv->base.sensor.program_alarms = g84_therm_program_alarms; + nv_subdev(priv)->intr = g84_therm_intr; + + /* init the thresholds */ + nvkm_therm_sensor_set_threshold_state(&priv->base.base, + NVKM_THERM_THRS_SHUTDOWN, + NVKM_THERM_THRS_LOWER); + nvkm_therm_sensor_set_threshold_state(&priv->base.base, + NVKM_THERM_THRS_FANBOOST, + NVKM_THERM_THRS_LOWER); + nvkm_therm_sensor_set_threshold_state(&priv->base.base, + NVKM_THERM_THRS_CRITICAL, + NVKM_THERM_THRS_LOWER); + nvkm_therm_sensor_set_threshold_state(&priv->base.base, + NVKM_THERM_THRS_DOWNCLOCK, + NVKM_THERM_THRS_LOWER); + + return nvkm_therm_preinit(&priv->base.base); +} + +int +g84_therm_fini(struct nvkm_object *object, bool suspend) +{ + /* Disable PTherm IRQs */ + nv_wr32(object, 0x20000, 0x00000000); + + /* ACK all PTherm IRQs */ + nv_wr32(object, 0x20100, 0xffffffff); + nv_wr32(object, 0x1100, 0x10000); /* PBUS */ + + return _nvkm_therm_fini(object, suspend); +} + +struct nvkm_oclass +g84_therm_oclass = { + .handle = NV_SUBDEV(THERM, 0x84), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = g84_therm_ctor, + .dtor = _nvkm_therm_dtor, + .init = g84_therm_init, + .fini = g84_therm_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gf110.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gf110.c new file mode 100644 index 000000000..46b7e656a --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gf110.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 "priv.h" + +#include + +struct gf110_therm_priv { + struct nvkm_therm_priv base; +}; + +static int +pwm_info(struct nvkm_therm *therm, int line) +{ + u32 gpio = nv_rd32(therm, 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; + } + default: + break; + } + + nv_error(therm, "GPIO %d unknown PWM: 0x%08x\n", line, gpio); + return -ENODEV; +} + +static int +gf110_fan_pwm_ctrl(struct nvkm_therm *therm, int line, bool enable) +{ + u32 data = enable ? 0x00000040 : 0x00000000; + int indx = pwm_info(therm, line); + if (indx < 0) + return indx; + else if (indx < 2) + nv_mask(therm, 0x00d610 + (line * 0x04), 0x000000c0, data); + /* nothing to do for indx == 2, it seems hardwired to PTHERM */ + return 0; +} + +static int +gf110_fan_pwm_get(struct nvkm_therm *therm, int line, u32 *divs, u32 *duty) +{ + int indx = pwm_info(therm, line); + if (indx < 0) + return indx; + else if (indx < 2) { + if (nv_rd32(therm, 0x00d610 + (line * 0x04)) & 0x00000040) { + *divs = nv_rd32(therm, 0x00e114 + (indx * 8)); + *duty = nv_rd32(therm, 0x00e118 + (indx * 8)); + return 0; + } + } else if (indx == 2) { + *divs = nv_rd32(therm, 0x0200d8) & 0x1fff; + *duty = nv_rd32(therm, 0x0200dc) & 0x1fff; + return 0; + } + + return -EINVAL; +} + +static int +gf110_fan_pwm_set(struct nvkm_therm *therm, int line, u32 divs, u32 duty) +{ + int indx = pwm_info(therm, line); + if (indx < 0) + return indx; + else if (indx < 2) { + nv_wr32(therm, 0x00e114 + (indx * 8), divs); + nv_wr32(therm, 0x00e118 + (indx * 8), duty | 0x80000000); + } else if (indx == 2) { + nv_mask(therm, 0x0200d8, 0x1fff, divs); /* keep the high bits */ + nv_wr32(therm, 0x0200dc, duty | 0x40000000); + } + return 0; +} + +static int +gf110_fan_pwm_clock(struct nvkm_therm *therm, int line) +{ + int indx = pwm_info(therm, line); + if (indx < 0) + return 0; + else if (indx < 2) + return (nv_device(therm)->crystal * 1000) / 20; + else + return nv_device(therm)->crystal * 1000 / 10; +} + +int +gf110_therm_init(struct nvkm_object *object) +{ + struct gf110_therm_priv *priv = (void *)object; + int ret; + + ret = nvkm_therm_init(&priv->base.base); + if (ret) + return ret; + + /* enable fan tach, count revolutions per-second */ + nv_mask(priv, 0x00e720, 0x00000003, 0x00000002); + if (priv->base.fan->tach.func != DCB_GPIO_UNUSED) { + nv_mask(priv, 0x00d79c, 0x000000ff, priv->base.fan->tach.line); + nv_wr32(priv, 0x00e724, nv_device(priv)->crystal * 1000); + nv_mask(priv, 0x00e720, 0x00000001, 0x00000001); + } + nv_mask(priv, 0x00e720, 0x00000002, 0x00000000); + + return 0; +} + +static int +gf110_therm_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gf110_therm_priv *priv; + int ret; + + ret = nvkm_therm_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + g84_sensor_setup(&priv->base.base); + + priv->base.base.pwm_ctrl = gf110_fan_pwm_ctrl; + priv->base.base.pwm_get = gf110_fan_pwm_get; + priv->base.base.pwm_set = gf110_fan_pwm_set; + priv->base.base.pwm_clock = gf110_fan_pwm_clock; + priv->base.base.temp_get = g84_temp_get; + priv->base.base.fan_sense = gt215_therm_fan_sense; + priv->base.sensor.program_alarms = nvkm_therm_program_alarms_polling; + return nvkm_therm_preinit(&priv->base.base); +} + +struct nvkm_oclass +gf110_therm_oclass = { + .handle = NV_SUBDEV(THERM, 0xd0), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf110_therm_ctor, + .dtor = _nvkm_therm_dtor, + .init = gf110_therm_init, + .fini = g84_therm_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gm107.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gm107.c new file mode 100644 index 000000000..2fd110f09 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gm107.c @@ -0,0 +1,93 @@ +/* + * 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" + +#include + +struct gm107_therm_priv { + struct nvkm_therm_priv base; +}; + +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) +{ + *divs = nv_rd32(therm, 0x10eb20) & 0x1fff; + *duty = nv_rd32(therm, 0x10eb24) & 0x1fff; + return 0; +} + +static int +gm107_fan_pwm_set(struct nvkm_therm *therm, int line, u32 divs, u32 duty) +{ + nv_mask(therm, 0x10eb10, 0x1fff, divs); /* keep the high bits */ + nv_wr32(therm, 0x10eb14, duty | 0x80000000); + return 0; +} + +static int +gm107_fan_pwm_clock(struct nvkm_therm *therm, int line) +{ + return nv_device(therm)->crystal * 1000; +} + +static int +gm107_therm_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gm107_therm_priv *priv; + int ret; + + ret = nvkm_therm_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.base.pwm_ctrl = gm107_fan_pwm_ctrl; + priv->base.base.pwm_get = gm107_fan_pwm_get; + priv->base.base.pwm_set = gm107_fan_pwm_set; + priv->base.base.pwm_clock = gm107_fan_pwm_clock; + priv->base.base.temp_get = g84_temp_get; + priv->base.base.fan_sense = gt215_therm_fan_sense; + priv->base.sensor.program_alarms = nvkm_therm_program_alarms_polling; + return nvkm_therm_preinit(&priv->base.base); +} + +struct nvkm_oclass +gm107_therm_oclass = { + .handle = NV_SUBDEV(THERM, 0x117), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gm107_therm_ctor, + .dtor = _nvkm_therm_dtor, + .init = gf110_therm_init, + .fini = g84_therm_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gt215.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gt215.c new file mode 100644 index 000000000..e99be2033 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gt215.c @@ -0,0 +1,100 @@ +/* + * 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 + +struct gt215_therm_priv { + struct nvkm_therm_priv base; +}; + +int +gt215_therm_fan_sense(struct nvkm_therm *therm) +{ + u32 tach = nv_rd32(therm, 0x00e728) & 0x0000ffff; + u32 ctrl = nv_rd32(therm, 0x00e720); + if (ctrl & 0x00000001) + return tach * 60 / 2; + return -ENODEV; +} + +static int +gt215_therm_init(struct nvkm_object *object) +{ + struct gt215_therm_priv *priv = (void *)object; + struct dcb_gpio_func *tach = &priv->base.fan->tach; + int ret; + + ret = nvkm_therm_init(&priv->base.base); + if (ret) + return ret; + + g84_sensor_setup(&priv->base.base); + + /* enable fan tach, count revolutions per-second */ + nv_mask(priv, 0x00e720, 0x00000003, 0x00000002); + if (tach->func != DCB_GPIO_UNUSED) { + nv_wr32(priv, 0x00e724, nv_device(priv)->crystal * 1000); + nv_mask(priv, 0x00e720, 0x001f0000, tach->line << 16); + nv_mask(priv, 0x00e720, 0x00000001, 0x00000001); + } + nv_mask(priv, 0x00e720, 0x00000002, 0x00000000); + + return 0; +} + +static int +gt215_therm_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gt215_therm_priv *priv; + int ret; + + ret = nvkm_therm_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.base.pwm_ctrl = nv50_fan_pwm_ctrl; + priv->base.base.pwm_get = nv50_fan_pwm_get; + priv->base.base.pwm_set = nv50_fan_pwm_set; + priv->base.base.pwm_clock = nv50_fan_pwm_clock; + priv->base.base.temp_get = g84_temp_get; + priv->base.base.fan_sense = gt215_therm_fan_sense; + priv->base.sensor.program_alarms = nvkm_therm_program_alarms_polling; + return nvkm_therm_preinit(&priv->base.base); +} + +struct nvkm_oclass +gt215_therm_oclass = { + .handle = NV_SUBDEV(THERM, 0xa3), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gt215_therm_ctor, + .dtor = _nvkm_therm_dtor, + .init = gt215_therm_init, + .fini = g84_therm_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/ic.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/ic.c new file mode 100644 index 000000000..09fc4605e --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/ic.c @@ -0,0 +1,119 @@ +/* + * 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_port *i2c, + struct i2c_board_info *info, void *data) +{ + struct nvkm_therm_priv *priv = data; + struct nvbios_therm_sensor *sensor = &priv->bios_sensor; + struct i2c_client *client; + + request_module("%s%s", I2C_MODULE_PREFIX, info->type); + + client = i2c_new_device(&i2c->adapter, info); + if (!client) + return false; + + if (!client->dev.driver || + to_i2c_driver(client->dev.driver)->detect(client, info)) { + i2c_unregister_device(client); + return false; + } + + nv_info(priv, + "Found an %s at address 0x%x (controlled by lm_sensors, " + "temp offset %+i C)\n", + info->type, info->addr, sensor->offset_constant); + priv->ic = client; + return true; +} + +static struct nvkm_i2c_board_info +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_therm_priv *priv = (void *)therm; + struct nvkm_bios *bios = nvkm_bios(therm); + struct nvkm_i2c *i2c = nvkm_i2c(therm); + struct nvbios_extdev_func extdev_entry; + + if (!nvbios_extdev_find(bios, NVBIOS_EXTDEV_LM89, &extdev_entry)) { + struct nvkm_i2c_board_info board[] = { + { { I2C_BOARD_INFO("lm90", extdev_entry.addr >> 1) }, 0}, + { } + }; + + i2c->identify(i2c, NV_I2C_DEFAULT(0), "monitoring device", + board, probe_monitoring_device, therm); + if (priv->ic) + return; + } + + if (!nvbios_extdev_find(bios, NVBIOS_EXTDEV_ADT7473, &extdev_entry)) { + struct nvkm_i2c_board_info board[] = { + { { I2C_BOARD_INFO("adt7473", extdev_entry.addr >> 1) }, 20 }, + { } + }; + + i2c->identify(i2c, NV_I2C_DEFAULT(0), "monitoring device", + board, probe_monitoring_device, therm); + if (priv->ic) + return; + } + + /* The vbios doesn't provide the address of an exisiting monitoring + device. Let's try our static list. + */ + i2c->identify(i2c, NV_I2C_DEFAULT(0), "monitoring device", + nv_board_infos, probe_monitoring_device, therm); +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv40.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv40.c new file mode 100644 index 000000000..8496fffd4 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv40.c @@ -0,0 +1,225 @@ +/* + * 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 + +struct nv40_therm_priv { + struct nvkm_therm_priv base; +}; + +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) +{ + struct nvkm_device *device = nv_device(therm); + + switch (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) +{ + enum nv40_sensor_style style = nv40_sensor_style(therm); + + /* enable ADC readout and disable the ALARM threshold */ + if (style == NEW_STYLE) { + nv_mask(therm, 0x15b8, 0x80000000, 0); + nv_wr32(therm, 0x15b0, 0x80003fff); + mdelay(20); /* wait for the temperature to stabilize */ + return nv_rd32(therm, 0x15b4) & 0x3fff; + } else if (style == OLD_STYLE) { + nv_wr32(therm, 0x15b0, 0xff); + mdelay(20); /* wait for the temperature to stabilize */ + return nv_rd32(therm, 0x15b4) & 0xff; + } else + return -ENODEV; +} + +static int +nv40_temp_get(struct nvkm_therm *therm) +{ + struct nvkm_therm_priv *priv = (void *)therm; + struct nvbios_therm_sensor *sensor = &priv->bios_sensor; + enum nv40_sensor_style style = nv40_sensor_style(therm); + int core_temp; + + if (style == NEW_STYLE) { + nv_wr32(therm, 0x15b0, 0x80003fff); + core_temp = nv_rd32(therm, 0x15b4) & 0x3fff; + } else if (style == OLD_STYLE) { + nv_wr32(therm, 0x15b0, 0xff); + core_temp = nv_rd32(therm, 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) +{ + u32 mask = enable ? 0x80000000 : 0x0000000; + if (line == 2) nv_mask(therm, 0x0010f0, 0x80000000, mask); + else if (line == 9) nv_mask(therm, 0x0015f4, 0x80000000, mask); + else { + nv_error(therm, "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) +{ + if (line == 2) { + u32 reg = nv_rd32(therm, 0x0010f0); + if (reg & 0x80000000) { + *duty = (reg & 0x7fff0000) >> 16; + *divs = (reg & 0x00007fff); + return 0; + } + } else + if (line == 9) { + u32 reg = nv_rd32(therm, 0x0015f4); + if (reg & 0x80000000) { + *divs = nv_rd32(therm, 0x0015f8); + *duty = (reg & 0x7fffffff); + return 0; + } + } else { + nv_error(therm, "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) +{ + if (line == 2) { + nv_mask(therm, 0x0010f0, 0x7fff7fff, (duty << 16) | divs); + } else + if (line == 9) { + nv_wr32(therm, 0x0015f8, divs); + nv_mask(therm, 0x0015f4, 0x7fffffff, duty); + } else { + nv_error(therm, "unknown pwm ctrl for gpio %d\n", line); + return -ENODEV; + } + + return 0; +} + +void +nv40_therm_intr(struct nvkm_subdev *subdev) +{ + struct nvkm_therm *therm = nvkm_therm(subdev); + uint32_t stat = nv_rd32(therm, 0x1100); + + /* traitement */ + + /* ack all IRQs */ + nv_wr32(therm, 0x1100, 0x70000); + + nv_error(therm, "THERM received an IRQ: stat = %x\n", stat); +} + +static int +nv40_therm_ctor(struct nvkm_object *parent, + struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv40_therm_priv *priv; + int ret; + + ret = nvkm_therm_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.base.pwm_ctrl = nv40_fan_pwm_ctrl; + priv->base.base.pwm_get = nv40_fan_pwm_get; + priv->base.base.pwm_set = nv40_fan_pwm_set; + priv->base.base.temp_get = nv40_temp_get; + priv->base.sensor.program_alarms = nvkm_therm_program_alarms_polling; + nv_subdev(priv)->intr = nv40_therm_intr; + return nvkm_therm_preinit(&priv->base.base); +} + +static int +nv40_therm_init(struct nvkm_object *object) +{ + struct nvkm_therm *therm = (void *)object; + + nv40_sensor_setup(therm); + + return _nvkm_therm_init(object); +} + +struct nvkm_oclass +nv40_therm_oclass = { + .handle = NV_SUBDEV(THERM, 0x40), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv40_therm_ctor, + .dtor = _nvkm_therm_dtor, + .init = nv40_therm_init, + .fini = _nvkm_therm_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv50.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv50.c new file mode 100644 index 000000000..1ef59e892 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv50.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 + * Martin Peres + */ +#include "priv.h" + +#include + +struct nv50_therm_priv { + struct nvkm_therm_priv base; +}; + +static int +pwm_info(struct nvkm_therm *therm, int *line, int *ctrl, int *indx) +{ + 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 { + nv_error(therm, "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) +{ + u32 data = enable ? 0x00000001 : 0x00000000; + int ctrl, id, ret = pwm_info(therm, &line, &ctrl, &id); + if (ret == 0) + nv_mask(therm, ctrl, 0x00010001 << line, data << line); + return ret; +} + +int +nv50_fan_pwm_get(struct nvkm_therm *therm, int line, u32 *divs, u32 *duty) +{ + int ctrl, id, ret = pwm_info(therm, &line, &ctrl, &id); + if (ret) + return ret; + + if (nv_rd32(therm, ctrl) & (1 << line)) { + *divs = nv_rd32(therm, 0x00e114 + (id * 8)); + *duty = nv_rd32(therm, 0x00e118 + (id * 8)); + return 0; + } + + return -EINVAL; +} + +int +nv50_fan_pwm_set(struct nvkm_therm *therm, int line, u32 divs, u32 duty) +{ + int ctrl, id, ret = pwm_info(therm, &line, &ctrl, &id); + if (ret) + return ret; + + nv_wr32(therm, 0x00e114 + (id * 8), divs); + nv_wr32(therm, 0x00e118 + (id * 8), duty | 0x80000000); + return 0; +} + +int +nv50_fan_pwm_clock(struct nvkm_therm *therm, int line) +{ + int chipset = nv_device(therm)->chipset; + int crystal = nv_device(therm)->crystal; + int pwm_clock; + + /* determine the PWM source clock */ + if (chipset > 0x50 && chipset < 0x94) { + u8 pwm_div = nv_rd32(therm, 0x410c); + if (nv_rd32(therm, 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 = (crystal * 1000) >> pwm_div; + pwm_clock /= 20; + } + } else { + pwm_clock = (crystal * 1000) / 20; + } + + return pwm_clock; +} + +static void +nv50_sensor_setup(struct nvkm_therm *therm) +{ + nv_mask(therm, 0x20010, 0x40000000, 0x0); + mdelay(20); /* wait for the temperature to stabilize */ +} + +static int +nv50_temp_get(struct nvkm_therm *therm) +{ + struct nvkm_therm_priv *priv = (void *)therm; + struct nvbios_therm_sensor *sensor = &priv->bios_sensor; + int core_temp; + + core_temp = nv_rd32(therm, 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 int +nv50_therm_ctor(struct nvkm_object *parent, + struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv50_therm_priv *priv; + int ret; + + ret = nvkm_therm_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.base.pwm_ctrl = nv50_fan_pwm_ctrl; + priv->base.base.pwm_get = nv50_fan_pwm_get; + priv->base.base.pwm_set = nv50_fan_pwm_set; + priv->base.base.pwm_clock = nv50_fan_pwm_clock; + priv->base.base.temp_get = nv50_temp_get; + priv->base.sensor.program_alarms = nvkm_therm_program_alarms_polling; + nv_subdev(priv)->intr = nv40_therm_intr; + + return nvkm_therm_preinit(&priv->base.base); +} + +static int +nv50_therm_init(struct nvkm_object *object) +{ + struct nvkm_therm *therm = (void *)object; + + nv50_sensor_setup(therm); + + return _nvkm_therm_init(object); +} + +struct nvkm_oclass +nv50_therm_oclass = { + .handle = NV_SUBDEV(THERM, 0x50), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_therm_ctor, + .dtor = _nvkm_therm_dtor, + .init = nv50_therm_init, + .fini = _nvkm_therm_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/priv.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/priv.h new file mode 100644 index 000000000..916a149ef --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/priv.h @@ -0,0 +1,153 @@ +#ifndef __NVTHERM_PRIV_H__ +#define __NVTHERM_PRIV_H__ +/* + * 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 +#include +#include + +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; +}; + +enum nvkm_therm_thrs_direction { + NVKM_THERM_THRS_FALLING = 0, + NVKM_THERM_THRS_RISING = 1 +}; + +enum nvkm_therm_thrs_state { + NVKM_THERM_THRS_LOWER = 0, + NVKM_THERM_THRS_HIGHER = 1 +}; + +enum nvkm_therm_thrs { + NVKM_THERM_THRS_FANBOOST = 0, + NVKM_THERM_THRS_DOWNCLOCK = 1, + NVKM_THERM_THRS_CRITICAL = 2, + NVKM_THERM_THRS_SHUTDOWN = 3, + NVKM_THERM_THRS_NR +}; + +struct nvkm_therm_priv { + struct nvkm_therm base; + + /* automatic thermal management */ + struct nvkm_alarm alarm; + spinlock_t lock; + struct nvbios_therm_trip_point *last_trip; + int mode; + int cstate; + int suspend; + + /* bios */ + struct nvbios_therm_sensor bios_sensor; + + /* fan priv */ + struct nvkm_fan *fan; + + /* alarms priv */ + struct { + spinlock_t alarm_program_lock; + struct nvkm_alarm therm_poll_alarm; + enum nvkm_therm_thrs_state alarm_state[NVKM_THERM_THRS_NR]; + void (*program_alarms)(struct nvkm_therm *); + } sensor; + + /* what should be done if the card overheats */ + struct { + void (*downclock)(struct nvkm_therm *, bool active); + void (*pause)(struct nvkm_therm *, bool active); + } emergency; + + /* ic */ + struct i2c_client *ic; +}; + +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_fan_sense(struct nvkm_therm *); + +int nvkm_therm_preinit(struct nvkm_therm *); + +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 *); + +void nv40_therm_intr(struct nvkm_subdev *); +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 *); +int g84_therm_fini(struct nvkm_object *, bool suspend); + +int gt215_therm_fan_sense(struct nvkm_therm *); + +int gf110_therm_init(struct nvkm_object *); + +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/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c new file mode 100644 index 000000000..aa13744f3 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c @@ -0,0 +1,259 @@ +/* + * 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) +{ + struct nvkm_therm_priv *priv = (void *)therm; + + priv->bios_sensor.offset_constant = 0; + + priv->bios_sensor.thrs_fan_boost.temp = 90; + priv->bios_sensor.thrs_fan_boost.hysteresis = 3; + + priv->bios_sensor.thrs_down_clock.temp = 95; + priv->bios_sensor.thrs_down_clock.hysteresis = 3; + + priv->bios_sensor.thrs_critical.temp = 105; + priv->bios_sensor.thrs_critical.hysteresis = 5; + + priv->bios_sensor.thrs_shutdown.temp = 135; + priv->bios_sensor.thrs_shutdown.hysteresis = 5; /*not that it matters */ +} + + +static void +nvkm_therm_temp_safety_checks(struct nvkm_therm *therm) +{ + struct nvkm_therm_priv *priv = (void *)therm; + struct nvbios_therm_sensor *s = &priv->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) +{ + struct nvkm_therm_priv *priv = (void *)therm; + priv->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) +{ + struct nvkm_therm_priv *priv = (void *)therm; + return priv->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_therm_priv *priv = (void *)therm; + bool active; + const char *thresolds[] = { + "fanboost", "downclock", "critical", "shutdown" + }; + int temperature = therm->temp_get(therm); + + if (thrs < 0 || thrs > 3) + return; + + if (dir == NVKM_THERM_THRS_FALLING) + nv_info(therm, "temperature (%i C) went below the '%s' threshold\n", + temperature, thresolds[thrs]); + else + nv_info(therm, "temperature (%i C) hit the '%s' threshold\n", + temperature, thresolds[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 (priv->emergency.downclock) + priv->emergency.downclock(therm, active); + break; + case NVKM_THERM_THRS_CRITICAL: + if (priv->emergency.pause) + priv->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->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_priv *priv = + container_of(alarm, struct nvkm_therm_priv, sensor.therm_poll_alarm); + struct nvbios_therm_sensor *sensor = &priv->bios_sensor; + struct nvkm_timer *ptimer = nvkm_timer(priv); + struct nvkm_therm *therm = &priv->base; + unsigned long flags; + + spin_lock_irqsave(&priv->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(&priv->sensor.alarm_program_lock, flags); + + /* schedule the next poll in one second */ + if (therm->temp_get(therm) >= 0 && list_empty(&alarm->head)) + ptimer->alarm(ptimer, 1000000000ULL, alarm); +} + +void +nvkm_therm_program_alarms_polling(struct nvkm_therm *therm) +{ + struct nvkm_therm_priv *priv = (void *)therm; + struct nvbios_therm_sensor *sensor = &priv->bios_sensor; + + nv_debug(therm, + "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(&priv->sensor.therm_poll_alarm); +} + +int +nvkm_therm_sensor_init(struct nvkm_therm *therm) +{ + struct nvkm_therm_priv *priv = (void *)therm; + priv->sensor.program_alarms(therm); + return 0; +} + +int +nvkm_therm_sensor_fini(struct nvkm_therm *therm, bool suspend) +{ + struct nvkm_therm_priv *priv = (void *)therm; + struct nvkm_timer *ptimer = nvkm_timer(therm); + + if (suspend) + ptimer->alarm_cancel(ptimer, &priv->sensor.therm_poll_alarm); + return 0; +} + +void +nvkm_therm_sensor_preinit(struct nvkm_therm *therm) +{ + const char *sensor_avail = "yes"; + + if (therm->temp_get(therm) < 0) + sensor_avail = "no"; + + nv_info(therm, "internal sensor: %s\n", sensor_avail); +} + +int +nvkm_therm_sensor_ctor(struct nvkm_therm *therm) +{ + struct nvkm_therm_priv *priv = (void *)therm; + struct nvkm_bios *bios = nvkm_bios(therm); + + nvkm_alarm_init(&priv->sensor.therm_poll_alarm, alarm_timer_callback); + + nvkm_therm_temp_set_defaults(therm); + if (nvbios_therm_sensor_parse(bios, NVBIOS_THERM_DOMAIN_CORE, + &priv->bios_sensor)) + nv_error(therm, "nvbios_therm_sensor_parse failed\n"); + nvkm_therm_temp_safety_checks(therm); + + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/timer/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/timer/Kbuild new file mode 100644 index 000000000..d1d38b4ba --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/timer/Kbuild @@ -0,0 +1,3 @@ +nvkm-y += nvkm/subdev/timer/base.o +nvkm-y += nvkm/subdev/timer/nv04.o +nvkm-y += nvkm/subdev/timer/gk20a.o diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c new file mode 100644 index 000000000..d894061ce --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.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 + +bool +nvkm_timer_wait_eq(void *obj, u64 nsec, u32 addr, u32 mask, u32 data) +{ + struct nvkm_timer *ptimer = nvkm_timer(obj); + u64 time0; + + time0 = ptimer->read(ptimer); + do { + if (nv_iclass(obj, NV_SUBDEV_CLASS)) { + if ((nv_rd32(obj, addr) & mask) == data) + return true; + } else { + if ((nv_ro32(obj, addr) & mask) == data) + return true; + } + } while (ptimer->read(ptimer) - time0 < nsec); + + return false; +} + +bool +nvkm_timer_wait_ne(void *obj, u64 nsec, u32 addr, u32 mask, u32 data) +{ + struct nvkm_timer *ptimer = nvkm_timer(obj); + u64 time0; + + time0 = ptimer->read(ptimer); + do { + if (nv_iclass(obj, NV_SUBDEV_CLASS)) { + if ((nv_rd32(obj, addr) & mask) != data) + return true; + } else { + if ((nv_ro32(obj, addr) & mask) != data) + return true; + } + } while (ptimer->read(ptimer) - time0 < nsec); + + return false; +} + +bool +nvkm_timer_wait_cb(void *obj, u64 nsec, bool (*func)(void *), void *data) +{ + struct nvkm_timer *ptimer = nvkm_timer(obj); + u64 time0; + + time0 = ptimer->read(ptimer); + do { + if (func(data) == true) + return true; + } while (ptimer->read(ptimer) - time0 < nsec); + + return false; +} + +void +nvkm_timer_alarm(void *obj, u32 nsec, struct nvkm_alarm *alarm) +{ + struct nvkm_timer *ptimer = nvkm_timer(obj); + ptimer->alarm(ptimer, nsec, alarm); +} + +void +nvkm_timer_alarm_cancel(void *obj, struct nvkm_alarm *alarm) +{ + struct nvkm_timer *ptimer = nvkm_timer(obj); + ptimer->alarm_cancel(ptimer, alarm); +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/timer/gk20a.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/timer/gk20a.c new file mode 100644 index 000000000..80e38063d --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/timer/gk20a.c @@ -0,0 +1,56 @@ +/* + * 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" + +static int +gk20a_timer_init(struct nvkm_object *object) +{ + struct nv04_timer_priv *priv = (void *)object; + u32 hi = upper_32_bits(priv->suspend_time); + u32 lo = lower_32_bits(priv->suspend_time); + int ret; + + ret = nvkm_timer_init(&priv->base); + if (ret) + return ret; + + nv_debug(priv, "time low : 0x%08x\n", lo); + nv_debug(priv, "time high : 0x%08x\n", hi); + + /* restore the time before suspend */ + nv_wr32(priv, NV04_PTIMER_TIME_1, hi); + nv_wr32(priv, NV04_PTIMER_TIME_0, lo); + return 0; +} + +struct nvkm_oclass +gk20a_timer_oclass = { + .handle = NV_SUBDEV(TIMER, 0xff), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_timer_ctor, + .dtor = nv04_timer_dtor, + .init = gk20a_timer_init, + .fini = nv04_timer_fini, + } +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.c new file mode 100644 index 000000000..6b7facbe5 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.c @@ -0,0 +1,262 @@ +/* + * 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 + +static u64 +nv04_timer_read(struct nvkm_timer *ptimer) +{ + struct nv04_timer_priv *priv = (void *)ptimer; + u32 hi, lo; + + do { + hi = nv_rd32(priv, NV04_PTIMER_TIME_1); + lo = nv_rd32(priv, NV04_PTIMER_TIME_0); + } while (hi != nv_rd32(priv, NV04_PTIMER_TIME_1)); + + return ((u64)hi << 32 | lo); +} + +static void +nv04_timer_alarm_trigger(struct nvkm_timer *ptimer) +{ + struct nv04_timer_priv *priv = (void *)ptimer; + struct nvkm_alarm *alarm, *atemp; + unsigned long flags; + LIST_HEAD(exec); + + /* move any due alarms off the pending list */ + spin_lock_irqsave(&priv->lock, flags); + list_for_each_entry_safe(alarm, atemp, &priv->alarms, head) { + if (alarm->timestamp <= ptimer->read(ptimer)) + list_move_tail(&alarm->head, &exec); + } + + /* reschedule interrupt for next alarm time */ + if (!list_empty(&priv->alarms)) { + alarm = list_first_entry(&priv->alarms, typeof(*alarm), head); + nv_wr32(priv, NV04_PTIMER_ALARM_0, alarm->timestamp); + nv_wr32(priv, NV04_PTIMER_INTR_EN_0, 0x00000001); + } else { + nv_wr32(priv, NV04_PTIMER_INTR_EN_0, 0x00000000); + } + spin_unlock_irqrestore(&priv->lock, flags); + + /* execute any pending alarm handlers */ + list_for_each_entry_safe(alarm, atemp, &exec, head) { + list_del_init(&alarm->head); + alarm->func(alarm); + } +} + +static void +nv04_timer_alarm(struct nvkm_timer *ptimer, u64 time, struct nvkm_alarm *alarm) +{ + struct nv04_timer_priv *priv = (void *)ptimer; + struct nvkm_alarm *list; + unsigned long flags; + + alarm->timestamp = ptimer->read(ptimer) + time; + + /* append new alarm to list, in soonest-alarm-first order */ + spin_lock_irqsave(&priv->lock, flags); + if (!time) { + if (!list_empty(&alarm->head)) + list_del(&alarm->head); + } else { + list_for_each_entry(list, &priv->alarms, head) { + if (list->timestamp > alarm->timestamp) + break; + } + list_add_tail(&alarm->head, &list->head); + } + spin_unlock_irqrestore(&priv->lock, flags); + + /* process pending alarms */ + nv04_timer_alarm_trigger(ptimer); +} + +static void +nv04_timer_alarm_cancel(struct nvkm_timer *ptimer, struct nvkm_alarm *alarm) +{ + struct nv04_timer_priv *priv = (void *)ptimer; + unsigned long flags; + spin_lock_irqsave(&priv->lock, flags); + list_del_init(&alarm->head); + spin_unlock_irqrestore(&priv->lock, flags); +} + +static void +nv04_timer_intr(struct nvkm_subdev *subdev) +{ + struct nv04_timer_priv *priv = (void *)subdev; + u32 stat = nv_rd32(priv, NV04_PTIMER_INTR_0); + + if (stat & 0x00000001) { + nv04_timer_alarm_trigger(&priv->base); + nv_wr32(priv, NV04_PTIMER_INTR_0, 0x00000001); + stat &= ~0x00000001; + } + + if (stat) { + nv_error(priv, "unknown stat 0x%08x\n", stat); + nv_wr32(priv, NV04_PTIMER_INTR_0, stat); + } +} + +int +nv04_timer_fini(struct nvkm_object *object, bool suspend) +{ + struct nv04_timer_priv *priv = (void *)object; + if (suspend) + priv->suspend_time = nv04_timer_read(&priv->base); + nv_wr32(priv, NV04_PTIMER_INTR_EN_0, 0x00000000); + return nvkm_timer_fini(&priv->base, suspend); +} + +static int +nv04_timer_init(struct nvkm_object *object) +{ + struct nvkm_device *device = nv_device(object); + struct nv04_timer_priv *priv = (void *)object; + u32 m = 1, f, n, d, lo, hi; + int ret; + + ret = nvkm_timer_init(&priv->base); + if (ret) + return ret; + + /* aim for 31.25MHz, which gives us nanosecond timestamps */ + d = 1000000 / 32; + + /* determine base clock for timer source */ +#if 0 /*XXX*/ + if (device->chipset < 0x40) { + n = nvkm_hw_get_clock(device, PLL_CORE); + } else +#endif + if (device->chipset <= 0x40) { + /*XXX: figure this out */ + f = -1; + n = 0; + } else { + f = device->crystal; + n = f; + while (n < (d * 2)) { + n += (n / m); + m++; + } + + nv_wr32(priv, 0x009220, m - 1); + } + + if (!n) { + nv_warn(priv, "unknown input clock freq\n"); + if (!nv_rd32(priv, NV04_PTIMER_NUMERATOR) || + !nv_rd32(priv, NV04_PTIMER_DENOMINATOR)) { + nv_wr32(priv, NV04_PTIMER_NUMERATOR, 1); + nv_wr32(priv, NV04_PTIMER_DENOMINATOR, 1); + } + return 0; + } + + /* 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; + } + + /* restore the time before suspend */ + lo = priv->suspend_time; + hi = (priv->suspend_time >> 32); + + nv_debug(priv, "input frequency : %dHz\n", f); + nv_debug(priv, "input multiplier: %d\n", m); + nv_debug(priv, "numerator : 0x%08x\n", n); + nv_debug(priv, "denominator : 0x%08x\n", d); + nv_debug(priv, "timer frequency : %dHz\n", (f * m) * d / n); + nv_debug(priv, "time low : 0x%08x\n", lo); + nv_debug(priv, "time high : 0x%08x\n", hi); + + nv_wr32(priv, NV04_PTIMER_NUMERATOR, n); + nv_wr32(priv, NV04_PTIMER_DENOMINATOR, d); + nv_wr32(priv, NV04_PTIMER_INTR_0, 0xffffffff); + nv_wr32(priv, NV04_PTIMER_INTR_EN_0, 0x00000000); + nv_wr32(priv, NV04_PTIMER_TIME_1, hi); + nv_wr32(priv, NV04_PTIMER_TIME_0, lo); + return 0; +} + +void +nv04_timer_dtor(struct nvkm_object *object) +{ + struct nv04_timer_priv *priv = (void *)object; + return nvkm_timer_destroy(&priv->base); +} + +int +nv04_timer_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv04_timer_priv *priv; + int ret; + + ret = nvkm_timer_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.base.intr = nv04_timer_intr; + priv->base.read = nv04_timer_read; + priv->base.alarm = nv04_timer_alarm; + priv->base.alarm_cancel = nv04_timer_alarm_cancel; + priv->suspend_time = 0; + + INIT_LIST_HEAD(&priv->alarms); + spin_lock_init(&priv->lock); + return 0; +} + +struct nvkm_oclass +nv04_timer_oclass = { + .handle = NV_SUBDEV(TIMER, 0x04), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_timer_ctor, + .dtor = nv04_timer_dtor, + .init = nv04_timer_init, + .fini = nv04_timer_fini, + } +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.h new file mode 100644 index 000000000..89996a982 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.h @@ -0,0 +1,25 @@ +#ifndef __NVKM_TIMER_NV04_H__ +#define __NVKM_TIMER_NV04_H__ +#include "priv.h" + +#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 + +struct nv04_timer_priv { + struct nvkm_timer base; + struct list_head alarms; + spinlock_t lock; + u64 suspend_time; +}; + +int nv04_timer_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); +void nv04_timer_dtor(struct nvkm_object *); +int nv04_timer_fini(struct nvkm_object *, bool); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/timer/priv.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/timer/priv.h new file mode 100644 index 000000000..08e29a3da --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/timer/priv.h @@ -0,0 +1,4 @@ +#ifndef __NVKM_TIMER_PRIV_H__ +#define __NVKM_TIMER_PRIV_H__ +#include +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/volt/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/volt/Kbuild new file mode 100644 index 000000000..6b46ff421 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/volt/Kbuild @@ -0,0 +1,4 @@ +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/gk20a.o diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/volt/base.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/volt/base.c new file mode 100644 index 000000000..39f15803f --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/volt/base.c @@ -0,0 +1,204 @@ +/* + * 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 int +nvkm_volt_get(struct nvkm_volt *volt) +{ + if (volt->vid_get) { + int ret = volt->vid_get(volt), i; + 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; + } + return -ENODEV; +} + +static int +nvkm_volt_set(struct nvkm_volt *volt, u32 uv) +{ + if (volt->vid_set) { + int i, ret = -EINVAL; + for (i = 0; i < volt->vid_nr; i++) { + if (volt->vid[i].uv == uv) { + ret = volt->vid_set(volt, volt->vid[i].vid); + nv_debug(volt, "set %duv: %d\n", uv, ret); + break; + } + } + return ret; + } + return -ENODEV; +} + +static int +nvkm_volt_map(struct nvkm_volt *volt, u8 id) +{ + struct nvkm_bios *bios = nvkm_bios(volt); + struct nvbios_vmap_entry info; + u8 ver, len; + u16 vmap; + + vmap = nvbios_vmap_entry_parse(bios, id, &ver, &len, &info); + if (vmap) { + if (info.link != 0xff) { + int ret = nvkm_volt_map(volt, info.link); + if (ret < 0) + return ret; + info.min += ret; + } + return info.min; + } + + return id ? id * 10000 : -ENODEV; +} + +static int +nvkm_volt_set_id(struct nvkm_volt *volt, u8 id, int condition) +{ + int ret = nvkm_volt_map(volt, id); + if (ret >= 0) { + int prev = nvkm_volt_get(volt); + if (!condition || prev < 0 || + (condition < 0 && ret < prev) || + (condition > 0 && ret > prev)) { + 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 nvbios_volt_entry ivid; + struct nvbios_volt info; + u8 ver, hdr, cnt, len; + u16 data; + int i; + + data = nvbios_volt_parse(bios, &ver, &hdr, &cnt, &len, &info); + if (data && info.vidmask && info.base && info.step) { + 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) { + 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->vid_mask = info.vidmask; + } +} + +int +_nvkm_volt_init(struct nvkm_object *object) +{ + struct nvkm_volt *volt = (void *)object; + int ret; + + ret = nvkm_subdev_init(&volt->base); + if (ret) + return ret; + + ret = volt->get(volt); + if (ret < 0) { + if (ret != -ENODEV) + nv_debug(volt, "current voltage unknown\n"); + return 0; + } + + nv_info(volt, "GPU voltage: %duv\n", ret); + return 0; +} + +void +_nvkm_volt_dtor(struct nvkm_object *object) +{ + struct nvkm_volt *volt = (void *)object; + nvkm_subdev_destroy(&volt->base); +} + +int +nvkm_volt_create_(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, int length, void **pobject) +{ + struct nvkm_bios *bios = nvkm_bios(parent); + struct nvkm_volt *volt; + int ret, i; + + ret = nvkm_subdev_create_(parent, engine, oclass, 0, "VOLT", + "voltage", length, pobject); + volt = *pobject; + if (ret) + return ret; + + volt->get = nvkm_volt_get; + volt->set = nvkm_volt_set; + volt->set_id = nvkm_volt_set_id; + + /* Assuming the non-bios device should build the voltage table later */ + if (bios) + nvkm_volt_parse_bios(bios, volt); + + if (volt->vid_nr) { + for (i = 0; i < volt->vid_nr; i++) { + nv_debug(volt, "VID %02x: %duv\n", + volt->vid[i].vid, volt->vid[i].uv); + } + + /*XXX: this is an assumption.. there probably exists boards + * out there with i2c-connected voltage controllers too.. + */ + ret = nvkm_voltgpio_init(volt); + if (ret == 0) { + volt->vid_get = nvkm_voltgpio_get; + volt->vid_set = nvkm_voltgpio_set; + } + } + + return ret; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gk20a.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gk20a.c new file mode 100644 index 000000000..871fd5101 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gk20a.c @@ -0,0 +1,197 @@ +/* + * 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 __KERNEL__ +#include +#endif + +struct cvb_coef { + int c0; + int c1; + int c2; + int c3; + int c4; + int c5; +}; + +struct gk20a_volt_priv { + struct nvkm_volt base; + struct regulator *vdd; +}; + +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) +{ + int mv; + + mv = gk20a_volt_get_cvb_t_voltage(speedo, -10, 100, 10, coef); + mv = DIV_ROUND_UP(mv, 1000); + + return mv * 1000; +} + +static int +gk20a_volt_vid_get(struct nvkm_volt *volt) +{ + struct gk20a_volt_priv *priv = (void *)volt; + int i, uv; + + uv = regulator_get_voltage(priv->vdd); + + for (i = 0; i < volt->vid_nr; i++) + if (volt->vid[i].uv >= uv) + return i; + + return -EINVAL; +} + +static int +gk20a_volt_vid_set(struct nvkm_volt *volt, u8 vid) +{ + struct gk20a_volt_priv *priv = (void *)volt; + + nv_debug(volt, "set voltage as %duv\n", volt->vid[vid].uv); + return regulator_set_voltage(priv->vdd, volt->vid[vid].uv, 1200000); +} + +static int +gk20a_volt_set_id(struct nvkm_volt *volt, u8 id, int condition) +{ + struct gk20a_volt_priv *priv = (void *)volt; + int prev_uv = regulator_get_voltage(priv->vdd); + int target_uv = volt->vid[id].uv; + int ret; + + nv_debug(volt, "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, volt->vid[id].vid); + } else { + ret = 0; + } + + return ret; +} + +static int +gk20a_volt_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gk20a_volt_priv *priv; + struct nvkm_volt *volt; + struct nouveau_platform_device *plat; + int i, ret, uv; + + ret = nvkm_volt_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + volt = &priv->base; + + plat = nv_device_to_platform(nv_device(parent)); + + uv = regulator_get_voltage(plat->gpu->vdd); + nv_info(priv, "The default voltage is %duV\n", uv); + + priv->vdd = plat->gpu->vdd; + priv->base.vid_get = gk20a_volt_vid_get; + priv->base.vid_set = gk20a_volt_vid_set; + priv->base.set_id = gk20a_volt_set_id; + + volt->vid_nr = ARRAY_SIZE(gk20a_cvb_coef); + nv_debug(priv, "%s - vid_nr = %d\n", __func__, volt->vid_nr); + for (i = 0; i < volt->vid_nr; i++) { + volt->vid[i].vid = i; + volt->vid[i].uv = gk20a_volt_calc_voltage(&gk20a_cvb_coef[i], + plat->gpu_speedo); + nv_debug(priv, "%2d: vid=%d, uv=%d\n", i, volt->vid[i].vid, + volt->vid[i].uv); + } + + return 0; +} + +struct nvkm_oclass +gk20a_volt_oclass = { + .handle = NV_SUBDEV(VOLT, 0xea), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gk20a_volt_ctor, + .dtor = _nvkm_volt_dtor, + .init = _nvkm_volt_init, + .fini = _nvkm_volt_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gpio.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gpio.c new file mode 100644 index 000000000..b778deb32 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gpio.c @@ -0,0 +1,96 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include +#include +#include +#include + +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 = nvkm_gpio(volt); + u8 vid = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(tags); i++) { + if (volt->vid_mask & (1 << i)) { + int ret = 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 = nvkm_gpio(volt); + int i; + + for (i = 0; i < ARRAY_SIZE(tags); i++, vid >>= 1) { + if (volt->vid_mask & (1 << i)) { + int ret = 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_gpio *gpio = nvkm_gpio(volt); + 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 = gpio->find(gpio, 0, tags[i], 0xff, &func); + if (ret) { + if (ret != -ENOENT) + return ret; + nv_debug(volt, "VID bit %d has no GPIO\n", i); + volt->vid_mask &= ~(1 << i); + } + } + } + + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/volt/nv40.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/volt/nv40.c new file mode 100644 index 000000000..0ac5a3f8c --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/volt/nv40.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 + +struct nv40_volt_priv { + struct nvkm_volt base; +}; + +static int +nv40_volt_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv40_volt_priv *priv; + int ret; + + ret = nvkm_volt_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + return 0; +} + +struct nvkm_oclass +nv40_volt_oclass = { + .handle = NV_SUBDEV(VOLT, 0x40), + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv40_volt_ctor, + .dtor = _nvkm_volt_dtor, + .init = _nvkm_volt_init, + .fini = _nvkm_volt_fini, + }, +}; -- cgit 1.2.3-korg