1 /* $NetBSD: nouveau_nvkm_subdev_pmu_memx.c,v 1.3 2021/12/18 23:45:41 riastradh Exp $ */ 2 3 // SPDX-License-Identifier: MIT 4 #include <sys/cdefs.h> 5 __KERNEL_RCSID(0, "$NetBSD: nouveau_nvkm_subdev_pmu_memx.c,v 1.3 2021/12/18 23:45:41 riastradh Exp $"); 6 7 #ifndef __NVKM_PMU_MEMX_H__ 8 #define __NVKM_PMU_MEMX_H__ 9 #include "priv.h" 10 11 struct nvkm_memx { 12 struct nvkm_pmu *pmu; 13 u32 base; 14 u32 size; 15 struct { 16 u32 mthd; 17 u32 size; 18 u32 data[64]; 19 } c; 20 }; 21 22 static void 23 memx_out(struct nvkm_memx *memx) 24 { 25 struct nvkm_device *device = memx->pmu->subdev.device; 26 int i; 27 28 if (memx->c.mthd) { 29 nvkm_wr32(device, 0x10a1c4, (memx->c.size << 16) | memx->c.mthd); 30 for (i = 0; i < memx->c.size; i++) 31 nvkm_wr32(device, 0x10a1c4, memx->c.data[i]); 32 memx->c.mthd = 0; 33 memx->c.size = 0; 34 } 35 } 36 37 static void 38 memx_cmd(struct nvkm_memx *memx, u32 mthd, u32 size, u32 data[]) 39 { 40 if ((memx->c.size + size >= ARRAY_SIZE(memx->c.data)) || 41 (memx->c.mthd && memx->c.mthd != mthd)) 42 memx_out(memx); 43 memcpy(&memx->c.data[memx->c.size], data, size * sizeof(data[0])); 44 memx->c.size += size; 45 memx->c.mthd = mthd; 46 } 47 48 int 49 nvkm_memx_init(struct nvkm_pmu *pmu, struct nvkm_memx **pmemx) 50 { 51 struct nvkm_device *device = pmu->subdev.device; 52 struct nvkm_memx *memx; 53 u32 reply[2]; 54 int ret; 55 56 ret = nvkm_pmu_send(pmu, reply, PROC_MEMX, MEMX_MSG_INFO, 57 MEMX_INFO_DATA, 0); 58 if (ret) 59 return ret; 60 61 memx = *pmemx = kzalloc(sizeof(*memx), GFP_KERNEL); 62 if (!memx) 63 return -ENOMEM; 64 memx->pmu = pmu; 65 memx->base = reply[0]; 66 memx->size = reply[1]; 67 68 /* acquire data segment access */ 69 do { 70 nvkm_wr32(device, 0x10a580, 0x00000003); 71 } while (nvkm_rd32(device, 0x10a580) != 0x00000003); 72 nvkm_wr32(device, 0x10a1c0, 0x01000000 | memx->base); 73 return 0; 74 } 75 76 int 77 nvkm_memx_fini(struct nvkm_memx **pmemx, bool exec) 78 { 79 struct nvkm_memx *memx = *pmemx; 80 struct nvkm_pmu *pmu = memx->pmu; 81 struct nvkm_subdev *subdev = &pmu->subdev; 82 struct nvkm_device *device = subdev->device; 83 u32 finish, reply[2]; 84 85 /* flush the cache... */ 86 memx_out(memx); 87 88 /* release data segment access */ 89 finish = nvkm_rd32(device, 0x10a1c0) & 0x00ffffff; 90 nvkm_wr32(device, 0x10a580, 0x00000000); 91 92 /* call MEMX process to execute the script, and wait for reply */ 93 if (exec) { 94 nvkm_pmu_send(pmu, reply, PROC_MEMX, MEMX_MSG_EXEC, 95 memx->base, finish); 96 nvkm_debug(subdev, "Exec took %uns, PMU_IN %08x\n", 97 reply[0], reply[1]); 98 } 99 100 kfree(memx); 101 return 0; 102 } 103 104 void 105 nvkm_memx_wr32(struct nvkm_memx *memx, u32 addr, u32 data) 106 { 107 nvkm_debug(&memx->pmu->subdev, "R[%06x] = %08x\n", addr, data); 108 memx_cmd(memx, MEMX_WR32, 2, (u32[]){ addr, data }); 109 } 110 111 void 112 nvkm_memx_wait(struct nvkm_memx *memx, 113 u32 addr, u32 mask, u32 data, u32 nsec) 114 { 115 nvkm_debug(&memx->pmu->subdev, "R[%06x] & %08x == %08x, %d us\n", 116 addr, mask, data, nsec); 117 memx_cmd(memx, MEMX_WAIT, 4, (u32[]){ addr, mask, data, nsec }); 118 memx_out(memx); /* fuc can't handle multiple */ 119 } 120 121 void 122 nvkm_memx_nsec(struct nvkm_memx *memx, u32 nsec) 123 { 124 nvkm_debug(&memx->pmu->subdev, " DELAY = %d ns\n", nsec); 125 memx_cmd(memx, MEMX_DELAY, 1, (u32[]){ nsec }); 126 memx_out(memx); /* fuc can't handle multiple */ 127 } 128 129 void 130 nvkm_memx_wait_vblank(struct nvkm_memx *memx) 131 { 132 struct nvkm_subdev *subdev = &memx->pmu->subdev; 133 struct nvkm_device *device = subdev->device; 134 u32 heads, x, y, px = 0; 135 int i, head_sync; 136 137 if (device->chipset < 0xd0) { 138 heads = nvkm_rd32(device, 0x610050); 139 for (i = 0; i < 2; i++) { 140 /* Heuristic: sync to head with biggest resolution */ 141 if (heads & (2 << (i << 3))) { 142 x = nvkm_rd32(device, 0x610b40 + (0x540 * i)); 143 y = (x & 0xffff0000) >> 16; 144 x &= 0x0000ffff; 145 if ((x * y) > px) { 146 px = (x * y); 147 head_sync = i; 148 } 149 } 150 } 151 } 152 153 if (px == 0) { 154 nvkm_debug(subdev, "WAIT VBLANK !NO ACTIVE HEAD\n"); 155 return; 156 } 157 158 nvkm_debug(subdev, "WAIT VBLANK HEAD%d\n", head_sync); 159 memx_cmd(memx, MEMX_VBLANK, 1, (u32[]){ head_sync }); 160 memx_out(memx); /* fuc can't handle multiple */ 161 } 162 163 void 164 nvkm_memx_train(struct nvkm_memx *memx) 165 { 166 nvkm_debug(&memx->pmu->subdev, " MEM TRAIN\n"); 167 memx_cmd(memx, MEMX_TRAIN, 0, NULL); 168 } 169 170 int 171 nvkm_memx_train_result(struct nvkm_pmu *pmu, u32 *res, int rsize) 172 { 173 struct nvkm_device *device = pmu->subdev.device; 174 u32 reply[2], base, size, i; 175 int ret; 176 177 ret = nvkm_pmu_send(pmu, reply, PROC_MEMX, MEMX_MSG_INFO, 178 MEMX_INFO_TRAIN, 0); 179 if (ret) 180 return ret; 181 182 base = reply[0]; 183 size = reply[1] >> 2; 184 if (size > rsize) 185 return -ENOMEM; 186 187 /* read the packet */ 188 nvkm_wr32(device, 0x10a1c0, 0x02000000 | base); 189 190 for (i = 0; i < size; i++) 191 res[i] = nvkm_rd32(device, 0x10a1c4); 192 193 return 0; 194 } 195 196 void 197 nvkm_memx_block(struct nvkm_memx *memx) 198 { 199 nvkm_debug(&memx->pmu->subdev, " HOST BLOCKED\n"); 200 memx_cmd(memx, MEMX_ENTER, 0, NULL); 201 } 202 203 void 204 nvkm_memx_unblock(struct nvkm_memx *memx) 205 { 206 nvkm_debug(&memx->pmu->subdev, " HOST UNBLOCKED\n"); 207 memx_cmd(memx, MEMX_LEAVE, 0, NULL); 208 } 209 #endif 210