Path: blob/master/drivers/gpu/drm/nouveau/nouveau_sgdma.c
15112 views
#include "drmP.h"1#include "nouveau_drv.h"2#include <linux/pagemap.h>3#include <linux/slab.h>45#define NV_CTXDMA_PAGE_SHIFT 126#define NV_CTXDMA_PAGE_SIZE (1 << NV_CTXDMA_PAGE_SHIFT)7#define NV_CTXDMA_PAGE_MASK (NV_CTXDMA_PAGE_SIZE - 1)89struct nouveau_sgdma_be {10struct ttm_backend backend;11struct drm_device *dev;1213dma_addr_t *pages;14bool *ttm_alloced;15unsigned nr_pages;1617u64 offset;18bool bound;19};2021static int22nouveau_sgdma_populate(struct ttm_backend *be, unsigned long num_pages,23struct page **pages, struct page *dummy_read_page,24dma_addr_t *dma_addrs)25{26struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be;27struct drm_device *dev = nvbe->dev;2829NV_DEBUG(nvbe->dev, "num_pages = %ld\n", num_pages);3031if (nvbe->pages)32return -EINVAL;3334nvbe->pages = kmalloc(sizeof(dma_addr_t) * num_pages, GFP_KERNEL);35if (!nvbe->pages)36return -ENOMEM;3738nvbe->ttm_alloced = kmalloc(sizeof(bool) * num_pages, GFP_KERNEL);39if (!nvbe->ttm_alloced)40return -ENOMEM;4142nvbe->nr_pages = 0;43while (num_pages--) {44/* this code path isn't called and is incorrect anyways */45if (0) { /*dma_addrs[nvbe->nr_pages] != DMA_ERROR_CODE)*/46nvbe->pages[nvbe->nr_pages] =47dma_addrs[nvbe->nr_pages];48nvbe->ttm_alloced[nvbe->nr_pages] = true;49} else {50nvbe->pages[nvbe->nr_pages] =51pci_map_page(dev->pdev, pages[nvbe->nr_pages], 0,52PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);53if (pci_dma_mapping_error(dev->pdev,54nvbe->pages[nvbe->nr_pages])) {55be->func->clear(be);56return -EFAULT;57}58nvbe->ttm_alloced[nvbe->nr_pages] = false;59}6061nvbe->nr_pages++;62}6364return 0;65}6667static void68nouveau_sgdma_clear(struct ttm_backend *be)69{70struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be;71struct drm_device *dev;7273if (nvbe && nvbe->pages) {74dev = nvbe->dev;75NV_DEBUG(dev, "\n");7677if (nvbe->bound)78be->func->unbind(be);7980while (nvbe->nr_pages--) {81if (!nvbe->ttm_alloced[nvbe->nr_pages])82pci_unmap_page(dev->pdev, nvbe->pages[nvbe->nr_pages],83PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);84}85kfree(nvbe->pages);86kfree(nvbe->ttm_alloced);87nvbe->pages = NULL;88nvbe->ttm_alloced = NULL;89nvbe->nr_pages = 0;90}91}9293static void94nouveau_sgdma_destroy(struct ttm_backend *be)95{96struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be;9798if (be) {99NV_DEBUG(nvbe->dev, "\n");100101if (nvbe) {102if (nvbe->pages)103be->func->clear(be);104kfree(nvbe);105}106}107}108109static int110nv04_sgdma_bind(struct ttm_backend *be, struct ttm_mem_reg *mem)111{112struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be;113struct drm_device *dev = nvbe->dev;114struct drm_nouveau_private *dev_priv = dev->dev_private;115struct nouveau_gpuobj *gpuobj = dev_priv->gart_info.sg_ctxdma;116unsigned i, j, pte;117118NV_DEBUG(dev, "pg=0x%lx\n", mem->start);119120nvbe->offset = mem->start << PAGE_SHIFT;121pte = (nvbe->offset >> NV_CTXDMA_PAGE_SHIFT) + 2;122for (i = 0; i < nvbe->nr_pages; i++) {123dma_addr_t dma_offset = nvbe->pages[i];124uint32_t offset_l = lower_32_bits(dma_offset);125126for (j = 0; j < PAGE_SIZE / NV_CTXDMA_PAGE_SIZE; j++, pte++) {127nv_wo32(gpuobj, (pte * 4) + 0, offset_l | 3);128dma_offset += NV_CTXDMA_PAGE_SIZE;129}130}131132nvbe->bound = true;133return 0;134}135136static int137nv04_sgdma_unbind(struct ttm_backend *be)138{139struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be;140struct drm_device *dev = nvbe->dev;141struct drm_nouveau_private *dev_priv = dev->dev_private;142struct nouveau_gpuobj *gpuobj = dev_priv->gart_info.sg_ctxdma;143unsigned i, j, pte;144145NV_DEBUG(dev, "\n");146147if (!nvbe->bound)148return 0;149150pte = (nvbe->offset >> NV_CTXDMA_PAGE_SHIFT) + 2;151for (i = 0; i < nvbe->nr_pages; i++) {152for (j = 0; j < PAGE_SIZE / NV_CTXDMA_PAGE_SIZE; j++, pte++)153nv_wo32(gpuobj, (pte * 4) + 0, 0x00000000);154}155156nvbe->bound = false;157return 0;158}159160static struct ttm_backend_func nv04_sgdma_backend = {161.populate = nouveau_sgdma_populate,162.clear = nouveau_sgdma_clear,163.bind = nv04_sgdma_bind,164.unbind = nv04_sgdma_unbind,165.destroy = nouveau_sgdma_destroy166};167168static void169nv41_sgdma_flush(struct nouveau_sgdma_be *nvbe)170{171struct drm_device *dev = nvbe->dev;172173nv_wr32(dev, 0x100810, 0x00000022);174if (!nv_wait(dev, 0x100810, 0x00000100, 0x00000100))175NV_ERROR(dev, "vm flush timeout: 0x%08x\n",176nv_rd32(dev, 0x100810));177nv_wr32(dev, 0x100810, 0x00000000);178}179180static int181nv41_sgdma_bind(struct ttm_backend *be, struct ttm_mem_reg *mem)182{183struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be;184struct drm_nouveau_private *dev_priv = nvbe->dev->dev_private;185struct nouveau_gpuobj *pgt = dev_priv->gart_info.sg_ctxdma;186dma_addr_t *list = nvbe->pages;187u32 pte = mem->start << 2;188u32 cnt = nvbe->nr_pages;189190nvbe->offset = mem->start << PAGE_SHIFT;191192while (cnt--) {193nv_wo32(pgt, pte, (*list++ >> 7) | 1);194pte += 4;195}196197nv41_sgdma_flush(nvbe);198nvbe->bound = true;199return 0;200}201202static int203nv41_sgdma_unbind(struct ttm_backend *be)204{205struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be;206struct drm_nouveau_private *dev_priv = nvbe->dev->dev_private;207struct nouveau_gpuobj *pgt = dev_priv->gart_info.sg_ctxdma;208u32 pte = (nvbe->offset >> 12) << 2;209u32 cnt = nvbe->nr_pages;210211while (cnt--) {212nv_wo32(pgt, pte, 0x00000000);213pte += 4;214}215216nv41_sgdma_flush(nvbe);217nvbe->bound = false;218return 0;219}220221static struct ttm_backend_func nv41_sgdma_backend = {222.populate = nouveau_sgdma_populate,223.clear = nouveau_sgdma_clear,224.bind = nv41_sgdma_bind,225.unbind = nv41_sgdma_unbind,226.destroy = nouveau_sgdma_destroy227};228229static void230nv44_sgdma_flush(struct nouveau_sgdma_be *nvbe)231{232struct drm_device *dev = nvbe->dev;233234nv_wr32(dev, 0x100814, (nvbe->nr_pages - 1) << 12);235nv_wr32(dev, 0x100808, nvbe->offset | 0x20);236if (!nv_wait(dev, 0x100808, 0x00000001, 0x00000001))237NV_ERROR(dev, "gart flush timeout: 0x%08x\n",238nv_rd32(dev, 0x100808));239nv_wr32(dev, 0x100808, 0x00000000);240}241242static void243nv44_sgdma_fill(struct nouveau_gpuobj *pgt, dma_addr_t *list, u32 base, u32 cnt)244{245struct drm_nouveau_private *dev_priv = pgt->dev->dev_private;246dma_addr_t dummy = dev_priv->gart_info.dummy.addr;247u32 pte, tmp[4];248249pte = base >> 2;250base &= ~0x0000000f;251252tmp[0] = nv_ro32(pgt, base + 0x0);253tmp[1] = nv_ro32(pgt, base + 0x4);254tmp[2] = nv_ro32(pgt, base + 0x8);255tmp[3] = nv_ro32(pgt, base + 0xc);256while (cnt--) {257u32 addr = list ? (*list++ >> 12) : (dummy >> 12);258switch (pte++ & 0x3) {259case 0:260tmp[0] &= ~0x07ffffff;261tmp[0] |= addr;262break;263case 1:264tmp[0] &= ~0xf8000000;265tmp[0] |= addr << 27;266tmp[1] &= ~0x003fffff;267tmp[1] |= addr >> 5;268break;269case 2:270tmp[1] &= ~0xffc00000;271tmp[1] |= addr << 22;272tmp[2] &= ~0x0001ffff;273tmp[2] |= addr >> 10;274break;275case 3:276tmp[2] &= ~0xfffe0000;277tmp[2] |= addr << 17;278tmp[3] &= ~0x00000fff;279tmp[3] |= addr >> 15;280break;281}282}283284tmp[3] |= 0x40000000;285286nv_wo32(pgt, base + 0x0, tmp[0]);287nv_wo32(pgt, base + 0x4, tmp[1]);288nv_wo32(pgt, base + 0x8, tmp[2]);289nv_wo32(pgt, base + 0xc, tmp[3]);290}291292static int293nv44_sgdma_bind(struct ttm_backend *be, struct ttm_mem_reg *mem)294{295struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be;296struct drm_nouveau_private *dev_priv = nvbe->dev->dev_private;297struct nouveau_gpuobj *pgt = dev_priv->gart_info.sg_ctxdma;298dma_addr_t *list = nvbe->pages;299u32 pte = mem->start << 2, tmp[4];300u32 cnt = nvbe->nr_pages;301int i;302303nvbe->offset = mem->start << PAGE_SHIFT;304305if (pte & 0x0000000c) {306u32 max = 4 - ((pte >> 2) & 0x3);307u32 part = (cnt > max) ? max : cnt;308nv44_sgdma_fill(pgt, list, pte, part);309pte += (part << 2);310list += part;311cnt -= part;312}313314while (cnt >= 4) {315for (i = 0; i < 4; i++)316tmp[i] = *list++ >> 12;317nv_wo32(pgt, pte + 0x0, tmp[0] >> 0 | tmp[1] << 27);318nv_wo32(pgt, pte + 0x4, tmp[1] >> 5 | tmp[2] << 22);319nv_wo32(pgt, pte + 0x8, tmp[2] >> 10 | tmp[3] << 17);320nv_wo32(pgt, pte + 0xc, tmp[3] >> 15 | 0x40000000);321pte += 0x10;322cnt -= 4;323}324325if (cnt)326nv44_sgdma_fill(pgt, list, pte, cnt);327328nv44_sgdma_flush(nvbe);329nvbe->bound = true;330return 0;331}332333static int334nv44_sgdma_unbind(struct ttm_backend *be)335{336struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be;337struct drm_nouveau_private *dev_priv = nvbe->dev->dev_private;338struct nouveau_gpuobj *pgt = dev_priv->gart_info.sg_ctxdma;339u32 pte = (nvbe->offset >> 12) << 2;340u32 cnt = nvbe->nr_pages;341342if (pte & 0x0000000c) {343u32 max = 4 - ((pte >> 2) & 0x3);344u32 part = (cnt > max) ? max : cnt;345nv44_sgdma_fill(pgt, NULL, pte, part);346pte += (part << 2);347cnt -= part;348}349350while (cnt >= 4) {351nv_wo32(pgt, pte + 0x0, 0x00000000);352nv_wo32(pgt, pte + 0x4, 0x00000000);353nv_wo32(pgt, pte + 0x8, 0x00000000);354nv_wo32(pgt, pte + 0xc, 0x00000000);355pte += 0x10;356cnt -= 4;357}358359if (cnt)360nv44_sgdma_fill(pgt, NULL, pte, cnt);361362nv44_sgdma_flush(nvbe);363nvbe->bound = false;364return 0;365}366367static struct ttm_backend_func nv44_sgdma_backend = {368.populate = nouveau_sgdma_populate,369.clear = nouveau_sgdma_clear,370.bind = nv44_sgdma_bind,371.unbind = nv44_sgdma_unbind,372.destroy = nouveau_sgdma_destroy373};374375static int376nv50_sgdma_bind(struct ttm_backend *be, struct ttm_mem_reg *mem)377{378struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be;379struct nouveau_mem *node = mem->mm_node;380/* noop: bound in move_notify() */381node->pages = nvbe->pages;382nvbe->pages = (dma_addr_t *)node;383nvbe->bound = true;384return 0;385}386387static int388nv50_sgdma_unbind(struct ttm_backend *be)389{390struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be;391struct nouveau_mem *node = (struct nouveau_mem *)nvbe->pages;392/* noop: unbound in move_notify() */393nvbe->pages = node->pages;394node->pages = NULL;395nvbe->bound = false;396return 0;397}398399static struct ttm_backend_func nv50_sgdma_backend = {400.populate = nouveau_sgdma_populate,401.clear = nouveau_sgdma_clear,402.bind = nv50_sgdma_bind,403.unbind = nv50_sgdma_unbind,404.destroy = nouveau_sgdma_destroy405};406407struct ttm_backend *408nouveau_sgdma_init_ttm(struct drm_device *dev)409{410struct drm_nouveau_private *dev_priv = dev->dev_private;411struct nouveau_sgdma_be *nvbe;412413nvbe = kzalloc(sizeof(*nvbe), GFP_KERNEL);414if (!nvbe)415return NULL;416417nvbe->dev = dev;418419nvbe->backend.func = dev_priv->gart_info.func;420return &nvbe->backend;421}422423int424nouveau_sgdma_init(struct drm_device *dev)425{426struct drm_nouveau_private *dev_priv = dev->dev_private;427struct nouveau_gpuobj *gpuobj = NULL;428u32 aper_size, align;429int ret;430431if (dev_priv->card_type >= NV_40 && drm_pci_device_is_pcie(dev))432aper_size = 512 * 1024 * 1024;433else434aper_size = 64 * 1024 * 1024;435436/* Dear NVIDIA, NV44+ would like proper present bits in PTEs for437* christmas. The cards before it have them, the cards after438* it have them, why is NV44 so unloved?439*/440dev_priv->gart_info.dummy.page = alloc_page(GFP_DMA32 | GFP_KERNEL);441if (!dev_priv->gart_info.dummy.page)442return -ENOMEM;443444dev_priv->gart_info.dummy.addr =445pci_map_page(dev->pdev, dev_priv->gart_info.dummy.page,4460, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);447if (pci_dma_mapping_error(dev->pdev, dev_priv->gart_info.dummy.addr)) {448NV_ERROR(dev, "error mapping dummy page\n");449__free_page(dev_priv->gart_info.dummy.page);450dev_priv->gart_info.dummy.page = NULL;451return -ENOMEM;452}453454if (dev_priv->card_type >= NV_50) {455dev_priv->gart_info.aper_base = 0;456dev_priv->gart_info.aper_size = aper_size;457dev_priv->gart_info.type = NOUVEAU_GART_HW;458dev_priv->gart_info.func = &nv50_sgdma_backend;459} else460if (0 && drm_pci_device_is_pcie(dev) &&461dev_priv->chipset > 0x40 && dev_priv->chipset != 0x45) {462if (nv44_graph_class(dev)) {463dev_priv->gart_info.func = &nv44_sgdma_backend;464align = 512 * 1024;465} else {466dev_priv->gart_info.func = &nv41_sgdma_backend;467align = 16;468}469470ret = nouveau_gpuobj_new(dev, NULL, aper_size / 1024, align,471NVOBJ_FLAG_ZERO_ALLOC |472NVOBJ_FLAG_ZERO_FREE, &gpuobj);473if (ret) {474NV_ERROR(dev, "Error creating sgdma object: %d\n", ret);475return ret;476}477478dev_priv->gart_info.sg_ctxdma = gpuobj;479dev_priv->gart_info.aper_base = 0;480dev_priv->gart_info.aper_size = aper_size;481dev_priv->gart_info.type = NOUVEAU_GART_HW;482} else {483ret = nouveau_gpuobj_new(dev, NULL, (aper_size / 1024) + 8, 16,484NVOBJ_FLAG_ZERO_ALLOC |485NVOBJ_FLAG_ZERO_FREE, &gpuobj);486if (ret) {487NV_ERROR(dev, "Error creating sgdma object: %d\n", ret);488return ret;489}490491nv_wo32(gpuobj, 0, NV_CLASS_DMA_IN_MEMORY |492(1 << 12) /* PT present */ |493(0 << 13) /* PT *not* linear */ |494(0 << 14) /* RW */ |495(2 << 16) /* PCI */);496nv_wo32(gpuobj, 4, aper_size - 1);497498dev_priv->gart_info.sg_ctxdma = gpuobj;499dev_priv->gart_info.aper_base = 0;500dev_priv->gart_info.aper_size = aper_size;501dev_priv->gart_info.type = NOUVEAU_GART_PDMA;502dev_priv->gart_info.func = &nv04_sgdma_backend;503}504505return 0;506}507508void509nouveau_sgdma_takedown(struct drm_device *dev)510{511struct drm_nouveau_private *dev_priv = dev->dev_private;512513nouveau_gpuobj_ref(NULL, &dev_priv->gart_info.sg_ctxdma);514515if (dev_priv->gart_info.dummy.page) {516pci_unmap_page(dev->pdev, dev_priv->gart_info.dummy.addr,517PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);518__free_page(dev_priv->gart_info.dummy.page);519dev_priv->gart_info.dummy.page = NULL;520}521}522523uint32_t524nouveau_sgdma_get_physical(struct drm_device *dev, uint32_t offset)525{526struct drm_nouveau_private *dev_priv = dev->dev_private;527struct nouveau_gpuobj *gpuobj = dev_priv->gart_info.sg_ctxdma;528int pte = (offset >> NV_CTXDMA_PAGE_SHIFT) + 2;529530BUG_ON(dev_priv->card_type >= NV_50);531532return (nv_ro32(gpuobj, 4 * pte) & ~NV_CTXDMA_PAGE_MASK) |533(offset & NV_CTXDMA_PAGE_MASK);534}535536537