linux/drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c
<<
>>
Prefs
   1/*
   2 * Copyright 2010 Red Hat Inc.
   3 *
   4 * Permission is hereby granted, free of charge, to any person obtaining a
   5 * copy of this software and associated documentation files (the "Software"),
   6 * to deal in the Software without restriction, including without limitation
   7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   8 * and/or sell copies of the Software, and to permit persons to whom the
   9 * Software is furnished to do so, subject to the following conditions:
  10 *
  11 * The above copyright notice and this permission notice shall be included in
  12 * all copies or substantial portions of the Software.
  13 *
  14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
  18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  20 * OTHER DEALINGS IN THE SOFTWARE.
  21 *
  22 * Authors: Ben Skeggs
  23 */
  24
  25#include <core/device.h>
  26#include <core/gpuobj.h>
  27
  28#include <subdev/timer.h>
  29#include <subdev/fb.h>
  30#include <subdev/bar.h>
  31#include <subdev/vm.h>
  32
  33struct nv50_vmmgr_priv {
  34        struct nouveau_vmmgr base;
  35};
  36
  37static void
  38nv50_vm_map_pgt(struct nouveau_gpuobj *pgd, u32 pde,
  39                struct nouveau_gpuobj *pgt[2])
  40{
  41        u64 phys = 0xdeadcafe00000000ULL;
  42        u32 coverage = 0;
  43
  44        if (pgt[0]) {
  45                phys = 0x00000003 | pgt[0]->addr; /* present, 4KiB pages */
  46                coverage = (pgt[0]->size >> 3) << 12;
  47        } else
  48        if (pgt[1]) {
  49                phys = 0x00000001 | pgt[1]->addr; /* present */
  50                coverage = (pgt[1]->size >> 3) << 16;
  51        }
  52
  53        if (phys & 1) {
  54                if (coverage <= 32 * 1024 * 1024)
  55                        phys |= 0x60;
  56                else if (coverage <= 64 * 1024 * 1024)
  57                        phys |= 0x40;
  58                else if (coverage <= 128 * 1024 * 1024)
  59                        phys |= 0x20;
  60        }
  61
  62        nv_wo32(pgd, (pde * 8) + 0, lower_32_bits(phys));
  63        nv_wo32(pgd, (pde * 8) + 4, upper_32_bits(phys));
  64}
  65
  66static inline u64
  67vm_addr(struct nouveau_vma *vma, u64 phys, u32 memtype, u32 target)
  68{
  69        phys |= 1; /* present */
  70        phys |= (u64)memtype << 40;
  71        phys |= target << 4;
  72        if (vma->access & NV_MEM_ACCESS_SYS)
  73                phys |= (1 << 6);
  74        if (!(vma->access & NV_MEM_ACCESS_WO))
  75                phys |= (1 << 3);
  76        return phys;
  77}
  78
  79static void
  80nv50_vm_map(struct nouveau_vma *vma, struct nouveau_gpuobj *pgt,
  81            struct nouveau_mem *mem, u32 pte, u32 cnt, u64 phys, u64 delta)
  82{
  83        u32 comp = (mem->memtype & 0x180) >> 7;
  84        u32 block, target;
  85        int i;
  86
  87        /* IGPs don't have real VRAM, re-target to stolen system memory */
  88        target = 0;
  89        if (nouveau_fb(vma->vm->vmm)->ram->stolen) {
  90                phys += nouveau_fb(vma->vm->vmm)->ram->stolen;
  91                target = 3;
  92        }
  93
  94        phys  = vm_addr(vma, phys, mem->memtype, target);
  95        pte <<= 3;
  96        cnt <<= 3;
  97
  98        while (cnt) {
  99                u32 offset_h = upper_32_bits(phys);
 100                u32 offset_l = lower_32_bits(phys);
 101
 102                for (i = 7; i >= 0; i--) {
 103                        block = 1 << (i + 3);
 104                        if (cnt >= block && !(pte & (block - 1)))
 105                                break;
 106                }
 107                offset_l |= (i << 7);
 108
 109                phys += block << (vma->node->type - 3);
 110                cnt  -= block;
 111                if (comp) {
 112                        u32 tag = mem->tag->offset + ((delta >> 16) * comp);
 113                        offset_h |= (tag << 17);
 114                        delta    += block << (vma->node->type - 3);
 115                }
 116
 117                while (block) {
 118                        nv_wo32(pgt, pte + 0, offset_l);
 119                        nv_wo32(pgt, pte + 4, offset_h);
 120                        pte += 8;
 121                        block -= 8;
 122                }
 123        }
 124}
 125
 126static void
 127nv50_vm_map_sg(struct nouveau_vma *vma, struct nouveau_gpuobj *pgt,
 128               struct nouveau_mem *mem, u32 pte, u32 cnt, dma_addr_t *list)
 129{
 130        u32 target = (vma->access & NV_MEM_ACCESS_NOSNOOP) ? 3 : 2;
 131        pte <<= 3;
 132        while (cnt--) {
 133                u64 phys = vm_addr(vma, (u64)*list++, mem->memtype, target);
 134                nv_wo32(pgt, pte + 0, lower_32_bits(phys));
 135                nv_wo32(pgt, pte + 4, upper_32_bits(phys));
 136                pte += 8;
 137        }
 138}
 139
 140static void
 141nv50_vm_unmap(struct nouveau_gpuobj *pgt, u32 pte, u32 cnt)
 142{
 143        pte <<= 3;
 144        while (cnt--) {
 145                nv_wo32(pgt, pte + 0, 0x00000000);
 146                nv_wo32(pgt, pte + 4, 0x00000000);
 147                pte += 8;
 148        }
 149}
 150
 151static void
 152nv50_vm_flush(struct nouveau_vm *vm)
 153{
 154        struct nv50_vmmgr_priv *priv = (void *)vm->vmm;
 155        struct nouveau_bar *bar = nouveau_bar(priv);
 156        struct nouveau_engine *engine;
 157        int i, vme;
 158
 159        bar->flush(bar);
 160
 161        mutex_lock(&nv_subdev(priv)->mutex);
 162        for (i = 0; i < NVDEV_SUBDEV_NR; i++) {
 163                if (!atomic_read(&vm->engref[i]))
 164                        continue;
 165
 166                /* unfortunate hw bug workaround... */
 167                engine = nouveau_engine(priv, i);
 168                if (engine && engine->tlb_flush) {
 169                        engine->tlb_flush(engine);
 170                        continue;
 171                }
 172
 173                switch (i) {
 174                case NVDEV_ENGINE_GR   : vme = 0x00; break;
 175                case NVDEV_ENGINE_VP   : vme = 0x01; break;
 176                case NVDEV_SUBDEV_BAR  : vme = 0x06; break;
 177                case NVDEV_ENGINE_PPP  :
 178                case NVDEV_ENGINE_MPEG : vme = 0x08; break;
 179                case NVDEV_ENGINE_BSP  : vme = 0x09; break;
 180                case NVDEV_ENGINE_CRYPT: vme = 0x0a; break;
 181                case NVDEV_ENGINE_COPY0: vme = 0x0d; break;
 182                default:
 183                        continue;
 184                }
 185
 186                nv_wr32(priv, 0x100c80, (vme << 16) | 1);
 187                if (!nv_wait(priv, 0x100c80, 0x00000001, 0x00000000))
 188                        nv_error(priv, "vm flush timeout: engine %d\n", vme);
 189        }
 190        mutex_unlock(&nv_subdev(priv)->mutex);
 191}
 192
 193static int
 194nv50_vm_create(struct nouveau_vmmgr *vmm, u64 offset, u64 length,
 195               u64 mm_offset, struct nouveau_vm **pvm)
 196{
 197        u32 block = (1 << (vmm->pgt_bits + 12));
 198        if (block > length)
 199                block = length;
 200
 201        return nouveau_vm_create(vmm, offset, length, mm_offset, block, pvm);
 202}
 203
 204static int
 205nv50_vmmgr_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
 206                struct nouveau_oclass *oclass, void *data, u32 size,
 207                struct nouveau_object **pobject)
 208{
 209        struct nv50_vmmgr_priv *priv;
 210        int ret;
 211
 212        ret = nouveau_vmmgr_create(parent, engine, oclass, "VM", "vm", &priv);
 213        *pobject = nv_object(priv);
 214        if (ret)
 215                return ret;
 216
 217        priv->base.limit = 1ULL << 40;
 218        priv->base.dma_bits = 40;
 219        priv->base.pgt_bits  = 29 - 12;
 220        priv->base.spg_shift = 12;
 221        priv->base.lpg_shift = 16;
 222        priv->base.create = nv50_vm_create;
 223        priv->base.map_pgt = nv50_vm_map_pgt;
 224        priv->base.map = nv50_vm_map;
 225        priv->base.map_sg = nv50_vm_map_sg;
 226        priv->base.unmap = nv50_vm_unmap;
 227        priv->base.flush = nv50_vm_flush;
 228        return 0;
 229}
 230
 231struct nouveau_oclass
 232nv50_vmmgr_oclass = {
 233        .handle = NV_SUBDEV(VM, 0x50),
 234        .ofuncs = &(struct nouveau_ofuncs) {
 235                .ctor = nv50_vmmgr_ctor,
 236                .dtor = _nouveau_vmmgr_dtor,
 237                .init = _nouveau_vmmgr_init,
 238                .fini = _nouveau_vmmgr_fini,
 239        },
 240};
 241