On Tue, 2023-09-19 at 06:21 +1000, Ben Skeggs wrote: > From: Ben Skeggs <bskeggs@xxxxxxxxxx> > > - preparation for GSP-RM, which has massive FW images > - based on a patch by Dave Airlie Probably more canonical to use one of the standard phrases such as Suggested-by > > Signed-off-by: Ben Skeggs <bskeggs@xxxxxxxxxx> > --- > .../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 d4e507e252b1..20839be72644 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 91fb494d4009..4641e7eebe56 100644 > --- a/drivers/gpu/drm/nouveau/nvkm/core/firmware.c > +++ b/drivers/gpu/drm/nouveau/nvkm/core/firmware.c > @@ -113,6 +113,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) > @@ -121,10 +137,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); > @@ -133,12 +149,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; > } > > @@ -189,6 +208,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; > @@ -226,6 +251,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;