From: Ben Skeggs <bske...@redhat.com>

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

Signed-off-by: Ben Skeggs <bske...@redhat.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 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;
-- 
2.41.0

Reply via email to