Commit 17a74021 authored by Ben Skeggs's avatar Ben Skeggs Committed by Dave Airlie
Browse files

drm/nouveau/nvkm: support loading fws into sg_table



- preparation for GSP-RM, which has massive FW images
- based on a patch by Dave Airlie

Signed-off-by: default avatarBen Skeggs <bskeggs@redhat.com>
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20230918202149.4343-32-skeggsb@gmail.com
parent e672f5f3
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@ struct nvkm_firmware {
		enum nvkm_firmware_type {
			NVKM_FIRMWARE_IMG_RAM,
			NVKM_FIRMWARE_IMG_DMA,
			NVKM_FIRMWARE_IMG_SGT,
		} type;
	} *func;
	const char *name;
@@ -21,7 +22,10 @@ struct nvkm_firmware {

	struct nvkm_firmware_mem {
		struct nvkm_memory memory;
		struct scatterlist sgl;
		union {
			struct scatterlist sgl; /* DMA */
			struct sg_table sgt;	/* SGT */
		};
	} mem;
};

+71 −3
Original line number Diff line number Diff line
@@ -112,6 +112,22 @@ nvkm_firmware_put(const struct firmware *fw)

#define nvkm_firmware_mem(p) container_of((p), struct nvkm_firmware, mem.memory)

static struct scatterlist *
nvkm_firmware_mem_sgl(struct nvkm_memory *memory)
{
	struct nvkm_firmware *fw = nvkm_firmware_mem(memory);

	switch (fw->func->type) {
	case NVKM_FIRMWARE_IMG_DMA: return &fw->mem.sgl;
	case NVKM_FIRMWARE_IMG_SGT: return  fw->mem.sgt.sgl;
	default:
		WARN_ON(1);
		break;
	}

	return NULL;
}

static int
nvkm_firmware_mem_map(struct nvkm_memory *memory, u64 offset, struct nvkm_vmm *vmm,
		      struct nvkm_vma *vma, void *argv, u32 argc)
@@ -120,10 +136,10 @@ nvkm_firmware_mem_map(struct nvkm_memory *memory, u64 offset, struct nvkm_vmm *v
	struct nvkm_vmm_map map = {
		.memory = &fw->mem.memory,
		.offset = offset,
		.sgl = &fw->mem.sgl,
		.sgl = nvkm_firmware_mem_sgl(memory),
	};

	if (WARN_ON(fw->func->type != NVKM_FIRMWARE_IMG_DMA))
	if (!map.sgl)
		return -ENOSYS;

	return nvkm_vmm_map(vmm, vma, argv, argc, &map);
@@ -132,12 +148,15 @@ nvkm_firmware_mem_map(struct nvkm_memory *memory, u64 offset, struct nvkm_vmm *v
static u64
nvkm_firmware_mem_size(struct nvkm_memory *memory)
{
	return sg_dma_len(&nvkm_firmware_mem(memory)->mem.sgl);
	struct scatterlist *sgl = nvkm_firmware_mem_sgl(memory);

	return sgl ? sg_dma_len(sgl) : 0;
}

static u64
nvkm_firmware_mem_addr(struct nvkm_memory *memory)
{
	BUG_ON(nvkm_firmware_mem(memory)->func->type != NVKM_FIRMWARE_IMG_DMA);
	return nvkm_firmware_mem(memory)->phys;
}

@@ -188,6 +207,12 @@ nvkm_firmware_dtor(struct nvkm_firmware *fw)
		nvkm_memory_unref(&memory);
		dma_free_coherent(fw->device->dev, sg_dma_len(&fw->mem.sgl), fw->img, fw->phys);
		break;
	case NVKM_FIRMWARE_IMG_SGT:
		nvkm_memory_unref(&memory);
		dma_unmap_sgtable(fw->device->dev, &fw->mem.sgt, DMA_TO_DEVICE, 0);
		sg_free_table(&fw->mem.sgt);
		vfree(fw->img);
		break;
	default:
		WARN_ON(1);
		break;
@@ -225,6 +250,49 @@ nvkm_firmware_ctor(const struct nvkm_firmware_func *func, const char *name,
		sg_dma_len(&fw->mem.sgl) = len;
	}
		break;
	case NVKM_FIRMWARE_IMG_SGT:
		len = ALIGN(fw->len, PAGE_SIZE);

		fw->img = vmalloc(len);
		if (fw->img) {
			int pages = len >> PAGE_SHIFT;
			int ret = 0;

			memcpy(fw->img, src, fw->len);

			ret = sg_alloc_table(&fw->mem.sgt, pages, GFP_KERNEL);
			if (ret == 0) {
				struct scatterlist *sgl;
				u8 *data = fw->img;
				int i;

				for_each_sgtable_sg(&fw->mem.sgt, sgl, i) {
					struct page *page = vmalloc_to_page(data);

					if (!page) {
						ret = -EFAULT;
						break;
					}

					sg_set_page(sgl, page, PAGE_SIZE, 0);
					data += PAGE_SIZE;
				}

				if (ret == 0) {
					ret = dma_map_sgtable(fw->device->dev, &fw->mem.sgt,
							      DMA_TO_DEVICE, 0);
				}

				if (ret)
					sg_free_table(&fw->mem.sgt);
			}

			if (ret) {
				vfree(fw->img);
				fw->img = NULL;
			}
		}
		break;
	default:
		WARN_ON(1);
		return -EINVAL;