diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 10:05:51 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 10:05:51 +0000 |
commit | 5d1646d90e1f2cceb9f0828f4b28318cd0ec7744 (patch) | |
tree | a94efe259b9009378be6d90eb30d2b019d95c194 /drivers/gpu/drm/nouveau/nvkm/subdev/pmu/memx.c | |
parent | Initial commit. (diff) | |
download | linux-5d1646d90e1f2cceb9f0828f4b28318cd0ec7744.tar.xz linux-5d1646d90e1f2cceb9f0828f4b28318cd0ec7744.zip |
Adding upstream version 5.10.209.upstream/5.10.209
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/gpu/drm/nouveau/nvkm/subdev/pmu/memx.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/subdev/pmu/memx.c | 204 |
1 files changed, 204 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/memx.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/memx.c new file mode 100644 index 000000000..22eaebefc --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/memx.c @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: MIT +#ifndef __NVKM_PMU_MEMX_H__ +#define __NVKM_PMU_MEMX_H__ +#include "priv.h" + +struct nvkm_memx { + struct nvkm_pmu *pmu; + u32 base; + u32 size; + struct { + u32 mthd; + u32 size; + u32 data[64]; + } c; +}; + +static void +memx_out(struct nvkm_memx *memx) +{ + struct nvkm_device *device = memx->pmu->subdev.device; + int i; + + if (memx->c.mthd) { + nvkm_wr32(device, 0x10a1c4, (memx->c.size << 16) | memx->c.mthd); + for (i = 0; i < memx->c.size; i++) + nvkm_wr32(device, 0x10a1c4, memx->c.data[i]); + memx->c.mthd = 0; + memx->c.size = 0; + } +} + +static void +memx_cmd(struct nvkm_memx *memx, u32 mthd, u32 size, u32 data[]) +{ + if ((memx->c.size + size >= ARRAY_SIZE(memx->c.data)) || + (memx->c.mthd && memx->c.mthd != mthd)) + memx_out(memx); + memcpy(&memx->c.data[memx->c.size], data, size * sizeof(data[0])); + memx->c.size += size; + memx->c.mthd = mthd; +} + +int +nvkm_memx_init(struct nvkm_pmu *pmu, struct nvkm_memx **pmemx) +{ + struct nvkm_device *device = pmu->subdev.device; + struct nvkm_memx *memx; + u32 reply[2]; + int ret; + + ret = nvkm_pmu_send(pmu, reply, PROC_MEMX, MEMX_MSG_INFO, + MEMX_INFO_DATA, 0); + if (ret) + return ret; + + memx = *pmemx = kzalloc(sizeof(*memx), GFP_KERNEL); + if (!memx) + return -ENOMEM; + memx->pmu = pmu; + memx->base = reply[0]; + memx->size = reply[1]; + + /* acquire data segment access */ + do { + nvkm_wr32(device, 0x10a580, 0x00000003); + } while (nvkm_rd32(device, 0x10a580) != 0x00000003); + nvkm_wr32(device, 0x10a1c0, 0x01000000 | memx->base); + return 0; +} + +int +nvkm_memx_fini(struct nvkm_memx **pmemx, bool exec) +{ + struct nvkm_memx *memx = *pmemx; + struct nvkm_pmu *pmu = memx->pmu; + struct nvkm_subdev *subdev = &pmu->subdev; + struct nvkm_device *device = subdev->device; + u32 finish, reply[2]; + + /* flush the cache... */ + memx_out(memx); + + /* release data segment access */ + finish = nvkm_rd32(device, 0x10a1c0) & 0x00ffffff; + nvkm_wr32(device, 0x10a580, 0x00000000); + + /* call MEMX process to execute the script, and wait for reply */ + if (exec) { + nvkm_pmu_send(pmu, reply, PROC_MEMX, MEMX_MSG_EXEC, + memx->base, finish); + nvkm_debug(subdev, "Exec took %uns, PMU_IN %08x\n", + reply[0], reply[1]); + } + + kfree(memx); + return 0; +} + +void +nvkm_memx_wr32(struct nvkm_memx *memx, u32 addr, u32 data) +{ + nvkm_debug(&memx->pmu->subdev, "R[%06x] = %08x\n", addr, data); + memx_cmd(memx, MEMX_WR32, 2, (u32[]){ addr, data }); +} + +void +nvkm_memx_wait(struct nvkm_memx *memx, + u32 addr, u32 mask, u32 data, u32 nsec) +{ + nvkm_debug(&memx->pmu->subdev, "R[%06x] & %08x == %08x, %d us\n", + addr, mask, data, nsec); + memx_cmd(memx, MEMX_WAIT, 4, (u32[]){ addr, mask, data, nsec }); + memx_out(memx); /* fuc can't handle multiple */ +} + +void +nvkm_memx_nsec(struct nvkm_memx *memx, u32 nsec) +{ + nvkm_debug(&memx->pmu->subdev, " DELAY = %d ns\n", nsec); + memx_cmd(memx, MEMX_DELAY, 1, (u32[]){ nsec }); + memx_out(memx); /* fuc can't handle multiple */ +} + +void +nvkm_memx_wait_vblank(struct nvkm_memx *memx) +{ + struct nvkm_subdev *subdev = &memx->pmu->subdev; + struct nvkm_device *device = subdev->device; + u32 heads, x, y, px = 0; + int i, head_sync; + + if (device->chipset < 0xd0) { + heads = nvkm_rd32(device, 0x610050); + for (i = 0; i < 2; i++) { + /* Heuristic: sync to head with biggest resolution */ + if (heads & (2 << (i << 3))) { + x = nvkm_rd32(device, 0x610b40 + (0x540 * i)); + y = (x & 0xffff0000) >> 16; + x &= 0x0000ffff; + if ((x * y) > px) { + px = (x * y); + head_sync = i; + } + } + } + } + + if (px == 0) { + nvkm_debug(subdev, "WAIT VBLANK !NO ACTIVE HEAD\n"); + return; + } + + nvkm_debug(subdev, "WAIT VBLANK HEAD%d\n", head_sync); + memx_cmd(memx, MEMX_VBLANK, 1, (u32[]){ head_sync }); + memx_out(memx); /* fuc can't handle multiple */ +} + +void +nvkm_memx_train(struct nvkm_memx *memx) +{ + nvkm_debug(&memx->pmu->subdev, " MEM TRAIN\n"); + memx_cmd(memx, MEMX_TRAIN, 0, NULL); +} + +int +nvkm_memx_train_result(struct nvkm_pmu *pmu, u32 *res, int rsize) +{ + struct nvkm_device *device = pmu->subdev.device; + u32 reply[2], base, size, i; + int ret; + + ret = nvkm_pmu_send(pmu, reply, PROC_MEMX, MEMX_MSG_INFO, + MEMX_INFO_TRAIN, 0); + if (ret) + return ret; + + base = reply[0]; + size = reply[1] >> 2; + if (size > rsize) + return -ENOMEM; + + /* read the packet */ + nvkm_wr32(device, 0x10a1c0, 0x02000000 | base); + + for (i = 0; i < size; i++) + res[i] = nvkm_rd32(device, 0x10a1c4); + + return 0; +} + +void +nvkm_memx_block(struct nvkm_memx *memx) +{ + nvkm_debug(&memx->pmu->subdev, " HOST BLOCKED\n"); + memx_cmd(memx, MEMX_ENTER, 0, NULL); +} + +void +nvkm_memx_unblock(struct nvkm_memx *memx) +{ + nvkm_debug(&memx->pmu->subdev, " HOST UNBLOCKED\n"); + memx_cmd(memx, MEMX_LEAVE, 0, NULL); +} +#endif |