From 17a74021a339a4d4bd27be1dd95b99442455a4ad Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 19 Sep 2023 06:21:36 +1000 Subject: [PATCH] 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: Ben Skeggs Signed-off-by: Dave Airlie Link: https://patchwork.freedesktop.org/patch/msgid/20230918202149.4343-32-skeggsb@gmail.com --- .../drm/nouveau/include/nvkm/core/firmware.h | 6 +- drivers/gpu/drm/nouveau/nvkm/core/firmware.c | 74 ++++++++++++++++++- 2 files changed, 76 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/nouveau/include/nvkm/core/firmware.h b/drivers/gpu/drm/nouveau/include/nvkm/core/firmware.h index d4e507e252b13..20839be72644b 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/core/firmware.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/core/firmware.h @@ -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; }; diff --git a/drivers/gpu/drm/nouveau/nvkm/core/firmware.c b/drivers/gpu/drm/nouveau/nvkm/core/firmware.c index 374212da9e959..adc60b25f8e6c 100644 --- a/drivers/gpu/drm/nouveau/nvkm/core/firmware.c +++ b/drivers/gpu/drm/nouveau/nvkm/core/firmware.c @@ -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;