Home | History | Annotate | Line # | Download | only in pmu
      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