From 9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00 Mon Sep 17 00:00:00 2001 From: Yunhong Jiang Date: Tue, 4 Aug 2015 12:17:53 -0700 Subject: Add the rt linux 4.1.3-rt3 as base Import the rt linux 4.1.3-rt3 as OPNFV kvm base. It's from git://git.kernel.org/pub/scm/linux/kernel/git/rt/linux-rt-devel.git linux-4.1.y-rt and the base is: commit 0917f823c59692d751951bf5ea699a2d1e2f26a2 Author: Sebastian Andrzej Siewior Date: Sat Jul 25 12:13:34 2015 +0200 Prepare v4.1.3-rt3 Signed-off-by: Sebastian Andrzej Siewior We lose all the git history this way and it's not good. We should apply another opnfv project repo in future. Change-Id: I87543d81c9df70d99c5001fbdf646b202c19f423 Signed-off-by: Yunhong Jiang --- .../gpu/drm/nouveau/nvkm/subdev/instmem/Kbuild | 5 + .../gpu/drm/nouveau/nvkm/subdev/instmem/base.c | 146 +++++++ .../gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c | 440 +++++++++++++++++++++ .../gpu/drm/nouveau/nvkm/subdev/instmem/nv04.c | 185 +++++++++ .../gpu/drm/nouveau/nvkm/subdev/instmem/nv04.h | 36 ++ .../gpu/drm/nouveau/nvkm/subdev/instmem/nv40.c | 136 +++++++ .../gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c | 169 ++++++++ .../gpu/drm/nouveau/nvkm/subdev/instmem/priv.h | 54 +++ 8 files changed, 1171 insertions(+) create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/base.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv04.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv04.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv40.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/priv.h (limited to 'kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem') diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/Kbuild new file mode 100644 index 000000000..13bb7fc0a --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/Kbuild @@ -0,0 +1,5 @@ +nvkm-y += nvkm/subdev/instmem/base.o +nvkm-y += nvkm/subdev/instmem/nv04.o +nvkm-y += nvkm/subdev/instmem/nv40.o +nvkm-y += nvkm/subdev/instmem/nv50.o +nvkm-y += nvkm/subdev/instmem/gk20a.o diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/base.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/base.c new file mode 100644 index 000000000..d16358cc6 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/base.c @@ -0,0 +1,146 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +#include + +/****************************************************************************** + * instmem object base implementation + *****************************************************************************/ + +void +_nvkm_instobj_dtor(struct nvkm_object *object) +{ + struct nvkm_instmem *imem = nvkm_instmem(object); + struct nvkm_instobj *iobj = (void *)object; + + mutex_lock(&nv_subdev(imem)->mutex); + list_del(&iobj->head); + mutex_unlock(&nv_subdev(imem)->mutex); + + return nvkm_object_destroy(&iobj->base); +} + +int +nvkm_instobj_create_(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, int length, void **pobject) +{ + struct nvkm_instmem *imem = nvkm_instmem(parent); + struct nvkm_instobj *iobj; + int ret; + + ret = nvkm_object_create_(parent, engine, oclass, NV_MEMOBJ_CLASS, + length, pobject); + iobj = *pobject; + if (ret) + return ret; + + mutex_lock(&imem->base.mutex); + list_add(&iobj->head, &imem->list); + mutex_unlock(&imem->base.mutex); + return 0; +} + +/****************************************************************************** + * instmem subdev base implementation + *****************************************************************************/ + +static int +nvkm_instmem_alloc(struct nvkm_instmem *imem, struct nvkm_object *parent, + u32 size, u32 align, struct nvkm_object **pobject) +{ + struct nvkm_instmem_impl *impl = (void *)imem->base.object.oclass; + struct nvkm_instobj_args args = { .size = size, .align = align }; + return nvkm_object_ctor(parent, &parent->engine->subdev.object, + impl->instobj, &args, sizeof(args), pobject); +} + +int +_nvkm_instmem_fini(struct nvkm_object *object, bool suspend) +{ + struct nvkm_instmem *imem = (void *)object; + struct nvkm_instobj *iobj; + int i, ret = 0; + + if (suspend) { + mutex_lock(&imem->base.mutex); + list_for_each_entry(iobj, &imem->list, head) { + iobj->suspend = vmalloc(iobj->size); + if (!iobj->suspend) { + ret = -ENOMEM; + break; + } + + for (i = 0; i < iobj->size; i += 4) + iobj->suspend[i / 4] = nv_ro32(iobj, i); + } + mutex_unlock(&imem->base.mutex); + if (ret) + return ret; + } + + return nvkm_subdev_fini(&imem->base, suspend); +} + +int +_nvkm_instmem_init(struct nvkm_object *object) +{ + struct nvkm_instmem *imem = (void *)object; + struct nvkm_instobj *iobj; + int ret, i; + + ret = nvkm_subdev_init(&imem->base); + if (ret) + return ret; + + mutex_lock(&imem->base.mutex); + list_for_each_entry(iobj, &imem->list, head) { + if (iobj->suspend) { + for (i = 0; i < iobj->size; i += 4) + nv_wo32(iobj, i, iobj->suspend[i / 4]); + vfree(iobj->suspend); + iobj->suspend = NULL; + } + } + mutex_unlock(&imem->base.mutex); + return 0; +} + +int +nvkm_instmem_create_(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, int length, void **pobject) +{ + struct nvkm_instmem *imem; + int ret; + + ret = nvkm_subdev_create_(parent, engine, oclass, 0, "INSTMEM", + "instmem", length, pobject); + imem = *pobject; + if (ret) + return ret; + + INIT_LIST_HEAD(&imem->list); + imem->alloc = nvkm_instmem_alloc; + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c new file mode 100644 index 000000000..dd0994d9e --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c @@ -0,0 +1,440 @@ +/* + * Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/* + * GK20A does not have dedicated video memory, and to accurately represent this + * fact Nouveau will not create a RAM device for it. Therefore its instmem + * implementation must be done directly on top of system memory, while providing + * coherent read and write operations. + * + * Instmem can be allocated through two means: + * 1) If an IOMMU mapping has been probed, the IOMMU API is used to make memory + * pages contiguous to the GPU. This is the preferred way. + * 2) If no IOMMU mapping is probed, the DMA API is used to allocate physically + * contiguous memory. + * + * In both cases CPU read and writes are performed using PRAMIN (i.e. using the + * GPU path) to ensure these operations are coherent for the GPU. This allows us + * to use more "relaxed" allocation parameters when using the DMA API, since we + * never need a kernel mapping. + */ + +#include +#include +#include + +#ifdef __KERNEL__ +#include +#include +#include +#endif + +#include "priv.h" + +struct gk20a_instobj_priv { + struct nvkm_instobj base; + /* Must be second member here - see nouveau_gpuobj_map_vm() */ + struct nvkm_mem *mem; + /* Pointed by mem */ + struct nvkm_mem _mem; +}; + +/* + * Used for objects allocated using the DMA API + */ +struct gk20a_instobj_dma { + struct gk20a_instobj_priv base; + + void *cpuaddr; + dma_addr_t handle; + struct nvkm_mm_node r; +}; + +/* + * Used for objects flattened using the IOMMU API + */ +struct gk20a_instobj_iommu { + struct gk20a_instobj_priv base; + + /* array of base.mem->size pages */ + struct page *pages[]; +}; + +struct gk20a_instmem_priv { + struct nvkm_instmem base; + spinlock_t lock; + u64 addr; + + /* Only used if IOMMU if present */ + struct mutex *mm_mutex; + struct nvkm_mm *mm; + struct iommu_domain *domain; + unsigned long iommu_pgshift; + + /* Only used by DMA API */ + struct dma_attrs attrs; +}; + +/* + * Use PRAMIN to read/write data and avoid coherency issues. + * PRAMIN uses the GPU path and ensures data will always be coherent. + * + * A dynamic mapping based solution would be desirable in the future, but + * the issue remains of how to maintain coherency efficiently. On ARM it is + * not easy (if possible at all?) to create uncached temporary mappings. + */ + +static u32 +gk20a_instobj_rd32(struct nvkm_object *object, u64 offset) +{ + struct gk20a_instmem_priv *priv = (void *)nvkm_instmem(object); + struct gk20a_instobj_priv *node = (void *)object; + unsigned long flags; + u64 base = (node->mem->offset + offset) & 0xffffff00000ULL; + u64 addr = (node->mem->offset + offset) & 0x000000fffffULL; + u32 data; + + spin_lock_irqsave(&priv->lock, flags); + if (unlikely(priv->addr != base)) { + nv_wr32(priv, 0x001700, base >> 16); + priv->addr = base; + } + data = nv_rd32(priv, 0x700000 + addr); + spin_unlock_irqrestore(&priv->lock, flags); + return data; +} + +static void +gk20a_instobj_wr32(struct nvkm_object *object, u64 offset, u32 data) +{ + struct gk20a_instmem_priv *priv = (void *)nvkm_instmem(object); + struct gk20a_instobj_priv *node = (void *)object; + unsigned long flags; + u64 base = (node->mem->offset + offset) & 0xffffff00000ULL; + u64 addr = (node->mem->offset + offset) & 0x000000fffffULL; + + spin_lock_irqsave(&priv->lock, flags); + if (unlikely(priv->addr != base)) { + nv_wr32(priv, 0x001700, base >> 16); + priv->addr = base; + } + nv_wr32(priv, 0x700000 + addr, data); + spin_unlock_irqrestore(&priv->lock, flags); +} + +static void +gk20a_instobj_dtor_dma(struct gk20a_instobj_priv *_node) +{ + struct gk20a_instobj_dma *node = (void *)_node; + struct gk20a_instmem_priv *priv = (void *)nvkm_instmem(node); + struct device *dev = nv_device_base(nv_device(priv)); + + if (unlikely(!node->cpuaddr)) + return; + + dma_free_attrs(dev, _node->mem->size << PAGE_SHIFT, node->cpuaddr, + node->handle, &priv->attrs); +} + +static void +gk20a_instobj_dtor_iommu(struct gk20a_instobj_priv *_node) +{ + struct gk20a_instobj_iommu *node = (void *)_node; + struct gk20a_instmem_priv *priv = (void *)nvkm_instmem(node); + struct nvkm_mm_node *r; + int i; + + if (unlikely(list_empty(&_node->mem->regions))) + return; + + r = list_first_entry(&_node->mem->regions, struct nvkm_mm_node, + rl_entry); + + /* clear bit 34 to unmap pages */ + r->offset &= ~BIT(34 - priv->iommu_pgshift); + + /* Unmap pages from GPU address space and free them */ + for (i = 0; i < _node->mem->size; i++) { + iommu_unmap(priv->domain, + (r->offset + i) << priv->iommu_pgshift, PAGE_SIZE); + __free_page(node->pages[i]); + } + + /* Release area from GPU address space */ + mutex_lock(priv->mm_mutex); + nvkm_mm_free(priv->mm, &r); + mutex_unlock(priv->mm_mutex); +} + +static void +gk20a_instobj_dtor(struct nvkm_object *object) +{ + struct gk20a_instobj_priv *node = (void *)object; + struct gk20a_instmem_priv *priv = (void *)nvkm_instmem(node); + + if (priv->domain) + gk20a_instobj_dtor_iommu(node); + else + gk20a_instobj_dtor_dma(node); + + nvkm_instobj_destroy(&node->base); +} + +static int +gk20a_instobj_ctor_dma(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, u32 npages, u32 align, + struct gk20a_instobj_priv **_node) +{ + struct gk20a_instobj_dma *node; + struct gk20a_instmem_priv *priv = (void *)nvkm_instmem(parent); + struct device *dev = nv_device_base(nv_device(parent)); + int ret; + + ret = nvkm_instobj_create_(parent, engine, oclass, sizeof(*node), + (void **)&node); + *_node = &node->base; + if (ret) + return ret; + + node->cpuaddr = dma_alloc_attrs(dev, npages << PAGE_SHIFT, + &node->handle, GFP_KERNEL, + &priv->attrs); + if (!node->cpuaddr) { + nv_error(priv, "cannot allocate DMA memory\n"); + return -ENOMEM; + } + + /* alignment check */ + if (unlikely(node->handle & (align - 1))) + nv_warn(priv, "memory not aligned as requested: %pad (0x%x)\n", + &node->handle, align); + + /* present memory for being mapped using small pages */ + node->r.type = 12; + node->r.offset = node->handle >> 12; + node->r.length = (npages << PAGE_SHIFT) >> 12; + + node->base._mem.offset = node->handle; + + INIT_LIST_HEAD(&node->base._mem.regions); + list_add_tail(&node->r.rl_entry, &node->base._mem.regions); + + return 0; +} + +static int +gk20a_instobj_ctor_iommu(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, u32 npages, u32 align, + struct gk20a_instobj_priv **_node) +{ + struct gk20a_instobj_iommu *node; + struct gk20a_instmem_priv *priv = (void *)nvkm_instmem(parent); + struct nvkm_mm_node *r; + int ret; + int i; + + ret = nvkm_instobj_create_(parent, engine, oclass, + sizeof(*node) + sizeof(node->pages[0]) * npages, + (void **)&node); + *_node = &node->base; + if (ret) + return ret; + + /* Allocate backing memory */ + for (i = 0; i < npages; i++) { + struct page *p = alloc_page(GFP_KERNEL); + + if (p == NULL) { + ret = -ENOMEM; + goto free_pages; + } + node->pages[i] = p; + } + + mutex_lock(priv->mm_mutex); + /* Reserve area from GPU address space */ + ret = nvkm_mm_head(priv->mm, 0, 1, npages, npages, + align >> priv->iommu_pgshift, &r); + mutex_unlock(priv->mm_mutex); + if (ret) { + nv_error(priv, "virtual space is full!\n"); + goto free_pages; + } + + /* Map into GPU address space */ + for (i = 0; i < npages; i++) { + struct page *p = node->pages[i]; + u32 offset = (r->offset + i) << priv->iommu_pgshift; + + ret = iommu_map(priv->domain, offset, page_to_phys(p), + PAGE_SIZE, IOMMU_READ | IOMMU_WRITE); + if (ret < 0) { + nv_error(priv, "IOMMU mapping failure: %d\n", ret); + + while (i-- > 0) { + offset -= PAGE_SIZE; + iommu_unmap(priv->domain, offset, PAGE_SIZE); + } + goto release_area; + } + } + + /* Bit 34 tells that an address is to be resolved through the IOMMU */ + r->offset |= BIT(34 - priv->iommu_pgshift); + + node->base._mem.offset = ((u64)r->offset) << priv->iommu_pgshift; + + INIT_LIST_HEAD(&node->base._mem.regions); + list_add_tail(&r->rl_entry, &node->base._mem.regions); + + return 0; + +release_area: + mutex_lock(priv->mm_mutex); + nvkm_mm_free(priv->mm, &r); + mutex_unlock(priv->mm_mutex); + +free_pages: + for (i = 0; i < npages && node->pages[i] != NULL; i++) + __free_page(node->pages[i]); + + return ret; +} + +static int +gk20a_instobj_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 _size, + struct nvkm_object **pobject) +{ + struct nvkm_instobj_args *args = data; + struct gk20a_instmem_priv *priv = (void *)nvkm_instmem(parent); + struct gk20a_instobj_priv *node; + u32 size, align; + int ret; + + nv_debug(parent, "%s (%s): size: %x align: %x\n", __func__, + priv->domain ? "IOMMU" : "DMA", args->size, args->align); + + /* Round size and align to page bounds */ + size = max(roundup(args->size, PAGE_SIZE), PAGE_SIZE); + align = max(roundup(args->align, PAGE_SIZE), PAGE_SIZE); + + if (priv->domain) + ret = gk20a_instobj_ctor_iommu(parent, engine, oclass, + size >> PAGE_SHIFT, align, &node); + else + ret = gk20a_instobj_ctor_dma(parent, engine, oclass, + size >> PAGE_SHIFT, align, &node); + *pobject = nv_object(node); + if (ret) + return ret; + + node->mem = &node->_mem; + + /* present memory for being mapped using small pages */ + node->mem->size = size >> 12; + node->mem->memtype = 0; + node->mem->page_shift = 12; + + node->base.addr = node->mem->offset; + node->base.size = size; + + nv_debug(parent, "alloc size: 0x%x, align: 0x%x, gaddr: 0x%llx\n", + size, align, node->mem->offset); + + return 0; +} + +static struct nvkm_instobj_impl +gk20a_instobj_oclass = { + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gk20a_instobj_ctor, + .dtor = gk20a_instobj_dtor, + .init = _nvkm_instobj_init, + .fini = _nvkm_instobj_fini, + .rd32 = gk20a_instobj_rd32, + .wr32 = gk20a_instobj_wr32, + }, +}; + + + +static int +gk20a_instmem_fini(struct nvkm_object *object, bool suspend) +{ + struct gk20a_instmem_priv *priv = (void *)object; + priv->addr = ~0ULL; + return nvkm_instmem_fini(&priv->base, suspend); +} + +static int +gk20a_instmem_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gk20a_instmem_priv *priv; + struct nouveau_platform_device *plat; + int ret; + + ret = nvkm_instmem_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + spin_lock_init(&priv->lock); + + plat = nv_device_to_platform(nv_device(parent)); + if (plat->gpu->iommu.domain) { + priv->domain = plat->gpu->iommu.domain; + priv->mm = plat->gpu->iommu.mm; + priv->iommu_pgshift = plat->gpu->iommu.pgshift; + priv->mm_mutex = &plat->gpu->iommu.mutex; + + nv_info(priv, "using IOMMU\n"); + } else { + init_dma_attrs(&priv->attrs); + /* + * We will access instmem through PRAMIN and thus do not need a + * consistent CPU pointer or kernel mapping + */ + dma_set_attr(DMA_ATTR_NON_CONSISTENT, &priv->attrs); + dma_set_attr(DMA_ATTR_WEAK_ORDERING, &priv->attrs); + dma_set_attr(DMA_ATTR_WRITE_COMBINE, &priv->attrs); + dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &priv->attrs); + + nv_info(priv, "using DMA API\n"); + } + + return 0; +} + +struct nvkm_oclass * +gk20a_instmem_oclass = &(struct nvkm_instmem_impl) { + .base.handle = NV_SUBDEV(INSTMEM, 0xea), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gk20a_instmem_ctor, + .dtor = _nvkm_instmem_dtor, + .init = _nvkm_instmem_init, + .fini = gk20a_instmem_fini, + }, + .instobj = &gk20a_instobj_oclass.base, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv04.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv04.c new file mode 100644 index 000000000..80614f1b2 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv04.c @@ -0,0 +1,185 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv04.h" + +#include + +/****************************************************************************** + * instmem object implementation + *****************************************************************************/ + +static u32 +nv04_instobj_rd32(struct nvkm_object *object, u64 addr) +{ + struct nv04_instmem_priv *priv = (void *)nvkm_instmem(object); + struct nv04_instobj_priv *node = (void *)object; + return nv_ro32(priv, node->mem->offset + addr); +} + +static void +nv04_instobj_wr32(struct nvkm_object *object, u64 addr, u32 data) +{ + struct nv04_instmem_priv *priv = (void *)nvkm_instmem(object); + struct nv04_instobj_priv *node = (void *)object; + nv_wo32(priv, node->mem->offset + addr, data); +} + +static void +nv04_instobj_dtor(struct nvkm_object *object) +{ + struct nv04_instmem_priv *priv = (void *)nvkm_instmem(object); + struct nv04_instobj_priv *node = (void *)object; + nvkm_mm_free(&priv->heap, &node->mem); + nvkm_instobj_destroy(&node->base); +} + +static int +nv04_instobj_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv04_instmem_priv *priv = (void *)nvkm_instmem(parent); + struct nv04_instobj_priv *node; + struct nvkm_instobj_args *args = data; + int ret; + + if (!args->align) + args->align = 1; + + ret = nvkm_instobj_create(parent, engine, oclass, &node); + *pobject = nv_object(node); + if (ret) + return ret; + + ret = nvkm_mm_head(&priv->heap, 0, 1, args->size, args->size, + args->align, &node->mem); + if (ret) + return ret; + + node->base.addr = node->mem->offset; + node->base.size = node->mem->length; + return 0; +} + +struct nvkm_instobj_impl +nv04_instobj_oclass = { + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_instobj_ctor, + .dtor = nv04_instobj_dtor, + .init = _nvkm_instobj_init, + .fini = _nvkm_instobj_fini, + .rd32 = nv04_instobj_rd32, + .wr32 = nv04_instobj_wr32, + }, +}; + +/****************************************************************************** + * instmem subdev implementation + *****************************************************************************/ + +static u32 +nv04_instmem_rd32(struct nvkm_object *object, u64 addr) +{ + return nv_rd32(object, 0x700000 + addr); +} + +static void +nv04_instmem_wr32(struct nvkm_object *object, u64 addr, u32 data) +{ + return nv_wr32(object, 0x700000 + addr, data); +} + +void +nv04_instmem_dtor(struct nvkm_object *object) +{ + struct nv04_instmem_priv *priv = (void *)object; + nvkm_gpuobj_ref(NULL, &priv->ramfc); + nvkm_gpuobj_ref(NULL, &priv->ramro); + nvkm_ramht_ref(NULL, &priv->ramht); + nvkm_gpuobj_ref(NULL, &priv->vbios); + nvkm_mm_fini(&priv->heap); + if (priv->iomem) + iounmap(priv->iomem); + nvkm_instmem_destroy(&priv->base); +} + +static int +nv04_instmem_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv04_instmem_priv *priv; + int ret; + + ret = nvkm_instmem_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + /* PRAMIN aperture maps over the end of VRAM, reserve it */ + priv->base.reserved = 512 * 1024; + + ret = nvkm_mm_init(&priv->heap, 0, priv->base.reserved, 1); + if (ret) + return ret; + + /* 0x00000-0x10000: reserve for probable vbios image */ + ret = nvkm_gpuobj_new(nv_object(priv), NULL, 0x10000, 0, 0, + &priv->vbios); + if (ret) + return ret; + + /* 0x10000-0x18000: reserve for RAMHT */ + ret = nvkm_ramht_new(nv_object(priv), NULL, 0x08000, 0, &priv->ramht); + if (ret) + return ret; + + /* 0x18000-0x18800: reserve for RAMFC (enough for 32 nv30 channels) */ + ret = nvkm_gpuobj_new(nv_object(priv), NULL, 0x00800, 0, + NVOBJ_FLAG_ZERO_ALLOC, &priv->ramfc); + if (ret) + return ret; + + /* 0x18800-0x18a00: reserve for RAMRO */ + ret = nvkm_gpuobj_new(nv_object(priv), NULL, 0x00200, 0, 0, + &priv->ramro); + if (ret) + return ret; + + return 0; +} + +struct nvkm_oclass * +nv04_instmem_oclass = &(struct nvkm_instmem_impl) { + .base.handle = NV_SUBDEV(INSTMEM, 0x04), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_instmem_ctor, + .dtor = nv04_instmem_dtor, + .init = _nvkm_instmem_init, + .fini = _nvkm_instmem_fini, + .rd32 = nv04_instmem_rd32, + .wr32 = nv04_instmem_wr32, + }, + .instobj = &nv04_instobj_oclass.base, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv04.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv04.h new file mode 100644 index 000000000..42b6c9280 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv04.h @@ -0,0 +1,36 @@ +#ifndef __NV04_INSTMEM_H__ +#define __NV04_INSTMEM_H__ +#include "priv.h" + +#include + +extern struct nvkm_instobj_impl nv04_instobj_oclass; + +struct nv04_instmem_priv { + struct nvkm_instmem base; + + void __iomem *iomem; + struct nvkm_mm heap; + + struct nvkm_gpuobj *vbios; + struct nvkm_ramht *ramht; + struct nvkm_gpuobj *ramro; + struct nvkm_gpuobj *ramfc; +}; + +static inline struct nv04_instmem_priv * +nv04_instmem(void *obj) +{ + return (void *)nvkm_instmem(obj); +} + +struct nv04_instobj_priv { + struct nvkm_instobj base; + struct nvkm_mm_node *mem; +}; + +void nv04_instmem_dtor(struct nvkm_object *); + +int nv04_instmem_alloc(struct nvkm_instmem *, struct nvkm_object *, + u32 size, u32 align, struct nvkm_object **pobject); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv40.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv40.c new file mode 100644 index 000000000..b42b8588f --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv40.c @@ -0,0 +1,136 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv04.h" + +#include +#include + +/****************************************************************************** + * instmem subdev implementation + *****************************************************************************/ + +static u32 +nv40_instmem_rd32(struct nvkm_object *object, u64 addr) +{ + struct nv04_instmem_priv *priv = (void *)object; + return ioread32_native(priv->iomem + addr); +} + +static void +nv40_instmem_wr32(struct nvkm_object *object, u64 addr, u32 data) +{ + struct nv04_instmem_priv *priv = (void *)object; + iowrite32_native(data, priv->iomem + addr); +} + +static int +nv40_instmem_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_device *device = nv_device(parent); + struct nv04_instmem_priv *priv; + int ret, bar, vs; + + ret = nvkm_instmem_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + /* map bar */ + if (nv_device_resource_len(device, 2)) + bar = 2; + else + bar = 3; + + priv->iomem = ioremap(nv_device_resource_start(device, bar), + nv_device_resource_len(device, bar)); + if (!priv->iomem) { + nv_error(priv, "unable to map PRAMIN BAR\n"); + return -EFAULT; + } + + /* PRAMIN aperture maps over the end of vram, reserve enough space + * to fit graphics contexts for every channel, the magics come + * from engine/gr/nv40.c + */ + vs = hweight8((nv_rd32(priv, 0x001540) & 0x0000ff00) >> 8); + if (device->chipset == 0x40) priv->base.reserved = 0x6aa0 * vs; + else if (device->chipset < 0x43) priv->base.reserved = 0x4f00 * vs; + else if (nv44_gr_class(priv)) priv->base.reserved = 0x4980 * vs; + else priv->base.reserved = 0x4a40 * vs; + priv->base.reserved += 16 * 1024; + priv->base.reserved *= 32; /* per-channel */ + priv->base.reserved += 512 * 1024; /* pci(e)gart table */ + priv->base.reserved += 512 * 1024; /* object storage */ + + priv->base.reserved = round_up(priv->base.reserved, 4096); + + ret = nvkm_mm_init(&priv->heap, 0, priv->base.reserved, 1); + if (ret) + return ret; + + /* 0x00000-0x10000: reserve for probable vbios image */ + ret = nvkm_gpuobj_new(nv_object(priv), NULL, 0x10000, 0, 0, + &priv->vbios); + if (ret) + return ret; + + /* 0x10000-0x18000: reserve for RAMHT */ + ret = nvkm_ramht_new(nv_object(priv), NULL, 0x08000, 0, &priv->ramht); + if (ret) + return ret; + + /* 0x18000-0x18200: reserve for RAMRO + * 0x18200-0x20000: padding + */ + ret = nvkm_gpuobj_new(nv_object(priv), NULL, 0x08000, 0, 0, + &priv->ramro); + if (ret) + return ret; + + /* 0x20000-0x21000: reserve for RAMFC + * 0x21000-0x40000: padding and some unknown crap + */ + ret = nvkm_gpuobj_new(nv_object(priv), NULL, 0x20000, 0, + NVOBJ_FLAG_ZERO_ALLOC, &priv->ramfc); + if (ret) + return ret; + + return 0; +} + +struct nvkm_oclass * +nv40_instmem_oclass = &(struct nvkm_instmem_impl) { + .base.handle = NV_SUBDEV(INSTMEM, 0x40), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv40_instmem_ctor, + .dtor = nv04_instmem_dtor, + .init = _nvkm_instmem_init, + .fini = _nvkm_instmem_fini, + .rd32 = nv40_instmem_rd32, + .wr32 = nv40_instmem_wr32, + }, + .instobj = &nv04_instobj_oclass.base, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c new file mode 100644 index 000000000..8404143f9 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c @@ -0,0 +1,169 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +#include + +struct nv50_instmem_priv { + struct nvkm_instmem base; + spinlock_t lock; + u64 addr; +}; + +struct nv50_instobj_priv { + struct nvkm_instobj base; + struct nvkm_mem *mem; +}; + +/****************************************************************************** + * instmem object implementation + *****************************************************************************/ + +static u32 +nv50_instobj_rd32(struct nvkm_object *object, u64 offset) +{ + struct nv50_instmem_priv *priv = (void *)nvkm_instmem(object); + struct nv50_instobj_priv *node = (void *)object; + unsigned long flags; + u64 base = (node->mem->offset + offset) & 0xffffff00000ULL; + u64 addr = (node->mem->offset + offset) & 0x000000fffffULL; + u32 data; + + spin_lock_irqsave(&priv->lock, flags); + if (unlikely(priv->addr != base)) { + nv_wr32(priv, 0x001700, base >> 16); + priv->addr = base; + } + data = nv_rd32(priv, 0x700000 + addr); + spin_unlock_irqrestore(&priv->lock, flags); + return data; +} + +static void +nv50_instobj_wr32(struct nvkm_object *object, u64 offset, u32 data) +{ + struct nv50_instmem_priv *priv = (void *)nvkm_instmem(object); + struct nv50_instobj_priv *node = (void *)object; + unsigned long flags; + u64 base = (node->mem->offset + offset) & 0xffffff00000ULL; + u64 addr = (node->mem->offset + offset) & 0x000000fffffULL; + + spin_lock_irqsave(&priv->lock, flags); + if (unlikely(priv->addr != base)) { + nv_wr32(priv, 0x001700, base >> 16); + priv->addr = base; + } + nv_wr32(priv, 0x700000 + addr, data); + spin_unlock_irqrestore(&priv->lock, flags); +} + +static void +nv50_instobj_dtor(struct nvkm_object *object) +{ + struct nv50_instobj_priv *node = (void *)object; + struct nvkm_fb *pfb = nvkm_fb(object); + pfb->ram->put(pfb, &node->mem); + nvkm_instobj_destroy(&node->base); +} + +static int +nv50_instobj_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_fb *pfb = nvkm_fb(parent); + struct nvkm_instobj_args *args = data; + struct nv50_instobj_priv *node; + int ret; + + args->size = max((args->size + 4095) & ~4095, (u32)4096); + args->align = max((args->align + 4095) & ~4095, (u32)4096); + + ret = nvkm_instobj_create(parent, engine, oclass, &node); + *pobject = nv_object(node); + if (ret) + return ret; + + ret = pfb->ram->get(pfb, args->size, args->align, 0, 0x800, &node->mem); + if (ret) + return ret; + + node->base.addr = node->mem->offset; + node->base.size = node->mem->size << 12; + node->mem->page_shift = 12; + return 0; +} + +static struct nvkm_instobj_impl +nv50_instobj_oclass = { + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_instobj_ctor, + .dtor = nv50_instobj_dtor, + .init = _nvkm_instobj_init, + .fini = _nvkm_instobj_fini, + .rd32 = nv50_instobj_rd32, + .wr32 = nv50_instobj_wr32, + }, +}; + +/****************************************************************************** + * instmem subdev implementation + *****************************************************************************/ + +static int +nv50_instmem_fini(struct nvkm_object *object, bool suspend) +{ + struct nv50_instmem_priv *priv = (void *)object; + priv->addr = ~0ULL; + return nvkm_instmem_fini(&priv->base, suspend); +} + +static int +nv50_instmem_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv50_instmem_priv *priv; + int ret; + + ret = nvkm_instmem_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + spin_lock_init(&priv->lock); + return 0; +} + +struct nvkm_oclass * +nv50_instmem_oclass = &(struct nvkm_instmem_impl) { + .base.handle = NV_SUBDEV(INSTMEM, 0x50), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_instmem_ctor, + .dtor = _nvkm_instmem_dtor, + .init = _nvkm_instmem_init, + .fini = nv50_instmem_fini, + }, + .instobj = &nv50_instobj_oclass.base, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/priv.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/priv.h new file mode 100644 index 000000000..b10e292e5 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/priv.h @@ -0,0 +1,54 @@ +#ifndef __NVKM_INSTMEM_PRIV_H__ +#define __NVKM_INSTMEM_PRIV_H__ +#include + +struct nvkm_instobj_impl { + struct nvkm_oclass base; +}; + +struct nvkm_instobj_args { + u32 size; + u32 align; +}; + +#define nvkm_instobj_create(p,e,o,d) \ + nvkm_instobj_create_((p), (e), (o), sizeof(**d), (void **)d) +#define nvkm_instobj_destroy(p) ({ \ + struct nvkm_instobj *iobj = (p); \ + _nvkm_instobj_dtor(nv_object(iobj)); \ +}) +#define nvkm_instobj_init(p) \ + nvkm_object_init(&(p)->base) +#define nvkm_instobj_fini(p,s) \ + nvkm_object_fini(&(p)->base, (s)) + +int nvkm_instobj_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, int, void **); +void _nvkm_instobj_dtor(struct nvkm_object *); +#define _nvkm_instobj_init nvkm_object_init +#define _nvkm_instobj_fini nvkm_object_fini + +struct nvkm_instmem_impl { + struct nvkm_oclass base; + struct nvkm_oclass *instobj; +}; + +#define nvkm_instmem_create(p,e,o,d) \ + nvkm_instmem_create_((p), (e), (o), sizeof(**d), (void **)d) +#define nvkm_instmem_destroy(p) \ + nvkm_subdev_destroy(&(p)->base) +#define nvkm_instmem_init(p) ({ \ + struct nvkm_instmem *imem = (p); \ + _nvkm_instmem_init(nv_object(imem)); \ +}) +#define nvkm_instmem_fini(p,s) ({ \ + struct nvkm_instmem *imem = (p); \ + _nvkm_instmem_fini(nv_object(imem), (s)); \ +}) + +int nvkm_instmem_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, int, void **); +#define _nvkm_instmem_dtor _nvkm_subdev_dtor +int _nvkm_instmem_init(struct nvkm_object *); +int _nvkm_instmem_fini(struct nvkm_object *, bool); +#endif -- cgit 1.2.3-korg