From d93fd75f4dc648a8380047a59541d43d8046d73a Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Sat, 17 Jan 2009 12:57:04 +0100 Subject: [PATCH] Add reworked via driver as "openchrome". --- linux-core/Makefile | 7 + linux-core/Makefile.kernel | 1 + linux-core/openchrome/Makefile | 14 + linux-core/openchrome/ochr_drm.h | 1 + linux-core/openchrome/ttm_agp_backend.c | 1 + linux-core/openchrome/ttm_bo.c | 1 + linux-core/openchrome/ttm_bo_util.c | 1 + linux-core/openchrome/ttm_bo_vm.c | 1 + linux-core/openchrome/ttm_execbuf_util.c | 1 + linux-core/openchrome/ttm_fence.c | 1 + linux-core/openchrome/ttm_fence_user.c | 1 + linux-core/openchrome/ttm_lock.c | 1 + linux-core/openchrome/ttm_memory.c | 1 + linux-core/openchrome/ttm_object.c | 1 + linux-core/openchrome/ttm_pat_compat.c | 1 + linux-core/openchrome/ttm_placement_user.c | 1 + linux-core/openchrome/ttm_tt.c | 1 + linux-core/openchrome/via_buffer.c | 244 +++++ linux-core/openchrome/via_context.c | 139 +++ linux-core/openchrome/via_dma.c | 653 ++++++++++++ linux-core/openchrome/via_dmablit.c | 784 ++++++++++++++ linux-core/openchrome/via_dmablit.h | 116 ++ linux-core/openchrome/via_drv.c | 195 ++++ linux-core/openchrome/via_drv.h | 355 +++++++ linux-core/openchrome/via_execbuf.c | 1045 ++++++++++++++++++ linux-core/openchrome/via_init.c | 1049 +++++++++++++++++++ linux-core/openchrome/via_irq.c | 246 +++++ linux-core/openchrome/via_ttm_fence.c | 230 ++++ linux-core/openchrome/via_ttm_glue.c | 209 ++++ linux-core/openchrome/via_verifier.c | 1104 ++++++++++++++++++++ linux-core/openchrome/via_verifier.h | 62 ++ linux-core/openchrome/via_video.c | 105 ++ shared-core/Makefile.am | 4 +- shared-core/ochr_drm.h | 472 +++++++++ 34 files changed, 7047 insertions(+), 1 deletion(-) create mode 100644 linux-core/openchrome/Makefile create mode 120000 linux-core/openchrome/ochr_drm.h create mode 120000 linux-core/openchrome/ttm_agp_backend.c create mode 120000 linux-core/openchrome/ttm_bo.c create mode 120000 linux-core/openchrome/ttm_bo_util.c create mode 120000 linux-core/openchrome/ttm_bo_vm.c create mode 120000 linux-core/openchrome/ttm_execbuf_util.c create mode 120000 linux-core/openchrome/ttm_fence.c create mode 120000 linux-core/openchrome/ttm_fence_user.c create mode 120000 linux-core/openchrome/ttm_lock.c create mode 120000 linux-core/openchrome/ttm_memory.c create mode 120000 linux-core/openchrome/ttm_object.c create mode 120000 linux-core/openchrome/ttm_pat_compat.c create mode 120000 linux-core/openchrome/ttm_placement_user.c create mode 120000 linux-core/openchrome/ttm_tt.c create mode 100644 linux-core/openchrome/via_buffer.c create mode 100644 linux-core/openchrome/via_context.c create mode 100644 linux-core/openchrome/via_dma.c create mode 100644 linux-core/openchrome/via_dmablit.c create mode 100644 linux-core/openchrome/via_dmablit.h create mode 100644 linux-core/openchrome/via_drv.c create mode 100644 linux-core/openchrome/via_drv.h create mode 100644 linux-core/openchrome/via_execbuf.c create mode 100644 linux-core/openchrome/via_init.c create mode 100644 linux-core/openchrome/via_irq.c create mode 100644 linux-core/openchrome/via_ttm_fence.c create mode 100644 linux-core/openchrome/via_ttm_glue.c create mode 100644 linux-core/openchrome/via_verifier.c create mode 100644 linux-core/openchrome/via_verifier.h create mode 100644 linux-core/openchrome/via_video.c create mode 100644 shared-core/ochr_drm.h diff --git a/linux-core/Makefile b/linux-core/Makefile index 66bc4f25..31260be2 100644 --- a/linux-core/Makefile +++ b/linux-core/Makefile @@ -97,6 +97,8 @@ RADEONMSHEADERS = radeon_ms_driver.h $(DRMHEADERS) PROGS = dristat drmstat CLEANFILES = *.o *.ko $(PROGS) .depend .*.flags .*.d .*.cmd *.mod.c drm_pciids.h .tmp_versions +CLEANMODFILES = *.o *.ko modules.order Module.symvers *.mod.c + # VERSION is not defined from the initial invocation. It is defined when # this Makefile is invoked from the kernel's root Makefile. @@ -218,6 +220,7 @@ endif clean cleandir: rm -rf $(CLEANFILES) + cd openchrome; rm -rf $(CLEANMODFILES) $(MODULE_LIST):: make DRM_MODULES=$@ modules @@ -292,6 +295,7 @@ CONFIG_DRM_NV := n CONFIG_DRM_NOUVEAU := n CONFIG_DRM_XGI := n CONFIG_DRM_RADEON_MS := n +CONFIG_DRM_OPENCHROME := n # Enable module builds for the modules requested/supported. @@ -334,6 +338,9 @@ endif ifneq (,$(findstring radeon_ms,$(DRM_MODULES))) #CONFIG_DRM_RADEON_MS := m endif +ifneq (,$(findstring openchrome,$(DRM_MODULES))) +CONFIG_DRM_OPENCHROME := m +endif # These require AGP support diff --git a/linux-core/Makefile.kernel b/linux-core/Makefile.kernel index 9361fa10..1281ca46 100644 --- a/linux-core/Makefile.kernel +++ b/linux-core/Makefile.kernel @@ -81,3 +81,4 @@ obj-$(CONFIG_DRM_NV) += nv.o #obj-$(CONFIG_DRM_NOUVEAU) += nouveau.o obj-$(CONFIG_DRM_XGI) += xgi.o obj-$(CONFIG_DRM_RADEON_MS) += radeon_ms.o +obj-$(CONFIG_DRM_OPENCHROME) += openchrome/ \ No newline at end of file diff --git a/linux-core/openchrome/Makefile b/linux-core/openchrome/Makefile new file mode 100644 index 00000000..6edadcd1 --- /dev/null +++ b/linux-core/openchrome/Makefile @@ -0,0 +1,14 @@ +# +# Makefile for the drm device driver. This driver provides support for the +# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. + +#ccflags-m := -I.. +openchrome-y := via_buffer.o via_context.o via_dmablit.o via_dma.o \ + via_drv.o via_execbuf.o via_init.o via_irq.o via_ttm_fence.o \ + via_ttm_glue.o via_verifier.o via_video.o \ + ttm_agp_backend.o ttm_bo.o ttm_bo_util.o ttm_bo_vm.o \ + ttm_execbuf_util.o ttm_fence.o ttm_fence_user.o ttm_lock.o \ + ttm_memory.o ttm_object.o ttm_pat_compat.o ttm_placement_user.o \ + ttm_tt.o + +obj-m += openchrome.o diff --git a/linux-core/openchrome/ochr_drm.h b/linux-core/openchrome/ochr_drm.h new file mode 120000 index 00000000..b85a0e32 --- /dev/null +++ b/linux-core/openchrome/ochr_drm.h @@ -0,0 +1 @@ +../../shared-core/ochr_drm.h \ No newline at end of file diff --git a/linux-core/openchrome/ttm_agp_backend.c b/linux-core/openchrome/ttm_agp_backend.c new file mode 120000 index 00000000..b56711e9 --- /dev/null +++ b/linux-core/openchrome/ttm_agp_backend.c @@ -0,0 +1 @@ +../ttm/ttm_agp_backend.c \ No newline at end of file diff --git a/linux-core/openchrome/ttm_bo.c b/linux-core/openchrome/ttm_bo.c new file mode 120000 index 00000000..313aa3b3 --- /dev/null +++ b/linux-core/openchrome/ttm_bo.c @@ -0,0 +1 @@ +../ttm/ttm_bo.c \ No newline at end of file diff --git a/linux-core/openchrome/ttm_bo_util.c b/linux-core/openchrome/ttm_bo_util.c new file mode 120000 index 00000000..9095b7dd --- /dev/null +++ b/linux-core/openchrome/ttm_bo_util.c @@ -0,0 +1 @@ +../ttm/ttm_bo_util.c \ No newline at end of file diff --git a/linux-core/openchrome/ttm_bo_vm.c b/linux-core/openchrome/ttm_bo_vm.c new file mode 120000 index 00000000..146a28a9 --- /dev/null +++ b/linux-core/openchrome/ttm_bo_vm.c @@ -0,0 +1 @@ +../ttm/ttm_bo_vm.c \ No newline at end of file diff --git a/linux-core/openchrome/ttm_execbuf_util.c b/linux-core/openchrome/ttm_execbuf_util.c new file mode 120000 index 00000000..ff96d1b0 --- /dev/null +++ b/linux-core/openchrome/ttm_execbuf_util.c @@ -0,0 +1 @@ +../ttm/ttm_execbuf_util.c \ No newline at end of file diff --git a/linux-core/openchrome/ttm_fence.c b/linux-core/openchrome/ttm_fence.c new file mode 120000 index 00000000..ccea64d8 --- /dev/null +++ b/linux-core/openchrome/ttm_fence.c @@ -0,0 +1 @@ +../ttm/ttm_fence.c \ No newline at end of file diff --git a/linux-core/openchrome/ttm_fence_user.c b/linux-core/openchrome/ttm_fence_user.c new file mode 120000 index 00000000..511cc36d --- /dev/null +++ b/linux-core/openchrome/ttm_fence_user.c @@ -0,0 +1 @@ +../ttm/ttm_fence_user.c \ No newline at end of file diff --git a/linux-core/openchrome/ttm_lock.c b/linux-core/openchrome/ttm_lock.c new file mode 120000 index 00000000..521b5dd4 --- /dev/null +++ b/linux-core/openchrome/ttm_lock.c @@ -0,0 +1 @@ +../ttm/ttm_lock.c \ No newline at end of file diff --git a/linux-core/openchrome/ttm_memory.c b/linux-core/openchrome/ttm_memory.c new file mode 120000 index 00000000..8248571f --- /dev/null +++ b/linux-core/openchrome/ttm_memory.c @@ -0,0 +1 @@ +../ttm/ttm_memory.c \ No newline at end of file diff --git a/linux-core/openchrome/ttm_object.c b/linux-core/openchrome/ttm_object.c new file mode 120000 index 00000000..99d6bc69 --- /dev/null +++ b/linux-core/openchrome/ttm_object.c @@ -0,0 +1 @@ +../ttm/ttm_object.c \ No newline at end of file diff --git a/linux-core/openchrome/ttm_pat_compat.c b/linux-core/openchrome/ttm_pat_compat.c new file mode 120000 index 00000000..268129ff --- /dev/null +++ b/linux-core/openchrome/ttm_pat_compat.c @@ -0,0 +1 @@ +../ttm/ttm_pat_compat.c \ No newline at end of file diff --git a/linux-core/openchrome/ttm_placement_user.c b/linux-core/openchrome/ttm_placement_user.c new file mode 120000 index 00000000..0c6a14b0 --- /dev/null +++ b/linux-core/openchrome/ttm_placement_user.c @@ -0,0 +1 @@ +../ttm/ttm_placement_user.c \ No newline at end of file diff --git a/linux-core/openchrome/ttm_tt.c b/linux-core/openchrome/ttm_tt.c new file mode 120000 index 00000000..2e3e2547 --- /dev/null +++ b/linux-core/openchrome/ttm_tt.c @@ -0,0 +1 @@ +../ttm/ttm_tt.c \ No newline at end of file diff --git a/linux-core/openchrome/via_buffer.c b/linux-core/openchrome/via_buffer.c new file mode 100644 index 00000000..fe1d60b4 --- /dev/null +++ b/linux-core/openchrome/via_buffer.c @@ -0,0 +1,244 @@ +/************************************************************************** + * + * Copyright (c) 2007 Tungsten Graphics, Inc., Cedar Park, TX., USA, + * All Rights Reserved. + * Copyright (c) 2009 VMware, Inc., Palo Alto, CA., USA, + * 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, sub license, 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 (including the + * next paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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: Thomas Hellström + */ + +#include "drmP.h" +#include "ochr_drm.h" +#include "via_drv.h" +#include "ttm/ttm_bo_driver.h" +#include "ttm/ttm_placement_common.h" +#include "ttm/ttm_execbuf_util.h" + +struct ttm_backend *via_create_ttm_backend_entry(struct ttm_bo_device *bdev) +{ + struct drm_via_private *dev_priv = + container_of(bdev, struct drm_via_private, bdev); + + return ttm_agp_backend_init(bdev, dev_priv->dev->agp->bridge); +} + +int via_invalidate_caches(struct ttm_bo_device *bdev, uint32_t flags) +{ + /* + * FIXME: Invalidate texture caches here. + */ + + return 0; +} + +int via_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, + struct ttm_mem_type_manager *man) +{ + struct drm_via_private *dev_priv = + container_of(bdev, struct drm_via_private, bdev); + + switch (type) { + case TTM_PL_SYSTEM: + /* System memory */ + + man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; + man->available_caching = TTM_PL_MASK_CACHING; + man->default_caching = TTM_PL_FLAG_CACHED; + break; + + case TTM_PL_TT: + man->gpu_offset = dev_priv->tt_start; + man->io_offset = dev_priv->tt_start; + man->io_size = dev_priv->tt_size; + man->io_addr = NULL; + man->flags = TTM_MEMTYPE_FLAG_NEEDS_IOREMAP | + TTM_MEMTYPE_FLAG_MAPPABLE; + man->available_caching = TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_WC; + man->default_caching = TTM_PL_FLAG_WC; + break; + + case TTM_PL_VRAM: + /* "On-card" video ram */ + man->gpu_offset = 0; + man->io_offset = dev_priv->vram_start; + man->io_size = dev_priv->vram_size * 1024; + man->flags = TTM_MEMTYPE_FLAG_FIXED | + TTM_MEMTYPE_FLAG_NEEDS_IOREMAP | TTM_MEMTYPE_FLAG_MAPPABLE; + man->io_addr = NULL; + man->available_caching = TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_WC; + man->default_caching = TTM_PL_FLAG_WC; + break; + + case TTM_PL_PRIV0: + man->gpu_offset = dev_priv->agp_bo->offset; + man->io_offset = dev_priv->agp_bo->offset; + man->io_size = (dev_priv->agp_bo->num_pages << PAGE_SHIFT); + man->io_addr = NULL; + man->flags = TTM_MEMTYPE_FLAG_FIXED | + TTM_MEMTYPE_FLAG_NEEDS_IOREMAP | TTM_MEMTYPE_FLAG_MAPPABLE; + man->available_caching = TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_WC; + man->default_caching = TTM_PL_FLAG_WC; + break; + + default: + DRM_ERROR("Unsupported memory type %u\n", (unsigned)type); + return -EINVAL; + } + return 0; +} + +uint32_t via_evict_flags(struct ttm_buffer_object * bo) +{ + uint32_t cur_placement = bo->mem.flags & ~TTM_PL_MASK_MEMTYPE; + + switch (bo->mem.mem_type) { + /* + * Evict pre-bound AGP to VRAM, since + * that's the only fastpath we have. + * That is, when that fast-path is implemented. + */ + case TTM_PL_PRIV0: + return cur_placement | TTM_PL_FLAG_SYSTEM | TTM_PL_FLAG_WC; + default: + return cur_placement | TTM_PL_FLAG_SYSTEM | TTM_PL_FLAG_WC; + } +} + +static int via_move_dmablit(struct ttm_buffer_object *bo, + int evict, int no_wait, struct ttm_mem_reg *new_mem) +{ + struct drm_via_private *dev_priv = + container_of(bo->bdev, struct drm_via_private, bdev); + int ret; + int fence_class; + struct ttm_fence_object *fence; + + if (no_wait) { + DRM_ERROR("Move dmablit busy.\n"); + return -EBUSY; + } + + ret = via_dmablit_bo(bo, new_mem, NULL, &fence_class); + if (unlikely(ret != 0)) { + DRM_ERROR("Dmablit error %d\n", ret); + return ret; + } + ret = ttm_fence_object_create(&dev_priv->fdev, fence_class, + TTM_FENCE_TYPE_EXE, + TTM_FENCE_FLAG_EMIT, &fence); + if (unlikely(ret != 0)) { + (void)via_driver_dma_quiescent(dev_priv->dev); + if (fence) + ttm_fence_object_unref(&fence); + } + + ret = ttm_bo_move_accel_cleanup(bo, (void *)fence, + (void *)(unsigned long) + TTM_FENCE_TYPE_EXE, evict, no_wait, + new_mem); + if (fence) + ttm_fence_object_unref(&fence); + return ret; +} + +static int via_move_vram_tt(struct ttm_buffer_object *bo, + int evict, int no_wait, struct ttm_mem_reg *new_mem) +{ + struct ttm_mem_reg *old_mem = &bo->mem; + int ret; + + if (old_mem->mem_type == TTM_PL_TT) { + struct ttm_mem_reg tmp_mem = *old_mem; + + tmp_mem.mm_node = NULL; + tmp_mem.proposed_flags &= ~TTM_PL_MASK_MEM; + tmp_mem.proposed_flags |= (TTM_PL_FLAG_SYSTEM | + TTM_PL_FLAG_CACHED); + tmp_mem.mem_type = TTM_PL_SYSTEM; + ret = ttm_bo_move_ttm(bo, evict, no_wait, &tmp_mem); + if (ret) + return ret; + + return ttm_bo_move_memcpy(bo, evict, no_wait, new_mem); + } else { + struct ttm_mem_reg tmp_mem = *new_mem; + + tmp_mem.mm_node = NULL; + tmp_mem.proposed_flags &= ~TTM_PL_MASK_MEM; + tmp_mem.proposed_flags |= (TTM_PL_FLAG_SYSTEM | + TTM_PL_FLAG_CACHED); + tmp_mem.mem_type = TTM_PL_SYSTEM; + ret = via_move_dmablit(bo, 1, no_wait, &tmp_mem); + if (ret) + return ret; + return ttm_bo_move_ttm(bo, evict, no_wait, new_mem); + } + return 0; +} + +static void via_move_null(struct ttm_buffer_object *bo, + struct ttm_mem_reg *new_mem) +{ + struct ttm_mem_reg *old_mem = &bo->mem; + uint32_t save_proposed_flags = old_mem->proposed_flags; + + BUG_ON(old_mem->mm_node != NULL); + *old_mem = *new_mem; + new_mem->mm_node = NULL; + old_mem->proposed_flags = save_proposed_flags; +} + +int via_bo_move(struct ttm_buffer_object *bo, + int evict, int interruptible, int no_wait, + struct ttm_mem_reg *new_mem) +{ + struct ttm_mem_reg *old_mem = &bo->mem; + + if (old_mem->mem_type == TTM_PL_SYSTEM && bo->ttm == NULL) { + via_move_null(bo, new_mem); + return 0; + } + + if (old_mem->mem_type == TTM_PL_VRAM && + new_mem->mem_type == TTM_PL_SYSTEM) { + int ret = via_move_dmablit(bo, evict, no_wait, new_mem); + + if (likely(ret == 0)) + return 0; + } + + if ((old_mem->mem_type == TTM_PL_VRAM && + new_mem->mem_type == TTM_PL_TT) || + (old_mem->mem_type == TTM_PL_TT && + new_mem->mem_type == TTM_PL_VRAM)) { + int ret = via_move_vram_tt(bo, evict, no_wait, new_mem); + + if (likely(ret == 0)) + return 0; + } + + return ttm_bo_move_memcpy(bo, evict, no_wait, new_mem); +} diff --git a/linux-core/openchrome/via_context.c b/linux-core/openchrome/via_context.c new file mode 100644 index 00000000..72790de9 --- /dev/null +++ b/linux-core/openchrome/via_context.c @@ -0,0 +1,139 @@ +/************************************************************************** + * + * Copyright (c) 2009 VMware, Inc., Palo Alto, CA., USA, + * 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, sub license, 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 (including the + * next paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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: Thomas Hellström + * + * The purpose of having device private context structures is to + * be able to have pre-allocated per-context reloc-, valicate_buffer-, and + * pci- buffer request in order to parallelize most of the execbuf handling. + * Also we have a possibility to check for invalid context use. + * + * We refcount the via_cpriv struct since it's entirely possible that + * via_context_dtor is called while execbuf is using the context. + */ + +#include "drmP.h" +#include "via_drv.h" + +static void via_context_destroy(struct kref *kref) +{ + struct via_cpriv *cpriv = container_of(kref, struct via_cpriv, kref); + + if (cpriv->reloc_buf) + drm_free(cpriv->reloc_buf, VIA_RELOC_BUF_SIZE, DRM_MEM_DRIVER); + if (cpriv->val_bufs) + vfree(cpriv->val_bufs); + + drm_free(cpriv, sizeof(*cpriv), DRM_MEM_DRIVER); +} + +/* + * Note! odd error reporting. + */ + +int via_context_ctor(struct drm_device *dev, int context) +{ + struct drm_via_private *dev_priv = via_priv(dev); + struct via_cpriv *cpriv; + int ret; + + cpriv = drm_calloc(1, sizeof(*cpriv), DRM_MEM_DRIVER); + if (unlikely(cpriv == NULL)) + return 0; + + cpriv->hash.key = context; + atomic_set(&cpriv->in_execbuf, -1); + kref_init(&cpriv->kref); + cpriv->reloc_buf = drm_alloc(VIA_RELOC_BUF_SIZE, DRM_MEM_DRIVER); + if (unlikely(cpriv->reloc_buf == NULL)) + goto out_err0; + + write_lock(&dev_priv->context_lock); + ret = drm_ht_insert_item(&dev_priv->context_hash, &cpriv->hash); + write_unlock(&dev_priv->context_lock); + + if (unlikely(ret != 0)) + goto out_err1; + + return 1; + out_err1: + drm_free(cpriv->reloc_buf, VIA_RELOC_BUF_SIZE, DRM_MEM_DRIVER); + out_err0: + drm_free(cpriv, sizeof(*cpriv), DRM_MEM_DRIVER); + return 0; +} + +int via_context_dtor(struct drm_device *dev, int context) +{ + struct drm_via_private *dev_priv = via_priv(dev); + struct drm_hash_item *hash; + struct via_cpriv *cpriv = NULL; + int ret; + + via_release_futex(dev_priv, context); + + write_lock(&dev_priv->context_lock); + ret = drm_ht_find_item(&dev_priv->context_hash, context, &hash); + + if (ret == 0) { + (void)drm_ht_remove_item(&dev_priv->context_hash, hash); + cpriv = drm_hash_entry(hash, struct via_cpriv, hash); + } + write_unlock(&dev_priv->context_lock); + + BUG_ON(ret != 0); + + if (likely(cpriv != NULL)) + kref_put(&cpriv->kref, via_context_destroy); + + return 0; +} + +struct via_cpriv *via_context_lookup(struct drm_via_private *dev_priv, + int context) +{ + struct drm_hash_item *hash; + struct via_cpriv *cpriv = NULL; + int ret; + + read_lock(&dev_priv->context_lock); + ret = drm_ht_find_item(&dev_priv->context_hash, context, &hash); + if (likely(ret == 0)) { + cpriv = drm_hash_entry(hash, struct via_cpriv, hash); + kref_get(&cpriv->kref); + } + read_unlock(&dev_priv->context_lock); + + return cpriv; +} + +void via_context_unref(struct via_cpriv **p_cpriv) +{ + struct via_cpriv *cpriv = *p_cpriv; + *p_cpriv = NULL; + kref_put(&cpriv->kref, via_context_destroy); +} diff --git a/linux-core/openchrome/via_dma.c b/linux-core/openchrome/via_dma.c new file mode 100644 index 00000000..ac920307 --- /dev/null +++ b/linux-core/openchrome/via_dma.c @@ -0,0 +1,653 @@ +/* via_dma.c -- DMA support for the VIA Unichrome/Pro + * + * Copyright 2003-2008 Tungsten Graphics, Inc., Cedar Park, Texas. + * All Rights Reserved. + * Copyright 2004 Digeo, Inc., Palo Alto, CA, U.S.A. + * All Rights Reserved. + * Copyright 2004 The Unichrome project. + * All Rights Reserved. + * Copyright (c) 2009 VMware, Inc., Palo Alto, CA., USA, + * 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, sub license, + * 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 (including the + * next paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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: + * Tungsten Graphics, + * Erdi Chen, + * Thomas Hellstrom. + */ + +#include "drmP.h" +#include "drm.h" +#include "ochr_drm.h" +#include "via_drv.h" +#include "via_3d_reg.h" + +#define VIA_FENCE_EXTRA (7*8) + +#define VIA_OUT_RING_H1(nReg, nData) \ + { \ + iowrite32(((nReg) >> 2) | HALCYON_HEADER1, vb); \ + iowrite32(nData, vb + 1); \ + vb += 2; \ + dev_priv->dma_low += 8; \ + } + +#define via_flush_write_combine() DRM_MEMORYBARRIER() + +#define VIA_OUT_RING_QW(w1,w2) \ + { \ + iowrite32(w1, vb); \ + iowrite32(w2, vb + 1); \ + vb += 2; \ + dev_priv->dma_low += 8; \ + } + +#define VIA_TRACKER_INTERVAL 0x10000 + +struct via_dma_tracker { + struct list_head head; + uint32_t seq; + uint32_t loc; +}; + +static void via_cmdbuf_start(struct drm_via_private *dev_priv); +static void via_cmdbuf_pause(struct drm_via_private *dev_priv); +static void via_cmdbuf_rewind(struct drm_via_private *dev_priv); + +/* + * FIXME: A memory cache for trackers? + */ + +static void via_traverse_trackers(struct drm_via_private *dev_priv) +{ + struct via_dma_tracker *tracker, *next; + uint32_t cur_seq = ioread32(dev_priv->fence_map); + + list_for_each_entry_safe(tracker, next, &dev_priv->dma_trackers, head) { + if ((cur_seq - tracker->seq) < (1 << 31)) { + dev_priv->dma_free = tracker->loc; + list_del(&tracker->head); + kfree(tracker); + } else { + break; + } + } +} + +/* + * Fixme: Wait interruptible. + */ + +static inline int via_cmdbuf_wait(struct drm_via_private *dev_priv, + unsigned int size) +{ + uint32_t cur_addr = dev_priv->dma_low; + uint32_t next_addr = cur_addr + size + (512 * 1024); + + while (unlikely(dev_priv->dma_free > cur_addr && + dev_priv->dma_free <= next_addr)) { + msleep(1); + via_traverse_trackers(dev_priv); + } + return 0; +} + +static int via_add_tracker(struct drm_via_private *dev_priv, uint32_t sequence) +{ + struct via_dma_tracker *tracker; + + if (likely + (((dev_priv->dma_low - dev_priv->dma_tracker) & VIA_AGPC_MASK) < + VIA_TRACKER_INTERVAL)) + return 0; + + via_traverse_trackers(dev_priv); + dev_priv->dma_tracker = dev_priv->dma_low; + tracker = kmalloc(sizeof(*tracker), GFP_KERNEL); + + if (!tracker) + return -ENOMEM; + + tracker->loc = dev_priv->dma_low; + tracker->seq = sequence; + + list_add_tail(&tracker->head, &dev_priv->dma_trackers); + + return 0; +} + +/* + * Checks whether buffer head has reach the end. Rewind the ring buffer + * when necessary. + * + * Returns virtual pointer to ring buffer. + */ + +static inline uint32_t __iomem *via_check_dma(struct drm_via_private *dev_priv, + unsigned int size) +{ + if ((dev_priv->dma_low + size + 4 * CMDBUF_ALIGNMENT_SIZE) > + dev_priv->dma_high) { + + /* + * Make sure the reader is wrapped before we do this!! + */ + + via_cmdbuf_wait(dev_priv, size + 4 * CMDBUF_ALIGNMENT_SIZE); + via_cmdbuf_rewind(dev_priv); + } + if (via_cmdbuf_wait(dev_priv, size) != 0) { + return NULL; + } + + return (uint32_t __iomem *) (dev_priv->dma_ptr + dev_priv->dma_low); +} + +void via_dma_initialize(struct drm_via_private *dev_priv) +{ + dev_priv->dma_ptr = (char *)dev_priv->agpc_map; + dev_priv->dma_low = 0; + dev_priv->dma_tracker = dev_priv->dma_low; + dev_priv->dma_high = dev_priv->agpc_bo->num_pages << PAGE_SHIFT; + dev_priv->dma_free = dev_priv->dma_high; + dev_priv->dma_wrap = dev_priv->dma_high; + dev_priv->dma_offset = dev_priv->agpc_bo->offset; + dev_priv->last_pause_ptr = NULL; + dev_priv->hw_addr_ptr = + (uint32_t __iomem *) (dev_priv->mmio_map + 0x418); + + via_cmdbuf_start(dev_priv); +} + +static void via_emit_blit_sequence(struct drm_via_private *dev_priv, + uint32_t __iomem * vb, + uint32_t offset, uint32_t value) +{ + uint32_t vram_offset = dev_priv->fence_bo->offset + offset; + + VIA_OUT_RING_H1(VIA_REG_GEMODE, VIA_GEM_32bpp); + VIA_OUT_RING_H1(VIA_REG_FGCOLOR, value); + VIA_OUT_RING_H1(VIA_REG_DSTBASE, (vram_offset & ~0x1f) >> 3); + VIA_OUT_RING_H1(VIA_REG_PITCH, VIA_PITCH_ENABLE | + (4 >> 3) | ((4 >> 3) << 16)); + VIA_OUT_RING_H1(VIA_REG_DSTPOS, (vram_offset & 0x1f) >> 2); + VIA_OUT_RING_H1(VIA_REG_DIMENSION, 0); + VIA_OUT_RING_H1(VIA_REG_GECMD, VIA_GEC_BLT | VIA_GEC_FIXCOLOR_PAT + | (VIA_ROP_PAT << 24)); +#if 0 + VIA_OUT_RING_QW(VIA_VIDEO_HEADER6, 0x00000002); + VIA_OUT_RING_QW(0x00F60000, 0x00000000); + VIA_OUT_RING_QW(0x326C, (3 << 30) | (0x1f << 25)); + VIA_OUT_RING_QW(0x33D0, (1 << 27) | (1 << 7) | (1 << 6) | + (1 << 4) | (1 << 0)); +#endif +} + +static void via_blit_sequence(struct drm_via_private *dev_priv, + uint32_t offset, uint32_t value) +{ + uint32_t vram_offset = dev_priv->fence_bo->offset + offset; + + VIA_WRITE(VIA_REG_GEMODE, VIA_GEM_32bpp); + VIA_WRITE(VIA_REG_FGCOLOR, value); + VIA_WRITE(VIA_REG_DSTBASE, (vram_offset & ~0x1f) >> 3); + VIA_WRITE(VIA_REG_PITCH, VIA_PITCH_ENABLE | + (4 >> 3) | ((4 >> 3) << 16)); + VIA_WRITE(VIA_REG_DSTPOS, (vram_offset & 0x1f) >> 2); + VIA_WRITE(VIA_REG_DIMENSION, 0); + VIA_WRITE(VIA_REG_GECMD, VIA_GEC_BLT | VIA_GEC_FIXCOLOR_PAT + | (VIA_ROP_PAT << 24)); +} + +/* + * This function is used internally by ring buffer mangement code. + * + * Returns virtual pointer to ring buffer. + */ +static inline uint32_t __iomem *via_get_dma(struct drm_via_private *dev_priv) +{ + return (uint32_t __iomem *) (dev_priv->dma_ptr + dev_priv->dma_low); +} + +static void via_emit_fence_seq(struct drm_via_private *dev_priv, + uint32_t offset, uint32_t value) +{ + via_emit_blit_sequence(dev_priv, via_get_dma(dev_priv), offset, value); +} + +void via_emit_fence_seq_standalone(struct drm_via_private *dev_priv, + uint32_t offset, uint32_t value) +{ + via_emit_blit_sequence(dev_priv, + via_check_dma(dev_priv, VIA_FENCE_EXTRA), + offset, value); + via_cmdbuf_pause(dev_priv); +} + +int via_driver_dma_quiescent(struct drm_device *dev) +{ + struct drm_via_private *dev_priv = dev->dev_private; + + via_wait_idle(dev_priv); + return 0; +} + +static inline uint32_t *via_align_buffer(struct drm_via_private *dev_priv, + uint32_t * vb, int qw_count) +{ + for (; qw_count > 0; --qw_count) { + VIA_OUT_RING_QW(HC_DUMMY, HC_DUMMY); + } + return vb; +} + +/* + * Hooks a segment of data into the tail of the ring-buffer by + * modifying the pause address stored in the buffer itself. If + * the regulator has already paused, restart it. + */ +static int via_hook_segment(struct drm_via_private *dev_priv, + uint32_t pause_addr_hi, uint32_t pause_addr_lo, + int no_pci_fire) +{ + int paused, count; + uint32_t *paused_at = dev_priv->last_pause_ptr; + uint32_t reader, ptr; + uint32_t diff; + + paused = 0; + via_flush_write_combine(); + (void)ioread32((uint32_t *) (via_get_dma(dev_priv) - 1)); + iowrite32(pause_addr_lo, paused_at); + via_flush_write_combine(); + (void)ioread32(paused_at); + + reader = ioread32(dev_priv->hw_addr_ptr); + ptr = ((volatile char *)paused_at - dev_priv->dma_ptr) + + dev_priv->dma_offset + 4; + + dev_priv->last_pause_ptr = via_get_dma(dev_priv) - 1; + + /* + * If there is a possibility that the command reader will + * miss the new pause address and pause on the old one, + * In that case we need to program the new start address + * using PCI. + */ + + diff = (uint32_t) (ptr - reader) - dev_priv->dma_diff; + count = 10000000; + + while ((diff < CMDBUF_ALIGNMENT_SIZE) && count--) { + paused = (VIA_READ(0x41c) & 0x80000000); + if (paused) + break; + reader = ioread32(dev_priv->hw_addr_ptr); + diff = (uint32_t) (ptr - reader) - dev_priv->dma_diff; + } + + paused = VIA_READ(0x41c) & 0x80000000; + if (paused && !no_pci_fire) { + reader = ioread32(dev_priv->hw_addr_ptr); + diff = (uint32_t) (ptr - reader) - dev_priv->dma_diff; + diff &= (dev_priv->dma_high - 1); + if (diff < (dev_priv->dma_high >> 1)) { + + if (diff != 0) { + uint32_t __iomem *rekick; + + DRM_INFO("Paused at incorrect address. " + "0x%08x, 0x%08x 0x%08x 0x%08x. Restarting.\n", + ptr, reader, VIA_READ(0x40c), + dev_priv->dma_diff); + /* + * Obtain the new pause address the command + * reader was supposed to pick up. + */ + + rekick = (uint32_t *) + dev_priv->dma_ptr + + ((reader - dev_priv->dma_offset + + dev_priv->dma_diff - 4) >> 2); + pause_addr_lo = ioread32(rekick); + pause_addr_hi = ioread32(--rekick); + DRM_INFO("Restarting 0x%08x 0x%08x\n", + pause_addr_hi, pause_addr_lo); + } + + /* + * There is a concern that these writes may stall the PCI bus + * if the GPU is not idle. However, idling the GPU first + * doesn't make a difference. + */ + VIA_WRITE(VIA_REG_TRANSET, (HC_ParaType_PreCR << 16)); + VIA_WRITE(VIA_REG_TRANSPACE, pause_addr_hi); + VIA_WRITE(VIA_REG_TRANSPACE, pause_addr_lo); + + /* + * Really need to flush PCI posting here, + * but some register reads will + * flush AGP completely according to docs. + * FIXME: Find a suitable register to read. + */ + } + } + + return paused; +} + +static inline int via_is_idle(uint32_t status) +{ + return ((status & (VIA_VR_QUEUE_BUSY | + VIA_CMD_RGTR_BUSY | + VIA_2D_ENG_BUSY | + VIA_3D_ENG_BUSY)) == (VIA_VR_QUEUE_BUSY)); +} + +void via_wait_idle(struct drm_via_private *dev_priv) +{ + unsigned long _end = jiffies + VIA_IDLE_TIMEOUT; + uint32_t status; + + status = VIA_READ(VIA_REG_STATUS); + do { + if (unlikely(!via_is_idle(status))) { + schedule(); + status = VIA_READ(VIA_REG_STATUS); + } + } while (unlikely(!time_after_eq(jiffies, _end) && + !via_is_idle(status))); + + if (unlikely(!via_is_idle(status))) + DRM_INFO("Warning: Idle timeout.\n"); + + return; +} + +static uint32_t *via_align_cmd(struct drm_via_private *dev_priv, + uint32_t cmd_type, uint32_t addr, + uint32_t * cmd_addr_hi, uint32_t * cmd_addr_lo, + int skip_wait) +{ + uint32_t agp_base; + uint32_t cmd_addr, addr_lo, addr_hi; + uint32_t *vb; + uint32_t qw_pad_count; + + if (!skip_wait) + via_cmdbuf_wait(dev_priv, 2 * CMDBUF_ALIGNMENT_SIZE); + + vb = via_get_dma(dev_priv); + VIA_OUT_RING_QW(HC_HEADER2 | ((VIA_REG_TRANSET >> 2) << 12) | + (VIA_REG_TRANSPACE >> 2), HC_ParaType_PreCR << 16); + + agp_base = dev_priv->dma_offset; + qw_pad_count = (CMDBUF_ALIGNMENT_SIZE >> 3) - + ((dev_priv->dma_low & CMDBUF_ALIGNMENT_MASK) >> 3); + + cmd_addr = (addr) ? addr : + agp_base + dev_priv->dma_low - 4 + (qw_pad_count << 3); + addr_lo = ((HC_SubA_HAGPBpL << 24) | (cmd_type & HC_HAGPBpID_MASK) | + (cmd_addr & HC_HAGPBpL_MASK)); + addr_hi = ((HC_SubA_HAGPBpH << 24) | (cmd_addr >> 24)); + + vb = via_align_buffer(dev_priv, vb, qw_pad_count - 1); + VIA_OUT_RING_QW(*cmd_addr_hi = addr_hi, *cmd_addr_lo = addr_lo); + + return vb; +} + +static void via_cmdbuf_start(struct drm_via_private *dev_priv) +{ + uint32_t pause_addr_lo, pause_addr_hi; + uint32_t start_addr, start_addr_lo; + uint32_t end_addr, end_addr_lo; + uint32_t command; + uint32_t agp_base; + uint32_t ptr; + uint32_t reader; + int count; + + dev_priv->dma_low = 0; + + agp_base = dev_priv->dma_offset; + start_addr = agp_base; + end_addr = agp_base + dev_priv->dma_high; + + start_addr_lo = ((HC_SubA_HAGPBstL << 24) | (start_addr & 0xFFFFFF)); + end_addr_lo = ((HC_SubA_HAGPBendL << 24) | (end_addr & 0xFFFFFF)); + command = ((HC_SubA_HAGPCMNT << 24) | (start_addr >> 24) | + ((end_addr & 0xff000000) >> 16)); + + dev_priv->last_pause_ptr = + via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, + &pause_addr_hi, &pause_addr_lo, 1) - 1; + + via_flush_write_combine(); + (void)ioread32((uint32_t *) dev_priv->last_pause_ptr); + + VIA_WRITE(VIA_REG_TRANSET, (HC_ParaType_PreCR << 16)); + VIA_WRITE(VIA_REG_TRANSPACE, command); + VIA_WRITE(VIA_REG_TRANSPACE, start_addr_lo); + VIA_WRITE(VIA_REG_TRANSPACE, end_addr_lo); + + VIA_WRITE(VIA_REG_TRANSPACE, pause_addr_hi); + VIA_WRITE(VIA_REG_TRANSPACE, pause_addr_lo); + DRM_WRITEMEMORYBARRIER(); + VIA_WRITE(VIA_REG_TRANSPACE, command | HC_HAGPCMNT_MASK); + VIA_READ(VIA_REG_TRANSPACE); + + dev_priv->dma_diff = 0; + + count = 10000000; + while (!(VIA_READ(0x41c) & 0x80000000) && count--) ; + + reader = ioread32(dev_priv->hw_addr_ptr); + ptr = ((volatile char *)dev_priv->last_pause_ptr - dev_priv->dma_ptr) + + dev_priv->dma_offset + 4; + + /* + * This is the difference between where we tell the + * command reader to pause and where it actually pauses. + * This differs between hw implementation so we need to + * detect it. + */ + + dev_priv->dma_diff = ptr - reader; +} + +static inline void via_dummy_bitblt(struct drm_via_private *dev_priv) +{ + uint32_t *vb = via_get_dma(dev_priv); + VIA_OUT_RING_H1(VIA_REG_DSTPOS, (0 | (0 << 16))); + VIA_OUT_RING_H1(VIA_REG_DIMENSION, 0 | (0 << 16)); + VIA_OUT_RING_H1(VIA_REG_GECMD, VIA_GEC_BLT | VIA_GEC_FIXCOLOR_PAT | + 0xAA000000); +} + +static void via_cmdbuf_rewind(struct drm_via_private *dev_priv) +{ + uint32_t agp_base; + uint32_t pause_addr_lo, pause_addr_hi; + uint32_t jump_addr_lo, jump_addr_hi, hook_addr; + uint32_t *hook_ptr; + uint32_t dma_low_save1, dma_low_save2; + + agp_base = dev_priv->dma_offset; + hook_ptr = via_align_cmd(dev_priv, HC_HAGPBpID_JUMP, 0, + &jump_addr_hi, &jump_addr_lo, 0); + via_align_cmd(dev_priv, HC_HAGPBpID_JUMP, 0, &jump_addr_hi, + &hook_addr, 0); + iowrite32(hook_addr, --hook_ptr); + dev_priv->dma_wrap = dev_priv->dma_low; + + /* + * Wrap command buffer to the beginning. + */ + + dev_priv->dma_low = 0; + if (via_cmdbuf_wait(dev_priv, CMDBUF_ALIGNMENT_SIZE) != 0) { + DRM_ERROR("via_cmdbuf_jump failed\n"); + } + + via_dummy_bitblt(dev_priv); + via_dummy_bitblt(dev_priv); + + hook_ptr = + via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, &pause_addr_hi, + &pause_addr_lo, 0); + via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, &pause_addr_hi, + &pause_addr_lo, 0); + + iowrite32(pause_addr_lo, --hook_ptr); + dma_low_save1 = dev_priv->dma_low; + + /* + * Now, set a trap that will pause the regulator if it tries to rerun the old + * command buffer. (Which may happen if via_hook_segment detecs a command regulator pause + * and reissues the jump command over PCI, while the regulator has already taken the jump + * and actually paused at the current buffer end). + * There appears to be no other way to detect this condition, since the hw_addr_pointer + * does not seem to get updated immediately when a jump occurs. + */ + + hook_ptr = + via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, &pause_addr_hi, + &pause_addr_lo, 0); + via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, &pause_addr_hi, + &pause_addr_lo, 0); + iowrite32(pause_addr_lo, --hook_ptr); + + dma_low_save2 = dev_priv->dma_low; + dev_priv->dma_low = dma_low_save1; + via_hook_segment(dev_priv, jump_addr_hi, jump_addr_lo, 0); + dev_priv->dma_low = dma_low_save2; + via_hook_segment(dev_priv, pause_addr_hi, pause_addr_lo, 0); +} + +static void via_cmdbuf_flush(struct drm_via_private *dev_priv, + uint32_t cmd_type) +{ + uint32_t pause_addr_lo, pause_addr_hi, hook; + uint32_t __iomem *hook_addr; + + hook_addr = + via_align_cmd(dev_priv, cmd_type, 0, &pause_addr_hi, &pause_addr_lo, + 0); + if (cmd_type == HC_HAGPBpID_PAUSE) { + via_align_cmd(dev_priv, cmd_type, 0, &pause_addr_hi, &hook, 0); + iowrite32(hook, --hook_addr); + } + via_hook_segment(dev_priv, pause_addr_hi, pause_addr_lo, 0); +} + +static void via_cmdbuf_pause(struct drm_via_private *dev_priv) +{ + via_cmdbuf_flush(dev_priv, HC_HAGPBpID_PAUSE); + +} + +void via_dma_takedown(struct drm_via_private *dev_priv) +{ + via_cmdbuf_flush(dev_priv, HC_HAGPBpID_STOP); + via_wait_idle(dev_priv); + while (!list_empty(&dev_priv->dma_trackers)) + via_traverse_trackers(dev_priv); +} + +int via_copy_cmdbuf(struct drm_via_private *dev_priv, + uint64_t cmd_buffer, + uint32_t size, + uint32_t mechanism, uint32_t ** cmdbuf_addr, int *is_iomem) +{ + void __user *commands = (void __user *)(unsigned long)cmd_buffer; + uint32_t *vb; + int ret; + + if (unlikely(size > VIA_PCI_BUF_SIZE)) { + DRM_ERROR("Command buffer too large.\n"); + return -ENOMEM; + } + + if (mechanism == _VIA_MECHANISM_AGP && drm_via_disable_verifier) { + vb = via_check_dma(dev_priv, size + VIA_FENCE_EXTRA); + if (unlikely(!vb)) { + DRM_ERROR("No space in AGP ring buffer.\n"); + return -EBUSY; + } + *is_iomem = 1; + } else { + vb = (uint32_t *) dev_priv->pci_buf; + *is_iomem = 0; + } + + ret = copy_from_user(vb, commands, size); + if (unlikely(ret != 0)) { + DRM_ERROR("Failed copying command " + "buffer from user space.\n"); + return ret; + } + *cmdbuf_addr = vb; + return 0; +} + +int via_dispatch_commands(struct drm_device *dev, unsigned long size, + uint32_t mechanism) +{ + struct drm_via_private *dev_priv = via_priv(dev); + uint32_t *vb; + uint32_t seq; + int ret; + + switch (mechanism) { + case _VIA_MECHANISM_AGP: + if (!drm_via_disable_verifier) { + vb = via_check_dma(dev_priv, size + VIA_FENCE_EXTRA); + if (unlikely(!vb)) { + DRM_ERROR("No space in AGP ring buffer.\n"); + return -EBUSY; + } + memcpy_toio(vb, dev_priv->pci_buf, size); + } + dev_priv->dma_low += size; + seq = + atomic_add_return(1, &dev_priv->fence_seq[VIA_ENGINE_CMD]); + via_emit_fence_seq(dev_priv, VIA_FENCE_OFFSET_CMD, seq); + via_add_tracker(dev_priv, seq); + via_cmdbuf_pause(dev_priv); + return 0; + case _VIA_MECHANISM_PCI: + via_wait_idle(dev_priv); + ret = via_parse_command_stream(dev, + (uint32_t *) dev_priv->pci_buf, + size); + seq = + atomic_add_return(1, &dev_priv->fence_seq[VIA_ENGINE_CMD]); + via_wait_idle(dev_priv); + via_blit_sequence(dev_priv, VIA_FENCE_OFFSET_CMD, seq); + via_wait_idle(dev_priv); + return ret; + default: + return -EINVAL; + } + + return -EINVAL; +} diff --git a/linux-core/openchrome/via_dmablit.c b/linux-core/openchrome/via_dmablit.c new file mode 100644 index 00000000..e89a3131 --- /dev/null +++ b/linux-core/openchrome/via_dmablit.c @@ -0,0 +1,784 @@ +/* via_dmablit.c -- PCI DMA BitBlt support for the VIA Unichrome/Pro + * + * Copyright (C) 2005 Thomas Hellstrom, All Rights Reserved. + * Copyright (c) 2009 VMware, Inc., Palo Alto, CA., USA, + * 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, sub license, + * 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 (including the + * next paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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: + * Thomas Hellstrom. + * Partially based on code obtained from Digeo Inc. + */ + +#include "drmP.h" +#include "ochr_drm.h" +#include "via_drv.h" +#include "via_dmablit.h" + +#include + +#define VIA_PGDN(x) (((unsigned long)(x)) & PAGE_MASK) +#define VIA_PGOFF(x) (((unsigned long)(x)) & ~PAGE_MASK) +#define VIA_PFN(x) ((unsigned long)(x) >> PAGE_SHIFT) + +enum via_dmablit_sys_type { + VIA_DMABLIT_BO, + VIA_DMABLIT_USER +}; + +struct drm_via_descriptor { + uint32_t mem_addr; + uint32_t dev_addr; + uint32_t size; + uint32_t next; +}; + +struct via_dmablit { + uint32_t num_lines; + uint32_t line_length; + + uint32_t vram_offs; + uint32_t vram_stride; + + uint32_t mem_pg_offs; + uint32_t mem_stride; + + int to_fb; +}; + +struct drm_via_sg_info { + uint32_t engine; + uint32_t fence_seq; + struct list_head head; + struct page **pages; + unsigned long num_pages; + enum via_dmablit_sys_type sys_type; + struct drm_via_descriptor **desc_pages; + int num_desc_pages; + int num_desc; + enum dma_data_direction direction; + unsigned char *bounce_buffer; + dma_addr_t chain_start; + unsigned int descriptors_per_page; + int aborted; + enum { + dr_via_device_mapped, + dr_via_desc_pages_alloc, + dr_via_pages_locked, + dr_via_pages_alloc, + dr_via_sg_init + } state; +}; + +/* + * Unmap a DMA mapping. + */ + +static void +via_unmap_blit_from_device(struct pci_dev *pdev, struct drm_via_sg_info *vsg) +{ + int num_desc = vsg->num_desc; + unsigned cur_descriptor_page = num_desc / vsg->descriptors_per_page; + unsigned descriptor_this_page = num_desc % vsg->descriptors_per_page; + struct drm_via_descriptor *desc_ptr = + vsg->desc_pages[cur_descriptor_page] + descriptor_this_page; + dma_addr_t next = vsg->chain_start; + + while (num_desc--) { + if (descriptor_this_page-- == 0) { + cur_descriptor_page--; + descriptor_this_page = vsg->descriptors_per_page - 1; + desc_ptr = vsg->desc_pages[cur_descriptor_page] + + descriptor_this_page; + } + dma_unmap_single(&pdev->dev, next, sizeof(*desc_ptr), + DMA_TO_DEVICE); + dma_unmap_page(&pdev->dev, desc_ptr->mem_addr, desc_ptr->size, + vsg->direction); + next = (dma_addr_t) desc_ptr->next; + desc_ptr--; + } +} + +/* + * If mode = 0, count how many descriptors are needed. + * If mode = 1, Map the DMA pages for the device, put together and map also the descriptors. + * Descriptors are run in reverse order by the hardware because we are not allowed to update the + * 'next' field without syncing calls when the descriptor is already mapped. + */ + +static void +via_map_blit_for_device(struct pci_dev *pdev, + const struct via_dmablit *xfer, + struct drm_via_sg_info *vsg, int mode) +{ + unsigned cur_descriptor_page = 0; + unsigned num_descriptors_this_page = 0; + unsigned long mem_addr = xfer->mem_pg_offs; + unsigned long cur_mem; + uint32_t fb_addr = xfer->vram_offs; + uint32_t cur_fb; + unsigned long line_len; + unsigned remaining_len; + int num_desc = 0; + int cur_line; + dma_addr_t next = 0 | VIA_DMA_DPR_EC; + struct drm_via_descriptor *desc_ptr = NULL; + + if (mode == 1) + desc_ptr = vsg->desc_pages[cur_descriptor_page]; + + for (cur_line = 0; cur_line < xfer->num_lines; ++cur_line) { + + line_len = xfer->line_length; + cur_fb = fb_addr; + cur_mem = mem_addr; + + while (line_len > 0) { + + remaining_len = + min(PAGE_SIZE - VIA_PGOFF(cur_mem), line_len); + line_len -= remaining_len; + + if (mode == 1) { + struct page *page; + + page = vsg->pages[cur_mem >> PAGE_SHIFT]; + BUG_ON(cur_descriptor_page >= + vsg->num_desc_pages); + desc_ptr->mem_addr = + dma_map_page(&pdev->dev, + page, + VIA_PGOFF(cur_mem), + remaining_len, vsg->direction); + desc_ptr->dev_addr = cur_fb; + desc_ptr->size = remaining_len; + desc_ptr->next = (uint32_t) next; + next = + dma_map_single(&pdev->dev, desc_ptr, + sizeof(*desc_ptr), + DMA_TO_DEVICE); + desc_ptr++; + if (++num_descriptors_this_page >= + vsg->descriptors_per_page) { + num_descriptors_this_page = 0; + desc_ptr = + vsg-> + desc_pages[++cur_descriptor_page]; + } + } + + num_desc++; + cur_mem += remaining_len; + cur_fb += remaining_len; + } + + mem_addr += xfer->mem_stride; + fb_addr += xfer->vram_stride; + } + + if (mode == 1) { + vsg->chain_start = next; + vsg->state = dr_via_device_mapped; + } + vsg->num_desc = num_desc; +} + +/* + * Function that frees up all resources for a blit. It is usable even if the + * blit info has only been partially built as long as the status enum is consistent + * with the actual status of the used resources. + */ + +static void via_free_sg_info(struct pci_dev *pdev, struct drm_via_sg_info *vsg) +{ + struct page *page; + int i; + + switch (vsg->state) { + case dr_via_device_mapped: + via_unmap_blit_from_device(pdev, vsg); + case dr_via_desc_pages_alloc: + for (i = 0; i < vsg->num_desc_pages; ++i) { + if (vsg->desc_pages[i] != NULL) + free_page((unsigned long)vsg->desc_pages[i]); + } + kfree(vsg->desc_pages); + case dr_via_pages_locked: + if (vsg->sys_type == VIA_DMABLIT_USER) { + for (i = 0; i < vsg->num_pages; ++i) { + if (NULL != (page = vsg->pages[i])) { + if (!PageReserved(page) + && (DMA_FROM_DEVICE == + vsg->direction)) + SetPageDirty(page); + page_cache_release(page); + } + } + } + case dr_via_pages_alloc: + if (vsg->sys_type == VIA_DMABLIT_USER) + vfree(vsg->pages); + default: + vsg->state = dr_via_sg_init; + } + if (vsg->bounce_buffer) { + vfree(vsg->bounce_buffer); + vsg->bounce_buffer = NULL; + } +} + +/* + * Fire a blit engine. + */ + +static void +via_fire_dmablit(struct drm_device *dev, struct drm_via_sg_info *vsg, + int engine) +{ + struct drm_via_private *dev_priv = via_priv(dev); + + VIA_WRITE(VIA_PCI_DMA_MAR0 + engine * 0x10, 0); + VIA_WRITE(VIA_PCI_DMA_DAR0 + engine * 0x10, 0); + VIA_WRITE(VIA_PCI_DMA_CSR0 + engine * 0x04, + VIA_DMA_CSR_DD | VIA_DMA_CSR_TD | VIA_DMA_CSR_DE); + VIA_WRITE(VIA_PCI_DMA_MR0 + engine * 0x04, + VIA_DMA_MR_CM | VIA_DMA_MR_TDIE); + VIA_WRITE(VIA_PCI_DMA_BCR0 + engine * 0x10, 0); + VIA_WRITE(VIA_PCI_DMA_DPR0 + engine * 0x10, vsg->chain_start); + DRM_WRITEMEMORYBARRIER(); + VIA_WRITE(VIA_PCI_DMA_CSR0 + engine * 0x04, + VIA_DMA_CSR_DE | VIA_DMA_CSR_TS); + VIA_READ(VIA_PCI_DMA_CSR0 + engine * 0x04); +} + +#if 0 +/* + * Obtain a page pointer array and lock all pages into system memory. + */ + +static int +via_lock_all_dma_pages(struct drm_via_sg_info *vsg, + struct drm_via_dmablit *xfer) +{ + int ret; + unsigned long first_pfn = VIA_PFN(xfer->mem_addr); + vsg->num_pages = + VIA_PFN(xfer->mem_addr + (xfer->num_lines * xfer->mem_stride - 1)) - + first_pfn + 1; + + if (NULL == + (vsg->pages = vmalloc(sizeof(struct page *) * vsg->num_pages))) + return -ENOMEM; + memset(vsg->pages, 0, sizeof(struct page *) * vsg->num_pages); + down_read(¤t->mm->mmap_sem); + ret = get_user_pages(current, current->mm, + (unsigned long)xfer->mem_addr, + vsg->num_pages, + (vsg->direction == DMA_FROM_DEVICE), + 0, vsg->pages, NULL); + + up_read(¤t->mm->mmap_sem); + if (ret != vsg->num_pages) { + if (ret < 0) + return ret; + vsg->state = dr_via_pages_locked; + return -EINVAL; + } + vsg->state = dr_via_pages_locked; + DRM_DEBUG("DMA pages locked\n"); + return 0; +} + +#endif + +/* + * Allocate DMA capable memory for the blit descriptor chain, and an array that keeps track of the + * pages we allocate. We don't want to use kmalloc for the descriptor chain because it may be + * quite large for some blits, and pages don't need to be contingous. + */ + +static int via_alloc_desc_pages(struct drm_via_sg_info *vsg) +{ + int i; + + vsg->descriptors_per_page = + PAGE_SIZE / sizeof(struct drm_via_descriptor); + vsg->num_desc_pages = + (vsg->num_desc + vsg->descriptors_per_page - + 1) / vsg->descriptors_per_page; + + if (NULL == + (vsg->desc_pages = + kmalloc(sizeof(void *) * vsg->num_desc_pages, GFP_KERNEL))) + return -ENOMEM; + + memset(vsg->desc_pages, 0, sizeof(void *) * vsg->num_desc_pages); + vsg->state = dr_via_desc_pages_alloc; + for (i = 0; i < vsg->num_desc_pages; ++i) { + if (NULL == (vsg->desc_pages[i] = (struct drm_via_descriptor *) + __get_free_page(GFP_KERNEL))) + return -ENOMEM; + } + DRM_DEBUG("Allocated %d pages for %d descriptors.\n", + vsg->num_desc_pages, vsg->num_desc); + return 0; +} + +static void via_abort_dmablit(struct drm_device *dev, int engine) +{ + struct drm_via_private *dev_priv = via_priv(dev); + + VIA_WRITE(VIA_PCI_DMA_CSR0 + engine * 0x04, VIA_DMA_CSR_TA); +} + +static void via_dmablit_engine_off(struct drm_device *dev, int engine) +{ + struct drm_via_private *dev_priv = via_priv(dev); + + VIA_WRITE(VIA_PCI_DMA_CSR0 + engine * 0x04, + VIA_DMA_CSR_TD | VIA_DMA_CSR_DD); +} + +/* + * The dmablit part of the IRQ handler. Trying to do only reasonably fast things here. + * The rest, like unmapping and freeing memory for done blits is done in a separate workqueue + * task. Basically the task of the interrupt handler is to submit a new blit to the engine, while + * the workqueue task takes care of processing associated with the old blit. + */ + +void via_dmablit_handler(struct drm_device *dev, int engine) +{ + struct drm_via_private *dev_priv = via_priv(dev); + struct drm_via_blitq *blitq = dev_priv->blit_queues + engine; + struct drm_via_sg_info *cur; + int done_transfer; + unsigned long irqsave = 0; + uint32_t status = 0; + + spin_lock_irqsave(&blitq->blit_lock, irqsave); + + done_transfer = blitq->is_active && + ((status = + VIA_READ(VIA_PCI_DMA_CSR0 + engine * 0x04)) & VIA_DMA_CSR_TD); + done_transfer = done_transfer || (blitq->aborting + && !(status & VIA_DMA_CSR_DE)); + + cur = blitq->cur; + if (done_transfer) { + cur->aborted = blitq->aborting; + blitq->completed_fence_seq = cur->fence_seq; + list_add_tail(&cur->head, &blitq->done_blits); + + /* + * Clear transfer done flag. + */ + + VIA_WRITE(VIA_PCI_DMA_CSR0 + engine * 0x04, VIA_DMA_CSR_TD); + + blitq->is_active = 0; + blitq->aborting = 0; + schedule_work(&blitq->wq); + + } else if (blitq->is_active && time_after_eq(jiffies, blitq->end)) { + + /* + * Abort transfer after one second. + */ + + via_abort_dmablit(dev, engine); + blitq->aborting = 1; + blitq->end = jiffies + DRM_HZ; + } + + if (!blitq->is_active) { + if (!list_empty(&blitq->pending_blits)) { + cur = list_entry(blitq->pending_blits.next, + struct drm_via_sg_info, head); + list_del_init(&cur->head); + via_fire_dmablit(dev, cur, engine); + blitq->is_active = 1; + blitq->cur = cur; + blitq->end = jiffies + DRM_HZ; + if (!timer_pending(&blitq->poll_timer)) { + blitq->poll_timer.expires = jiffies + 1; + add_timer(&blitq->poll_timer); + } + } else { + if (timer_pending(&blitq->poll_timer)) { + del_timer(&blitq->poll_timer); + } + via_dmablit_engine_off(dev, engine); + } + } + + spin_unlock_irqrestore(&blitq->blit_lock, irqsave); +} + +/* + * A timer that regularly polls the blit engine in cases where we don't have interrupts: + * a) Broken hardware (typically those that don't have any video capture facility). + * b) Blit abort. The hardware doesn't send an interrupt when a blit is aborted. + * The timer and hardware IRQ's can and do work in parallel. If the hardware has + * irqs, it will shorten the latency somewhat. + */ + +static void via_dmablit_timer(unsigned long data) +{ + struct drm_via_blitq *blitq = (struct drm_via_blitq *)data; + struct drm_device *dev = blitq->dev; + int engine = (int) + (blitq - ((struct drm_via_private *)dev->dev_private)->blit_queues); + + DRM_DEBUG("Polling timer called for engine %d, jiffies %lu\n", engine, + (unsigned long)jiffies); + + via_dmablit_handler(dev, engine); + + if (!timer_pending(&blitq->poll_timer)) { + blitq->poll_timer.expires = jiffies + 1; + add_timer(&blitq->poll_timer); + + /* + * Rerun handler to delete timer if engines are off, and + * to shorten abort latency. This is a little nasty. + */ + + via_dmablit_handler(dev, engine); + } +} + +/* + * Workqueue task that frees data and mappings associated with a blit. + * Also wakes up waiting processes. Each of these tasks handles one + * blit engine only and may not be called on each interrupt. + */ + +static void via_dmablit_workqueue(struct work_struct *work) +{ + struct drm_via_blitq *blitq = + container_of(work, struct drm_via_blitq, wq); + struct drm_device *dev = blitq->dev; + unsigned long irqsave; + struct drm_via_sg_info *cur_sg; + + DRM_DEBUG("Workqueue task called for blit engine %ld\n", (unsigned long) + (blitq - + ((struct drm_via_private *)dev->dev_private)->blit_queues)); + + spin_lock_irqsave(&blitq->blit_lock, irqsave); + while (!list_empty(&blitq->done_blits)) { + + cur_sg = list_entry(blitq->done_blits.next, + struct drm_via_sg_info, head); + + list_del_init(&cur_sg->head); + + spin_unlock_irqrestore(&blitq->blit_lock, irqsave); + DRM_WAKEUP(&blitq->busy_queue); + + via_free_sg_info(dev->pdev, cur_sg); + kfree(cur_sg); + + spin_lock_irqsave(&blitq->blit_lock, irqsave); + } + + spin_unlock_irqrestore(&blitq->blit_lock, irqsave); +} + +/* + * Init all blit engines. Currently we use two, but some hardware have 4. + */ + +void via_init_dmablit(struct drm_device *dev) +{ + int i; + struct drm_via_private *dev_priv = via_priv(dev); + struct drm_via_blitq *blitq; + + pci_set_master(dev->pdev); + + for (i = 0; i < VIA_NUM_BLIT_ENGINES; ++i) { + blitq = dev_priv->blit_queues + i; + blitq->dev = dev; + blitq->cur = NULL; + blitq->num_pending = 0; + blitq->is_active = 0; + blitq->aborting = 0; + blitq->num_pending = 0; + INIT_LIST_HEAD(&blitq->pending_blits); + INIT_LIST_HEAD(&blitq->done_blits); + spin_lock_init(&blitq->blit_lock); + DRM_INIT_WAITQUEUE(&blitq->busy_queue); + INIT_WORK(&blitq->wq, via_dmablit_workqueue); + init_timer(&blitq->poll_timer); + blitq->poll_timer.function = &via_dmablit_timer; + blitq->poll_timer.data = (unsigned long)blitq; + } +} + +int +via_dmablit_bo(struct ttm_buffer_object *bo, + struct ttm_mem_reg *new_mem, + struct page **pages, int *fence_class) +{ + struct ttm_bo_device *bdev = bo->bdev; + struct drm_via_private *dev_priv = + container_of(bdev, struct drm_via_private, bdev); + struct drm_device *dev = dev_priv->dev; + int draw = (new_mem->mem_type == TTM_PL_VRAM); + int ret = 0; + struct ttm_tt *ttm = bo->ttm; + struct via_dmablit xfer; + int engine; + struct drm_via_sg_info *vsg; + struct drm_via_blitq *blitq; + unsigned long irq_flags; + int i; + struct page *d; + + BUG_ON(!draw && (bo->mem.mem_type != TTM_PL_VRAM)); + BUG_ON(!ttm); + + for (i = 0; i < ttm->num_pages; ++i) { + d = ttm_tt_get_page(ttm, i); + if (unlikely(d == NULL)) + return -ENOMEM; + } + + vsg = kmalloc(sizeof(*vsg), GFP_KERNEL); + if (unlikely(vsg == NULL)) + return -ENOMEM; + + if (draw) { + struct ttm_mem_type_manager *man = + &bdev->man[new_mem->mem_type]; + vsg->direction = DMA_TO_DEVICE; + xfer.vram_offs = (new_mem->mm_node->start << PAGE_SHIFT) + + man->gpu_offset; + engine = 0; + } else { + vsg->direction = DMA_FROM_DEVICE; + xfer.vram_offs = bo->offset; + engine = 1; + } + + blitq = dev_priv->blit_queues + engine; + vsg->bounce_buffer = NULL; + vsg->state = dr_via_sg_init; + + xfer.mem_stride = PAGE_SIZE; + xfer.line_length = xfer.mem_stride; + xfer.vram_stride = xfer.mem_stride; + xfer.num_lines = bo->num_pages; + xfer.to_fb = draw; + xfer.mem_pg_offs = 0; + + vsg->sys_type = VIA_DMABLIT_BO; + vsg->pages = (pages == NULL) ? ttm->pages : pages; + vsg->num_pages = bo->num_pages; + + vsg->state = dr_via_pages_alloc; + vsg->state = dr_via_pages_locked; + + via_map_blit_for_device(dev->pdev, &xfer, vsg, 0); + ret = via_alloc_desc_pages(vsg); + + if (unlikely(ret != 0)) + goto out_err; + + via_map_blit_for_device(dev->pdev, &xfer, vsg, 1); + spin_lock_irqsave(&blitq->blit_lock, irq_flags); + list_add_tail(&vsg->head, &blitq->pending_blits); + ++blitq->num_pending; + *fence_class = engine + VIA_ENGINE_DMA0; + vsg->fence_seq = + atomic_add_return(1, &dev_priv->fence_seq[*fence_class]); + spin_unlock_irqrestore(&blitq->blit_lock, irq_flags); + + via_dmablit_handler(dev, engine); + + return 0; + out_err: + via_free_sg_info(dev->pdev, vsg); + kfree(vsg); + return ret; +} + +#if 0 +/* + * Build all info and do all mappings required for a blit. + */ + +static int +via_build_sg_info(struct drm_device *dev, struct drm_via_sg_info *vsg, + struct drm_via_dmablit *xfer) +{ + int draw = xfer->to_fb; + int ret = 0; + + vsg->direction = (draw) ? DMA_TO_DEVICE : DMA_FROM_DEVICE; + vsg->bounce_buffer = NULL; + + vsg->state = dr_via_sg_init; + + if (xfer->num_lines <= 0 || xfer->line_length <= 0) { + DRM_ERROR("Zero size bitblt.\n"); + return -EINVAL; + } + + /* + * Below check is a driver limitation, not a hardware one. We + * don't want to lock unused pages, and don't want to incoporate the + * extra logic of avoiding them. Make sure there are no. + * (Not a big limitation anyway.) + */ + + if ((xfer->mem_stride - xfer->line_length) > 2 * PAGE_SIZE) { + DRM_ERROR("Too large system memory stride. Stride: %d, " + "Length: %d\n", xfer->mem_stride, xfer->line_length); + return -EINVAL; + } + + if ((xfer->mem_stride == xfer->line_length) && + (xfer->fb_stride == xfer->line_length)) { + xfer->mem_stride *= xfer->num_lines; + xfer->line_length = xfer->mem_stride; + xfer->fb_stride = xfer->mem_stride; + xfer->num_lines = 1; + } + + /* + * Don't lock an arbitrary large number of pages, since that causes a + * DOS security hole. + */ + + if (xfer->num_lines > 2048 + || (xfer->num_lines * xfer->mem_stride > (2048 * 2048 * 4))) { + DRM_ERROR("Too large PCI DMA bitblt.\n"); + return -EINVAL; + } + + /* + * we allow a negative fb stride to allow flipping of images in + * transfer. + */ + + if (xfer->mem_stride < xfer->line_length || + abs(xfer->fb_stride) < xfer->line_length) { + DRM_ERROR("Invalid frame-buffer / memory stride.\n"); + return -EINVAL; + } + + /* + * A hardware bug seems to be worked around if system memory addresses start on + * 16 byte boundaries. This seems a bit restrictive however. VIA is contacted + * about this. Meanwhile, impose the following restrictions: + */ + +#ifdef VIA_BUGFREE + if ((((unsigned long)xfer->mem_addr & 3) != + ((unsigned long)xfer->fb_addr & 3)) || ((xfer->num_lines > 1) + && ((xfer->mem_stride & 3) + != + (xfer-> + fb_stride & 3)))) { + DRM_ERROR("Invalid DRM bitblt alignment.\n"); + return -EINVAL; + } +#else + if ((((unsigned long)xfer->mem_addr & 15) + || ((unsigned long)xfer->fb_addr & 3)) || ((xfer->num_lines > 1) + && + ((xfer->mem_stride & 15) + || (xfer-> + fb_stride & 3)))) { + DRM_ERROR("Invalid DRM bitblt alignment.\n"); + return -EINVAL; + } +#endif + + if (0 != (ret = via_lock_all_dma_pages(vsg, xfer))) { + DRM_ERROR("Could not lock DMA pages.\n"); + via_free_sg_info(dev->pdev, vsg); + return ret; + } + + via_map_blit_for_device(dev->pdev, xfer, vsg, 0); + if (0 != (ret = via_alloc_desc_pages(vsg))) { + DRM_ERROR("Could not allocate DMA descriptor pages.\n"); + via_free_sg_info(dev->pdev, vsg); + return ret; + } + via_map_blit_for_device(dev->pdev, xfer, vsg, 1); + + return 0; +} + +/* + * Grab a free slot. Build blit info and queue a blit. + */ + +static int via_dmablit(struct drm_device *dev, struct drm_via_dmablit *xfer, + int interruptible) +{ + struct drm_via_private *dev_priv = via_priv(dev); + struct drm_via_sg_info *vsg; + struct drm_via_blitq *blitq; + int ret; + int engine; + unsigned long irqsave; + + if (dev_priv == NULL) { + DRM_ERROR("Called without initialization.\n"); + return -EINVAL; + } + + engine = (xfer->to_fb) ? 0 : 1; + blitq = dev_priv->blit_queues + engine; + vsg = kmalloc(sizeof(*vsg), GFP_KERNEL); + + if (unlikely(vsg == NULL)) + return -ENOMEM; + + ret = via_build_sg_info(dev, vsg, xfer); + if (unlikely(ret != 0)) + goto out_err0; + + spin_lock_irqsave(&blitq->blit_lock, irqsave); + list_add_tail(&vsg->head, &blitq->pending_blits); + ++blitq->num_pending; + vsg->fence_seq = + atomic_add_return(1, + &dev_priv->fence_seq[engine + VIA_ENGINE_DMA0]); + xfer->sync.sync_handle = vsg->fence_seq; + xfer->sync.engine = engine + VIA_ENGINE_DMA0; + spin_unlock_irqrestore(&blitq->blit_lock, irqsave); + + via_dmablit_handler(dev, engine, 0); + + return 0; + out_err0: + kfree(vsg); + return ret; +} + +#endif diff --git a/linux-core/openchrome/via_dmablit.h b/linux-core/openchrome/via_dmablit.h new file mode 100644 index 00000000..7700d274 --- /dev/null +++ b/linux-core/openchrome/via_dmablit.h @@ -0,0 +1,116 @@ +/* via_dmablit.h -- PCI DMA BitBlt support for the VIA Unichrome/Pro + * + * Copyright (c) 2005 Thomas Hellstrom. + * All Rights Reserved. + * Copyright (c) 2009 VMware, Inc., Palo Alto, CA., USA, + * 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, sub license, + * 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 (including the + * next paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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: + * Thomas Hellstrom. + * Register info from Digeo Inc. + */ + +#ifndef _VIA_DMABLIT_H +#define _VIA_DMABLIT_H + +#include + +#define VIA_NUM_BLIT_ENGINES 2 +#define VIA_NUM_BLIT_SLOTS 8 + +struct drm_via_sg_info; + +struct drm_via_blitq { + uint32_t completed_fence_seq; + struct drm_device *dev; + unsigned long end; + int aborting; + int is_active; + unsigned int num_pending; + struct list_head pending_blits; + struct list_head done_blits; + struct drm_via_sg_info *cur; + spinlock_t blit_lock; + wait_queue_head_t busy_queue; + struct work_struct wq; + struct timer_list poll_timer; +}; + +/* + * PCI DMA Registers + * Channels 2 & 3 don't seem to be implemented in hardware. + */ + +#define VIA_PCI_DMA_MAR0 0xE40 /* Memory Address Register of Channel 0 */ +#define VIA_PCI_DMA_DAR0 0xE44 /* Device Address Register of Channel 0 */ +#define VIA_PCI_DMA_BCR0 0xE48 /* Byte Count Register of Channel 0 */ +#define VIA_PCI_DMA_DPR0 0xE4C /* Descriptor Pointer Register of Channel 0 */ + +#define VIA_PCI_DMA_MAR1 0xE50 /* Memory Address Register of Channel 1 */ +#define VIA_PCI_DMA_DAR1 0xE54 /* Device Address Register of Channel 1 */ +#define VIA_PCI_DMA_BCR1 0xE58 /* Byte Count Register of Channel 1 */ +#define VIA_PCI_DMA_DPR1 0xE5C /* Descriptor Pointer Register of Channel 1 */ + +#define VIA_PCI_DMA_MAR2 0xE60 /* Memory Address Register of Channel 2 */ +#define VIA_PCI_DMA_DAR2 0xE64 /* Device Address Register of Channel 2 */ +#define VIA_PCI_DMA_BCR2 0xE68 /* Byte Count Register of Channel 2 */ +#define VIA_PCI_DMA_DPR2 0xE6C /* Descriptor Pointer Register of Channel 2 */ + +#define VIA_PCI_DMA_MAR3 0xE70 /* Memory Address Register of Channel 3 */ +#define VIA_PCI_DMA_DAR3 0xE74 /* Device Address Register of Channel 3 */ +#define VIA_PCI_DMA_BCR3 0xE78 /* Byte Count Register of Channel 3 */ +#define VIA_PCI_DMA_DPR3 0xE7C /* Descriptor Pointer Register of Channel 3 */ + +#define VIA_PCI_DMA_MR0 0xE80 /* Mode Register of Channel 0 */ +#define VIA_PCI_DMA_MR1 0xE84 /* Mode Register of Channel 1 */ +#define VIA_PCI_DMA_MR2 0xE88 /* Mode Register of Channel 2 */ +#define VIA_PCI_DMA_MR3 0xE8C /* Mode Register of Channel 3 */ + +#define VIA_PCI_DMA_CSR0 0xE90 /* Command/Status Register of Channel 0 */ +#define VIA_PCI_DMA_CSR1 0xE94 /* Command/Status Register of Channel 1 */ +#define VIA_PCI_DMA_CSR2 0xE98 /* Command/Status Register of Channel 2 */ +#define VIA_PCI_DMA_CSR3 0xE9C /* Command/Status Register of Channel 3 */ + +#define VIA_PCI_DMA_PTR 0xEA0 /* Priority Type Register */ + +/* Define for DMA engine */ +/* DPR */ +#define VIA_DMA_DPR_EC (1<<1) /* end of chain */ +#define VIA_DMA_DPR_DDIE (1<<2) /* descriptor done interrupt enable */ +#define VIA_DMA_DPR_DT (1<<3) /* direction of transfer (RO) */ + +/* MR */ +#define VIA_DMA_MR_CM (1<<0) /* chaining mode */ +#define VIA_DMA_MR_TDIE (1<<1) /* transfer done interrupt enable */ +#define VIA_DMA_MR_HENDMACMD (1<<7) /* ? */ + +/* CSR */ +#define VIA_DMA_CSR_DE (1<<0) /* DMA enable */ +#define VIA_DMA_CSR_TS (1<<1) /* transfer start */ +#define VIA_DMA_CSR_TA (1<<2) /* transfer abort */ +#define VIA_DMA_CSR_TD (1<<3) /* transfer done */ +#define VIA_DMA_CSR_DD (1<<4) /* descriptor done */ +#define VIA_DMA_DPR_EC (1<<1) /* end of chain */ + +extern void via_dmablit_handler(struct drm_device *dev, int engine); +extern void via_init_dmablit(struct drm_device *dev); +#endif diff --git a/linux-core/openchrome/via_drv.c b/linux-core/openchrome/via_drv.c new file mode 100644 index 00000000..928697d5 --- /dev/null +++ b/linux-core/openchrome/via_drv.c @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2007-2008 Tungsten Graphics, Inc., Cedar Park, TX., USA, + * All Rights Reserved. + * Copyright (c) 2009 VMware, Inc., Palo Alto, CA., USA, + * 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, sub license, + * 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 (including the + * next paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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. + */ + +#include "drmP.h" +#include "ochr_drm.h" +#include "via_drv.h" +#include "drm_pciids.h" + +int drm_via_disable_verifier; +MODULE_PARM_DESC(disable_verifier, "Disable GPU command security check. " + "DANGEROUS!!"); +module_param_named(disable_verifier, drm_via_disable_verifier, int, 0600); + +static int dri_library_name(struct drm_device *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "unichrome\n"); +} + +static struct pci_device_id pciidlist[] = { + viadrv_PCI_IDS +}; + +#define DRM_IOCTL_VIA_DEC_FUTEX \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_DEC_FUTEX,\ + struct drm_via_futex) + +#define DRM_IOCTL_VIA_TTM_EXECBUF \ + DRM_IOW(DRM_COMMAND_BASE + DRM_VIA_TTM_EXECBUF,\ + struct drm_via_ttm_execbuf_arg) + +#define DRM_VIA_TTM_PL_CREATE (TTM_PL_CREATE + DRM_VIA_PLACEMENT_OFFSET) +#define DRM_VIA_TTM_PL_REFERENCE (TTM_PL_REFERENCE + DRM_VIA_PLACEMENT_OFFSET) +#define DRM_VIA_TTM_PL_UNREF (TTM_PL_UNREF + DRM_VIA_PLACEMENT_OFFSET) +#define DRM_VIA_TTM_PL_SYNCCPU (TTM_PL_SYNCCPU + DRM_VIA_PLACEMENT_OFFSET) +#define DRM_VIA_TTM_PL_WAITIDLE (TTM_PL_WAITIDLE + DRM_VIA_PLACEMENT_OFFSET) +#define DRM_VIA_TTM_PL_SETSTATUS (TTM_PL_SETSTATUS + DRM_VIA_PLACEMENT_OFFSET) + +#define DRM_VIA_TTM_FENCE_SIGNALED (TTM_FENCE_SIGNALED + DRM_VIA_FENCE_OFFSET) +#define DRM_VIA_TTM_FENCE_FINISH (TTM_FENCE_FINISH + DRM_VIA_FENCE_OFFSET) +#define DRM_VIA_TTM_FENCE_UNREF (TTM_FENCE_UNREF + DRM_VIA_FENCE_OFFSET) + +#define DRM_IOCTL_VIA_TTM_PL_CREATE \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_TTM_PL_CREATE,\ + union ttm_pl_create_arg) +#define DRM_IOCTL_VIA_TTM_PL_REFERENCE \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_TTM_PL_REFERENCE,\ + union ttm_pl_reference_arg) +#define DRM_IOCTL_VIA_TTM_PL_UNREF \ + DRM_IOW(DRM_COMMAND_BASE + DRM_VIA_TTM_PL_UNREF,\ + struct ttm_pl_reference_req) +#define DRM_IOCTL_VIA_TTM_PL_SYNCCPU \ + DRM_IOW(DRM_COMMAND_BASE + DRM_VIA_TTM_PL_SYNCCPU,\ + struct ttm_synccpu_arg) +#define DRM_IOCTL_VIA_TTM_PL_WAITIDLE \ + DRM_IOW(DRM_COMMAND_BASE + DRM_VIA_TTM_PL_WAITIDLE,\ + struct ttm_waitidle_arg) +#define DRM_IOCTL_VIA_TTM_PL_SETSTATUS \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_TTM_PL_CREATE,\ + union ttm_pl_setstatus_arg) +#define DRM_IOCTL_VIA_TTM_FENCE_SIGNALED \ + DRM_IOWR (DRM_COMMAND_BASE + DRM_VIA_TTM_FENCE_SIGNALED, \ + union ttm_fence_signaled_arg) +#define DRM_IOCTL_VIA_TTM_FENCE_FINISH \ + DRM_IOWR (DRM_COMMAND_BASE + DRM_VIA_TTM_FENCE_FINISH, \ + union ttm_fence_finish_arg) +#define DRM_IOCTL_VIA_TTM_FENCE_UNREF \ + DRM_IOW (DRM_COMMAND_BASE + DRM_VIA_TTM_FENCE_UNREF, \ + struct ttm_fence_unref_arg) + +static struct drm_ioctl_desc via_ioctls[] = { + DRM_IOCTL_DEF(DRM_VIA_VT, via_vt_ioctl, DRM_AUTH | DRM_MASTER), + DRM_IOCTL_DEF(DRM_VIA_GET_PARAM, via_getparam_ioctl, DRM_AUTH), + DRM_IOCTL_DEF(DRM_VIA_EXTENSION, via_extension_ioctl, DRM_AUTH), + DRM_IOCTL_DEF(DRM_VIA_DEC_FUTEX, via_decoder_futex, DRM_AUTH), + DRM_IOCTL_DEF(DRM_VIA_TTM_EXECBUF, via_execbuffer, DRM_AUTH), + DRM_IOCTL_DEF(DRM_VIA_TTM_PL_CREATE, via_pl_create_ioctl, + DRM_AUTH), + DRM_IOCTL_DEF(DRM_VIA_TTM_PL_REFERENCE, via_pl_reference_ioctl, + DRM_AUTH), + DRM_IOCTL_DEF(DRM_VIA_TTM_PL_UNREF, via_pl_unref_ioctl, + DRM_AUTH), + DRM_IOCTL_DEF(DRM_VIA_TTM_PL_SYNCCPU, via_pl_synccpu_ioctl, + DRM_AUTH), + DRM_IOCTL_DEF(DRM_VIA_TTM_PL_WAITIDLE, via_pl_waitidle_ioctl, + DRM_AUTH), + DRM_IOCTL_DEF(DRM_VIA_TTM_PL_SETSTATUS, via_pl_setstatus_ioctl, + DRM_AUTH), + DRM_IOCTL_DEF(DRM_VIA_TTM_FENCE_SIGNALED, + via_fence_signaled_ioctl, DRM_AUTH), + DRM_IOCTL_DEF(DRM_VIA_TTM_FENCE_FINISH, via_fence_finish_ioctl, + DRM_AUTH), + DRM_IOCTL_DEF(DRM_VIA_TTM_FENCE_UNREF, via_fence_unref_ioctl, + DRM_AUTH) +}; + +static int probe(struct pci_dev *pdev, const struct pci_device_id *ent); +static struct drm_driver driver = { + .driver_features = + DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_HAVE_IRQ | + DRIVER_IRQ_SHARED, + .load = via_driver_load, + .unload = via_driver_unload, + .context_ctor = via_context_ctor, + .context_dtor = via_context_dtor, + .get_vblank_counter = via_get_vblank_counter, + .enable_vblank = via_enable_vblank, + .disable_vblank = via_disable_vblank, + .irq_preinstall = via_driver_irq_preinstall, + .irq_postinstall = via_driver_irq_postinstall, + .irq_uninstall = via_driver_irq_uninstall, + .irq_handler = via_driver_irq_handler, + .dma_quiescent = NULL, + .dri_library_name = dri_library_name, + .reclaim_buffers = drm_core_reclaim_buffers, + .reclaim_buffers_locked = NULL, + .firstopen = via_firstopen, + .reclaim_buffers_idlelocked = NULL, + .lastclose = via_lastclose, + .get_map_ofs = drm_core_get_map_ofs, + .get_reg_ofs = drm_core_get_reg_ofs, + .ioctls = via_ioctls, + .num_ioctls = DRM_ARRAY_SIZE(via_ioctls), + .fops = { + .owner = THIS_MODULE, + .open = via_open, + .release = via_release, + .unlocked_ioctl = drm_unlocked_ioctl, + .mmap = via_mmap, + .poll = drm_poll, + .fasync = drm_fasync, + .read = via_ttm_read, + .write = via_ttm_write}, + .pci_driver = { + .name = DRIVER_NAME, + .id_table = pciidlist, + .probe = probe, + .remove = __devexit_p(drm_cleanup_pci), + .resume = via_resume, + .suspend = via_suspend, + }, + .fence_driver = NULL, + .bo_driver = NULL, + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRM_VIA_DRIVER_DATE, + .major = DRM_VIA_DRIVER_MAJOR, + .minor = DRM_VIA_DRIVER_MINOR, + .patchlevel = DRM_VIA_DRIVER_PATCHLEVEL +}; + +static int probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + return drm_get_dev(pdev, ent, &driver); +} + +static int __init via_init(void) +{ + via_init_command_verifier(); + return drm_init(&driver, pciidlist); +} + +static void __exit via_exit(void) +{ + drm_exit(&driver); +} + +module_init(via_init); +module_exit(via_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL and additional rights"); diff --git a/linux-core/openchrome/via_drv.h b/linux-core/openchrome/via_drv.h new file mode 100644 index 00000000..75565e75 --- /dev/null +++ b/linux-core/openchrome/via_drv.h @@ -0,0 +1,355 @@ +/* + * Copyright (c) 2007-2008 Tungsten Graphics, Inc., Cedar Park, TX., USA, + * All Rights Reserved. + * Copyright (c) 2009 VMware, Inc., Palo Alto, CA., USA, + * 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, sub license, + * 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 (including the + * next paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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. + */ + +#ifndef _VIA_DRV_H_ +#define _VIA_DRV_H_ + +#include "via_verifier.h" +#include "ochr_drm.h" +#include "via_dmablit.h" +#include "ttm/ttm_object.h" +#include "ttm/ttm_fence_driver.h" +#include "ttm/ttm_bo_driver.h" +#include "ttm/ttm_lock.h" +#include "ttm/ttm_memory.h" + +#define DRIVER_AUTHOR "Tungsten Graphics" +#define DRIVER_NAME "openchrome" +#define DRIVER_DESC "VIA Unichrome / Pro / II" + +#define VIA_VQ_SIZE (512*1024) +#define VIA_AGPC_SIZE (2*1024*1024) +#define VIA_AGPC_MASK (VIA_AGPC_SIZE -1) +#define VIA_AGPBO_SIZE (16*1024*1024) + +#define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT) + +/* + * Registers go here. + */ +#define CMDBUF_ALIGNMENT_SIZE (0x100) +#define CMDBUF_ALIGNMENT_MASK (0x0ff) + +/* defines for VIA 2D registers */ +#define VIA_REG_GEMODE 0x004 +#define VIA_GEM_8bpp 0x00000000 +#define VIA_GEM_16bpp 0x00000100 +#define VIA_GEM_32bpp 0x00000300 + + +#define VIA_REG_SRCBASE 0x030 +#define VIA_REG_DSTBASE 0x034 +#define VIA_REG_PITCH 0x038 +#define VIA_PITCH_ENABLE 0x80000000 + +#define VIA_REG_SRCCOLORKEY 0x01C +#define VIA_REG_KEYCONTROL 0x02C +#define VIA_REG_SRCPOS 0x008 +#define VIA_REG_DSTPOS 0x00C + +#define VIA_REG_GECMD 0x000 +#define VIA_GEC_BLT 0x00000001 +#define VIA_GEC_INCX 0x00000000 +#define VIA_GEC_DECY 0x00004000 +#define VIA_GEC_INCY 0x00000000 +#define VIA_GEC_DECX 0x00008000 +#define VIA_GEC_FIXCOLOR_PAT 0x00002000 + +#define VIA_REG_DIMENSION 0x010 /* width and height */ +#define VIA_REG_FGCOLOR 0x018 + +#define VIA_ROP_CLEAR 0x00 +#define VIA_ROP_SRC 0xCC +#define VIA_ROP_PAT 0xF0 +#define VIA_ROP_SET 0xFF + + + + +/* defines for VIA 3D registers */ +#define VIA_REG_STATUS 0x400 +#define VIA_REG_TRANSET 0x43C +#define VIA_REG_TRANSPACE 0x440 + +/* VIA_REG_STATUS(0x400): Engine Status */ +#define VIA_CMD_RGTR_BUSY 0x00000080 /* Command Regulator is busy */ +#define VIA_2D_ENG_BUSY 0x00000002 /* 2D Engine is busy */ +#define VIA_3D_ENG_BUSY 0x00000001 /* 3D Engine is busy */ +#define VIA_VR_QUEUE_BUSY 0x00020000 /* Virtual Queue is busy */ +#define VIA_PCI_BUF_SIZE 60000 +#define VIA_FIRE_BUF_SIZE 1024 + +#define VIA_FENCE_OFFSET_CMD 0x000 + +#define VIA_IDLE_TIMEOUT (3*HZ) + +/* + * Extension offsets. + */ + +#define DRM_VIA_DEC_FUTEX 0x03 +#define DRM_VIA_TTM_EXECBUF 0x04 +#define DRM_VIA_PLACEMENT_OFFSET 0x10 +#define DRM_VIA_FENCE_OFFSET 0x18 + +enum via_barriers { + VIA_BARRIER_HQV0 = 0, + VIA_BARRIER_HQV1, + VIA_BARRIER_MPEG0, + VIA_BARRIER_MPEG1, + VIA_NUM_BARRIERS +}; + +struct via_fpriv { + struct ttm_object_file *tfile; +}; + +/* + * Context private stuff. Mainly buffers used + * for execbuf that we don't want to allocate + * for each call, and that may be used by + * a number of contexts at a time. + */ + +struct via_sarea { + struct drm_via_sarea sa; + struct drm_via_sarea_xvmc sa_xvmc; +}; + +struct drm_via_private { + struct drm_global_reference mem_global_ref; + struct drm_device *dev; + struct ttm_object_device *tdev; + struct ttm_fence_device fdev; + struct ttm_bo_device bdev; + struct ttm_lock ttm_lock; + struct via_sarea *sarea_priv; + drm_local_map_t *sarea; + unsigned long agpAddr; + wait_queue_head_t decoder_queue[DRM_VIA_NR_XVMC_LOCKS]; + char pci_buf[VIA_PCI_BUF_SIZE]; + char *dma_ptr; + unsigned int dma_low; + unsigned int dma_high; + unsigned int dma_offset; + uint32_t dma_wrap; + uint32_t dma_tracker; + uint32_t dma_free; + struct list_head dma_trackers; + uint32_t __iomem *last_pause_ptr; + uint32_t __iomem *hw_addr_ptr; + atomic_t vbl_received; + drm_via_state_t hc_state; + const uint32_t *fire_offsets[VIA_FIRE_BUF_SIZE]; + uint32_t num_fire_offsets; + int chipset; + uint32_t irq_enable_mask; + uint32_t irq_pending_mask; + uint32_t irq2_enable_mask; + uint32_t irq2_pending_mask; + spinlock_t irq_lock; + + /* Memory manager stuff */ + + unsigned long vram_offset; + unsigned long agp_offset; + struct drm_via_blitq blit_queues[VIA_NUM_BLIT_ENGINES]; + uint32_t dma_diff; + atomic_t fence_seq[VIA_NUM_ENGINES]; + uint64_t vram_size; /* kiB */ + uint64_t vram_start; + int vram_direct; + int vram_mtrr; + + uint64_t tt_size; /* bytes */ + uint64_t tt_start; + + struct ttm_buffer_object *vq_bo; + + struct ttm_buffer_object *fence_bo; + struct ttm_bo_kmap_obj fence_bmo; + uint32_t *fence_map; + + struct ttm_buffer_object *agpc_bo; + struct ttm_bo_kmap_obj agpc_bmo; + volatile uint32_t *agpc_map; + + /* + * Fixed memory region for transient buffer objects. + */ + + struct ttm_buffer_object *agp_bo; + struct mutex init_mutex; + u8 __iomem *mmio_map; + atomic_t val_seq; + struct mutex cmdbuf_mutex; + int has_irq; + + struct hrtimer fence_timer; + struct ttm_fence_object *barriers[VIA_NUM_BARRIERS]; + uint32_t max_validate_buffers; + + rwlock_t context_lock; + struct drm_open_hash context_hash; +}; + +struct via_cpriv { + struct drm_hash_item hash; + struct drm_via_private *dev_priv; + struct kref kref; + atomic_t in_execbuf; + void *reloc_buf; + struct via_validate_buffer *val_bufs; +}; + +enum via_family { + VIA_OTHER = 0, /* Baseline */ + VIA_PRO_GROUP_A, /* Another video engine and DMA commands */ + VIA_DX9_0 /* Same video as pro_group_a, but 3D is unsupported */ +}; + +/* VIA MMIO register access */ + +#define VIA_READ(reg) ioread32((u32 *)(dev_priv->mmio_map + (reg))) +#define VIA_WRITE(reg,val) iowrite32(val, (u32 *)(dev_priv->mmio_map + (reg))) +#define VIA_READ8(reg) ioread8(dev_priv->mmio_map + (reg)) +#define VIA_WRITE8(reg,val) iowrite8(val, dev_priv->mmio_map + (reg)) + +extern int drm_via_disable_verifier; +extern struct drm_fence_driver via_fence_driver; + +static inline struct drm_via_private *via_priv(struct drm_device *dev) +{ + return (struct drm_via_private *)dev->dev_private; +} + +extern int via_decoder_futex(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern int via_driver_load(struct drm_device *dev, unsigned long chipset); +extern int via_driver_unload(struct drm_device *dev); +extern int via_suspend(struct pci_dev *pdev, pm_message_t state); +extern int via_resume(struct pci_dev *pdev); + +extern u32 via_get_vblank_counter(struct drm_device *dev, int crtc); +extern int via_enable_vblank(struct drm_device *dev, int crtc); +extern void via_disable_vblank(struct drm_device *dev, int crtc); + +extern irqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS); +extern void via_driver_irq_preinstall(struct drm_device *dev); +extern int via_driver_irq_postinstall(struct drm_device *dev); +extern void via_driver_irq_uninstall(struct drm_device *dev); + +extern int via_dma_cleanup(struct drm_device *dev); +extern void via_init_command_verifier(void); +extern int via_driver_dma_quiescent(struct drm_device *dev); +extern void via_init_futex(struct drm_via_private *dev_priv); +extern void via_release_futex(struct drm_via_private *dev_priv, int context); +extern int via_firstopen(struct drm_device *dev); +extern void via_lastclose(struct drm_device *dev); + +extern int via_dmablit_bo(struct ttm_buffer_object *bo, + struct ttm_mem_reg *new_mem, + struct page **pages, + int *fence_class); + +extern struct ttm_backend *via_create_ttm_backend_entry(struct ttm_bo_device + *bdev); +extern int via_invalidate_caches(struct ttm_bo_device *bdev, + uint32_t buffer_flags); +extern int via_init_mem_type(struct ttm_bo_device *dev, uint32_t type, + struct ttm_mem_type_manager *man); +extern uint32_t via_evict_flags(struct ttm_buffer_object *bo); +extern int via_bo_move(struct ttm_buffer_object *bo, int evict, + int interruptible, int no_wait, struct ttm_mem_reg *new_mem); +extern void via_dma_initialize(struct drm_via_private *dev_priv); +extern void via_dma_takedown(struct drm_via_private *dev_priv); +extern void via_wait_idle(struct drm_via_private *dev_priv); +int via_copy_cmdbuf(struct drm_via_private *dev_priv, + uint64_t cmd_buffer, + uint32_t size, + uint32_t mechanism, + uint32_t ** cmdbuf_addr, int *is_iomem); + +extern int via_vt_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern int via_execbuffer(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern int via_dispatch_commands(struct drm_device *dev, + unsigned long size, uint32_t mechanism); +extern void via_ttm_signal_fences(struct drm_via_private *dev_priv); +extern void via_ttm_fence_cmd_handler(struct drm_via_private *dev_priv, uint32_t signal_types); +extern void via_ttm_fence_dmablit_handler(struct drm_via_private *dev_priv, int engine); +extern enum hrtimer_restart via_ttm_fence_timer_func(struct hrtimer *timer); +extern int via_ttm_fence_device_init(struct drm_via_private *dev_priv); + +extern int via_getparam_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern int via_extension_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); + +extern int via_release(struct inode *inode, struct file *filp); +extern int via_open(struct inode *inode, struct file *filp); + +extern int via_context_ctor(struct drm_device *dev, int context); +extern int via_context_dtor(struct drm_device *dev, int context); + +struct via_cpriv *via_context_lookup(struct drm_via_private *dev_priv, + int context); +extern void via_context_unref(struct via_cpriv **cpriv); + +static inline struct via_fpriv *via_fpriv(struct drm_file *file_priv) +{ + return (struct via_fpriv *) file_priv->driver_priv; +} + +extern int via_fence_signaled_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern int via_fence_finish_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern int via_fence_unref_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern int via_pl_waitidle_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern int via_pl_setstatus_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern int via_pl_synccpu_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern int via_pl_unref_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern int via_pl_reference_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern int via_pl_create_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern int via_mmap(struct file *filp, struct vm_area_struct *vma); +extern int via_verify_access(struct ttm_buffer_object *bo, + struct file *filp); +extern ssize_t via_ttm_read(struct file *filp, char __user *buf, + size_t count, loff_t *f_pos); +extern ssize_t via_ttm_write(struct file *filp, const char __user *buf, + size_t count, loff_t *f_pos); +extern int via_ttm_global_init(struct drm_via_private *dev_priv); +extern void via_ttm_global_release(struct drm_via_private *dev_priv); +#endif diff --git a/linux-core/openchrome/via_execbuf.c b/linux-core/openchrome/via_execbuf.c new file mode 100644 index 00000000..5bb758a3 --- /dev/null +++ b/linux-core/openchrome/via_execbuf.c @@ -0,0 +1,1045 @@ +/* + * Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas. + * 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, sub license, 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 (including the + * next paragraph) 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 NON-INFRINGEMENT. + * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS 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: + * Thomas Hellstrom + */ + +#include "drmP.h" +#include "drm.h" +#include "via_drv.h" +#include "via_3d_reg.h" +#include "ttm/ttm_bo_api.h" +#include "ttm/ttm_execbuf_util.h" +#include "ttm/ttm_userobj_api.h" + +struct via_validate_buffer { + struct ttm_validate_buffer base; + struct drm_via_validate_req req; + int ret; + struct drm_via_validate_arg __user *user_val_arg; + uint32_t flags; + uint32_t offset; + uint32_t fence_type; + int po_correct; +}; + +/* + * These must correspond to the enum via_barriers + * definition. + */ + +static uint32_t via_barrier_fence_type[] = { + VIA_FENCE_TYPE_HQV0, + VIA_FENCE_TYPE_HQV1, + VIA_FENCE_TYPE_MPEG0, + VIA_FENCE_TYPE_MPEG1 +}; + +static int +via_placement_fence_type(struct ttm_buffer_object *bo, + uint64_t set_val_flags, + uint64_t clr_val_flags, + uint32_t new_fence_class, uint32_t * new_fence_type) +{ + int ret; + uint32_t n_fence_type; + uint32_t set_flags = set_val_flags & VIA_PLACEMENT_MASK; + uint32_t clr_flags = clr_val_flags & VIA_PLACEMENT_MASK; + struct ttm_fence_object *old_fence; + uint32_t old_fence_types; + + ret = ttm_bo_check_placement(bo, set_flags, clr_flags); + if (unlikely(ret != 0)) + return ret; + + switch (new_fence_class) { + case VIA_ENGINE_CMD: + n_fence_type = TTM_FENCE_TYPE_EXE; + if (set_val_flags & VIA_VAL_FLAG_HQV0) + n_fence_type |= VIA_FENCE_TYPE_HQV0; + if (set_val_flags & VIA_VAL_FLAG_HQV1) + n_fence_type |= VIA_FENCE_TYPE_HQV1; + if (set_val_flags & VIA_VAL_FLAG_MPEG0) + n_fence_type |= VIA_FENCE_TYPE_MPEG0; + if (set_val_flags & VIA_VAL_FLAG_MPEG1) + n_fence_type |= VIA_FENCE_TYPE_MPEG1; + break; + default: + n_fence_type = TTM_FENCE_TYPE_EXE; +#if 0 + /* + * FIXME + */ + if (bo->mem.proposed_flags & TTM_PL_FLAG_SYSTEM) + n_fence_type |= VIA_FENCE_TYPE_SYSMEM; +#endif + break; + } + + *new_fence_type = n_fence_type; + old_fence = (struct ttm_fence_object *)bo->sync_obj; + old_fence_types = (uint32_t) (unsigned long)bo->sync_obj_arg; + + if (old_fence && ((new_fence_class != old_fence->fence_class) || + ((n_fence_type ^ old_fence_types) & + old_fence_types))) { + ret = ttm_bo_wait(bo, 0, 0, 0); + if (unlikely(ret != 0)) + return ret; + } + + bo->proposed_flags = (bo->proposed_flags | set_flags) + & ~clr_flags & TTM_PL_MASK_MEMTYPE; + + return 0; +} + +static int +via_apply_texture_reloc(uint32_t ** cmdbuf, + uint32_t num_buffers, + struct via_validate_buffer *buffers, + const struct drm_via_texture_reloc *reloc) +{ + const struct drm_via_reloc_bufaddr *baddr = reloc->addr; + uint32_t baseh[4]; + uint32_t *buf = *cmdbuf + reloc->base.offset; + uint32_t val; + int i; + int basereg; + int shift; + uint64_t flags = 0; + uint32_t reg_tex_fm; + + memset(baseh, 0, sizeof(baseh)); + + for (i = 0; i <= (reloc->hi_mip - reloc->low_mip); ++i) { + if (baddr->index > num_buffers) { + *cmdbuf = buf; + return -EINVAL; + } + + val = buffers[baddr->index].offset + baddr->delta; + if (i == 0) + flags = buffers[baddr->index].flags; + + *buf++ = ((HC_SubA_HTXnL0BasL + i) << 24) | (val & 0x00FFFFFF); + + basereg = i / 3; + shift = (3 - (i % 3)) << 3; + + baseh[basereg] |= (val & 0xFF000000) >> shift; + baddr++; + } + + if (reloc->low_mip < 3) + *buf++ = baseh[0] | (HC_SubA_HTXnL012BasH << 24); + if (reloc->low_mip < 6 && reloc->hi_mip > 2) + *buf++ = baseh[1] | (HC_SubA_HTXnL345BasH << 24); + if (reloc->low_mip < 9 && reloc->hi_mip > 5) + *buf++ = baseh[2] | (HC_SubA_HTXnL678BasH << 24); + if (reloc->hi_mip > 8) + *buf++ = baseh[3] | (HC_SubA_HTXnL9abBasH << 24); + + reg_tex_fm = reloc->reg_tex_fm & ~HC_HTXnLoc_MASK; + + if (flags & TTM_PL_FLAG_VRAM) { + reg_tex_fm |= HC_HTXnLoc_Local; + } else if (flags & (TTM_PL_FLAG_TT | TTM_PL_FLAG_PRIV0)) { + reg_tex_fm |= HC_HTXnLoc_AGP; + } else + BUG(); + + *buf++ = reg_tex_fm; + *cmdbuf = buf; + + return 0; +} + +static int +via_apply_zbuf_reloc(uint32_t ** cmdbuf, + uint32_t num_buffers, + struct via_validate_buffer *buffers, + const struct drm_via_zbuf_reloc *reloc) +{ + uint32_t *buf = *cmdbuf + reloc->base.offset; + const struct drm_via_reloc_bufaddr *baddr = &reloc->addr; + const struct via_validate_buffer *val_buf; + uint32_t val; + + if (baddr->index > num_buffers) + return -EINVAL; + + val_buf = &buffers[baddr->index]; + if (val_buf->po_correct) + return 0; + + val = val_buf->offset + baddr->delta; + *buf++ = (HC_SubA_HZWBBasL << 24) | (val & 0xFFFFFF); + *buf++ = (HC_SubA_HZWBBasH << 24) | ((val & 0xFF000000) >> 24); + + *cmdbuf = buf; + return 0; +} + +static int +via_apply_yuv_reloc(uint32_t ** cmdbuf, + uint32_t num_buffers, + struct via_validate_buffer *buffers, + const struct drm_via_yuv_reloc *reloc) +{ + uint32_t *buf = *cmdbuf + reloc->base.offset; + const struct drm_via_reloc_bufaddr *baddr = &reloc->addr; + const struct via_validate_buffer *val_buf; + uint32_t val; + int i; + + if (reloc->planes > 4) + return -EINVAL; + + if (baddr->index > num_buffers) + return -EINVAL; + + val_buf = &buffers[baddr->index]; + if (val_buf->po_correct) + return 0; + val = val_buf->offset + baddr->delta; + + for (i = 0; i < reloc->planes; ++i) { + *buf++ = (val + reloc->plane_offs[i]) >> reloc->shift; + ++buf; + } + + *cmdbuf = buf - 1; + return 0; +} + +static int +via_apply_dstbuf_reloc(uint32_t ** cmdbuf, + uint32_t num_buffers, + struct via_validate_buffer *buffers, + const struct drm_via_zbuf_reloc *reloc) +{ + uint32_t *buf = *cmdbuf + reloc->base.offset; + const struct drm_via_reloc_bufaddr *baddr = &reloc->addr; + const struct via_validate_buffer *val_buf; + uint32_t val; + + if (baddr->index > num_buffers) + return -EINVAL; + + val_buf = &buffers[baddr->index]; + if (0 && val_buf->po_correct) + return 0; + + val = val_buf->offset + baddr->delta; + *buf++ = (HC_SubA_HDBBasL << 24) | (val & 0xFFFFFF); + *buf++ = (HC_SubA_HDBBasH << 24) | ((val & 0xFF000000) >> 24); + + *cmdbuf = buf; + return 0; +} + +static int +via_apply_2d_reloc(uint32_t ** cmdbuf, + uint32_t num_buffers, + const struct via_validate_buffer *buffers, + const struct drm_via_2d_reloc *reloc) +{ + uint32_t *buf = *cmdbuf + reloc->base.offset; + const struct drm_via_reloc_bufaddr *baddr = &reloc->addr; + const struct via_validate_buffer *val_buf; + uint32_t val; + uint32_t x; + + if (baddr->index > num_buffers) + return -EINVAL; + + val_buf = &buffers[baddr->index]; + if (val_buf->po_correct) + return 0; + + val = val_buf->base.bo->offset + baddr->delta; + x = val & 0x1f; + + if (reloc->bpp == 32) + x >>= 2; + else if (reloc->bpp == 16) + x >>= 1; + + *buf = (val & ~0x1f) >> 3; + buf += 2; + *buf++ = reloc->pos + x; + + *cmdbuf = buf; + return 0; +} + +int via_apply_reloc(struct drm_via_private *dev_priv, + void **reloc_buf, + struct via_validate_buffer *bufs, + uint32_t num_validate_buffers, uint32_t * cmdbuf) +{ + const struct drm_via_base_reloc *reloc = + (const struct drm_via_base_reloc *)*reloc_buf; + size_t size; + int ret; + + switch (reloc->type) { + case VIA_RELOC_TEX: + + { + const struct drm_via_texture_reloc *tex_reloc = + (const struct drm_via_texture_reloc *)reloc; + + ret = + via_apply_texture_reloc(&cmdbuf, + num_validate_buffers, bufs, + (const struct + drm_via_texture_reloc *) + reloc); + + size = offsetof(struct drm_via_texture_reloc, addr) - + offsetof(struct drm_via_texture_reloc, base) + + (tex_reloc->hi_mip - tex_reloc->low_mip + 1) * + sizeof(struct drm_via_reloc_bufaddr); + } + break; + + case VIA_RELOC_ZBUF: + ret = via_apply_zbuf_reloc(&cmdbuf, num_validate_buffers, bufs, + (const struct drm_via_zbuf_reloc *) + reloc); + size = sizeof(struct drm_via_zbuf_reloc); + break; + + case VIA_RELOC_DSTBUF: + ret = + via_apply_dstbuf_reloc(&cmdbuf, num_validate_buffers, bufs, + (const struct drm_via_zbuf_reloc *) + reloc); + size = sizeof(struct drm_via_zbuf_reloc); + break; + + case VIA_RELOC_2D: + ret = via_apply_2d_reloc(&cmdbuf, num_validate_buffers, bufs, + (const struct drm_via_2d_reloc *) + reloc); + size = sizeof(struct drm_via_2d_reloc); + break; + + case VIA_RELOC_YUV: + ret = via_apply_yuv_reloc(&cmdbuf, num_validate_buffers, bufs, + (const struct drm_via_yuv_reloc *) + reloc); + size = sizeof(struct drm_via_yuv_reloc); + break; + default: + DRM_ERROR("Illegal relocation type %d\n", reloc->type); + ret = -EINVAL; + break; + } + + if (ret) + return ret; + + *reloc_buf = (void *)((char *)*reloc_buf + size); + return 0; +} + +static int +via_apply_reloc_chain(struct drm_via_private *dev_priv, + uint64_t data, + uint32_t * cmdbuf_addr, + int is_iomem, + struct via_cpriv *cpriv, uint32_t num_validate_buffers) +{ + struct drm_via_reloc_header __user *user_header = + (struct drm_via_reloc_header __user *)(unsigned long)data; + struct drm_via_reloc_header *header; + void *relocs; + int count = 0; + int i; + uint32_t size; + int ret; + + while (user_header != NULL) { + // DRM_INFO("Reloc header %d\n", count); + ret = get_user(size, &user_header->used); + if (unlikely(ret != 0)) + return ret; + + // DRM_INFO("Header size %d\n", size); + if (unlikely(size > VIA_RELOC_BUF_SIZE)) { + DRM_ERROR("Illegal relocation buffer size.\n"); + return -EINVAL; + } + + ret = copy_from_user(cpriv->reloc_buf, user_header, size); + if (unlikely(ret != 0)) + return ret; + + header = (struct drm_via_reloc_header *)cpriv->reloc_buf; + relocs = (void *)((unsigned long)header + sizeof(*header)); + + for (i = 0; i < header->num_relocs; ++i) { + // DRM_INFO("Reloc %d\n", i); + ret = + via_apply_reloc(dev_priv, &relocs, cpriv->val_bufs, + num_validate_buffers, cmdbuf_addr); + if (ret) + return ret; + } + + user_header = + (struct drm_via_reloc_header __user *)(unsigned long) + header->next_header; + count++; + } + + return 0; +} + +static int via_check_presumed(struct drm_via_validate_req *req, + struct ttm_buffer_object *bo, + struct drm_via_validate_arg __user * data, + int *presumed_ok) +{ + struct drm_via_validate_req __user *user_req = &(data->d.req); + + *presumed_ok = 0; + if (unlikely(!(req->presumed_flags & VIA_USE_PRESUMED))) + return 0; + + if (bo->mem.mem_type == TTM_PL_SYSTEM) { + *presumed_ok = 1; + return 0; + } + + if (bo->offset == req->presumed_gpu_offset && + !((req->presumed_flags & VIA_PRESUMED_AGP) && + !(bo->mem.flags & (TTM_PL_FLAG_PRIV0 | TTM_PL_FLAG_TT)))) { + *presumed_ok = 1; + return 0; + } + + return __put_user(req->presumed_flags & ~VIA_USE_PRESUMED, + &user_req->presumed_flags); +} + +static int via_lookup_validate_buffer(struct drm_file *file_priv, + uint64_t data, + struct via_validate_buffer *item) +{ + struct ttm_object_file *tfile = via_fpriv(file_priv)->tfile; + + item->user_val_arg = + (struct drm_via_validate_arg __user *)(unsigned long)data; + + if (copy_from_user(&item->req, &item->user_val_arg->d.req, + sizeof(item->req))) + return -EFAULT; + + item->base.bo = + ttm_buffer_object_lookup(tfile, item->req.buffer_handle); + if (item->base.bo == NULL) + return -EINVAL; + + return 0; +} + +static void via_unreference_buffers(struct list_head *list) +{ + struct ttm_validate_buffer *entry, *next; + struct via_validate_buffer *vbuf; + + list_for_each_entry_safe(entry, next, list, head) { + vbuf = container_of(entry, struct via_validate_buffer, base); + list_del(&entry->head); + ttm_bo_unref(&entry->bo); + } +} + +static int via_reference_buffers(struct drm_file *file_priv, + struct list_head *list, + struct via_cpriv *cpriv, + uint64_t data, uint32_t * num_buffers) +{ + struct drm_device *dev = file_priv->minor->dev; + struct drm_via_private *dev_priv = via_priv(dev); + struct via_validate_buffer *item; + uint32_t buf_count; + int ret; + + INIT_LIST_HEAD(list); + + if (unlikely(*num_buffers == 0)) { + if (data == 0ULL) + return 0; + else + goto out_err0; + } + + if (unlikely(*num_buffers > dev_priv->max_validate_buffers)) + goto out_err0; + + buf_count = 0; + while (likely(data != 0)) { + item = &cpriv->val_bufs[buf_count]; + + ret = via_lookup_validate_buffer(file_priv, data, item); + if (unlikely(ret != 0)) + goto out_err1; + + list_add_tail(&item->base.head, list); + ++buf_count; + data = item->req.next; + } + + *num_buffers = buf_count; + return 0; + + out_err1: + via_unreference_buffers(list); + return ret; + + out_err0: + DRM_ERROR("Too many validate buffers on validate list.\n"); + return -EINVAL; +} + +static int via_validate_buffer_list(struct drm_file *file_priv, + uint32_t fence_class, + struct list_head *list, + int *po_correct, + uint32_t * buffers_fence_types) +{ + struct via_validate_buffer *item; + struct ttm_buffer_object *bo; + int ret; + struct drm_via_validate_req *req; + uint32_t fence_types = 0; + uint32_t cur_fence_type; + struct ttm_validate_buffer *entry; + + *po_correct = 1; + + list_for_each_entry(entry, list, head) { + item = container_of(entry, struct via_validate_buffer, base); + bo = entry->bo; + item->ret = 0; + req = &item->req; + + mutex_lock(&bo->mutex); + ret = via_placement_fence_type(bo, + req->set_flags, + req->clear_flags, + fence_class, &cur_fence_type); + if (unlikely(ret != 0)) + goto out_err; + + ret = ttm_buffer_object_validate(bo, 1, 0); + + if (unlikely(ret != 0)) + goto out_err; + + fence_types |= cur_fence_type; + entry->new_sync_obj_arg = (void *)(unsigned long)cur_fence_type; + + item->offset = bo->offset; + item->flags = bo->mem.flags; + mutex_unlock(&bo->mutex); + + ret = via_check_presumed(&item->req, bo, item->user_val_arg, + &item->po_correct); + if (unlikely(ret != 0)) + goto out_err; + + if (unlikely(!item->po_correct)) + *po_correct = 0; + + item++; + } + + *buffers_fence_types = fence_types; + + return 0; + out_err: + mutex_unlock(&bo->mutex); + item->ret = ret; + return ret; +} + +static int via_handle_copyback(struct drm_device *dev, + struct list_head *list, int ret) +{ + int err = ret; + struct ttm_validate_buffer *entry; + struct drm_via_validate_arg arg; + + if (ret) + ttm_eu_backoff_reservation(list); + + if (ret != -ERESTART) { + list_for_each_entry(entry, list, head) { + struct via_validate_buffer *vbuf = + container_of(entry, struct via_validate_buffer, + base); + arg.handled = 1; + arg.ret = vbuf->ret; + if (!arg.ret) { + struct ttm_buffer_object *bo = entry->bo; + + mutex_lock(&bo->mutex); + arg.d.rep.gpu_offset = bo->offset; + arg.d.rep.placement = bo->mem.flags; + arg.d.rep.fence_type_mask = + (uint32_t) (unsigned long) + entry->new_sync_obj_arg; + mutex_unlock(&bo->mutex); + } + + if (__copy_to_user(vbuf->user_val_arg, + &arg, sizeof(arg))) + err = -EFAULT; + + if (arg.ret) + break; + } + } + + return err; +} + +/* + * Create a fence object, and if that fails, pretend that everything is + * OK and just idle the GPU. + */ + +static void via_fence_or_sync(struct drm_file *file_priv, + uint32_t fence_class, + uint32_t fence_types, + uint32_t fence_flags, + struct list_head *list, + struct drm_via_ttm_fence_rep *fence_arg, + struct ttm_fence_object **fence_p) +{ + struct drm_device *dev = file_priv->minor->dev; + struct drm_via_private *dev_priv = via_priv(dev); + struct ttm_fence_device *fdev = &dev_priv->fdev; + int ret; + struct ttm_fence_object *fence; + struct ttm_object_file *tfile = via_fpriv(file_priv)->tfile; + uint32_t handle; + + ret = ttm_fence_user_create(fdev, tfile, + fence_class, fence_types, + TTM_FENCE_FLAG_EMIT, &fence, &handle); + if (ret) { + + /* + * Fence creation failed. + * Fall back to synchronous operation and idle the engine. + */ + + (void)via_driver_dma_quiescent(dev); + if (!(fence_flags & DRM_VIA_FENCE_NO_USER)) { + + /* + * Communicate to user-space that + * fence creation has failed and that + * the engine is idle. + */ + + fence_arg->handle = ~0; + fence_arg->error = ret; + } + + ttm_eu_backoff_reservation(list); + if (fence_p) + *fence_p = NULL; + return; + } + + ttm_eu_fence_buffer_objects(list, (void *)fence); + if (!(fence_flags & DRM_VIA_FENCE_NO_USER)) { + struct ttm_fence_info info = ttm_fence_get_info(fence); + fence_arg->handle = handle; + fence_arg->fence_class = fence->fence_class; + fence_arg->fence_type = fence->fence_type; + fence_arg->signaled_types = info.signaled_types; + fence_arg->error = 0; + } else { + ttm_ref_object_base_unref(tfile, handle, ttm_fence_type); + } + + if (fence_p) + *fence_p = fence; + else if (fence) + ttm_fence_object_unref(&fence); +} + +static int via_apply_cliprect(uint32_t * cmdbuf, uint32_t offset, uint64_t clip) +{ + struct drm_via_clip_rect clip_rect; + void __user *user_clip = (void *)(unsigned long)clip; + int ret; + + ret = copy_from_user(&clip_rect, user_clip, sizeof(clip_rect)); + if (unlikely(ret)) + return ret; + + cmdbuf += offset; + cmdbuf[0] = (HC_SubA_HClipTB << 24) | + ((clip_rect.y1) << 12) | (clip_rect.y2); + cmdbuf[1] = (HC_SubA_HClipLR << 24) | + ((clip_rect.x1) << 12) | (clip_rect.x2); + + return 0; +} + +static int via_dispatch_clip(struct drm_device *dev, + struct drm_via_ttm_execbuf_arg *exec_buf, + struct drm_via_ttm_execbuf_control *control, + int po_correct, + struct via_cpriv *cpriv, + uint32_t num_buffers, + uint32_t * cmdbuf_addr, int cmdbuf_iomem) +{ + struct drm_via_private *dev_priv = via_priv(dev); + uint32_t num_clip; + uint32_t first_clip; + uint32_t i; + uint64_t cliprect_addr = 0; + int ret = 0; + + if (exec_buf->exec_flags & DRM_VIA_HAVE_CLIP) { + first_clip = control->first_clip; + cliprect_addr = exec_buf->cliprect_addr + + first_clip * sizeof(struct drm_via_clip_rect); + num_clip = exec_buf->num_cliprects; + } else { + control->first_clip = 0; + first_clip = 0; + num_clip = 1; + } + + for (i = first_clip; i < num_clip; ++i) { + if (i > first_clip && drm_via_disable_verifier && + exec_buf->mechanism == _VIA_MECHANISM_AGP) { + ret = via_copy_cmdbuf(dev_priv, exec_buf->cmd_buffer, + exec_buf->cmd_buffer_size, + exec_buf->mechanism, + &cmdbuf_addr, &cmdbuf_iomem); + + if (unlikely(ret != 0)) + goto out; + + if (unlikely(!po_correct)) { + ret = via_apply_reloc_chain(dev_priv, + exec_buf-> + reloc_list, + cmdbuf_addr, + cmdbuf_iomem, cpriv, + num_buffers); + if (unlikely(ret != 0)) + goto out; + } + } + + if (i == 0 && (!drm_via_disable_verifier || + exec_buf->mechanism != _VIA_MECHANISM_AGP)) { + + ret = via_verify_command_stream(cmdbuf_addr, + exec_buf-> + cmd_buffer_size, dev, + exec_buf->mechanism == + _VIA_MECHANISM_AGP); + if (unlikely(ret != 0)) { + DRM_ERROR("Command verifier error\n"); + goto out; + } + } + + if (exec_buf->exec_flags & DRM_VIA_HAVE_CLIP) { + ret = via_apply_cliprect(cmdbuf_addr, + exec_buf->cliprect_offset, + cliprect_addr); + cliprect_addr += sizeof(struct drm_via_clip_rect); + } + + ret = via_dispatch_commands(dev, exec_buf->cmd_buffer_size, + exec_buf->mechanism); + + if (unlikely(ret != 0)) + goto out; + + ++control->first_clip; + } + + out: + return ret; +} + +static int via_wait_single_barrier(struct drm_via_private *dev_priv, + uint32_t fence_type, + uint32_t barrier_type, + enum via_barriers barrier) +{ + int ret; + + if (likely(!(fence_type & barrier_type))) + return 0; + if (likely(dev_priv->barriers[barrier] == NULL)) + return 0; + + ret = ttm_fence_object_wait(dev_priv->barriers[barrier], 0, 1, + barrier_type); + if (unlikely(ret != 0)) + return ret; + + ttm_fence_object_unref(&dev_priv->barriers[barrier]); + return 0; +} + +static int via_wait_barriers(struct drm_via_private *dev_priv, + uint32_t fence_type) +{ + int ret; + int i; + + if (likely(fence_type == TTM_FENCE_TYPE_EXE)) + return 0; + + for (i = 0; i < VIA_NUM_BARRIERS; ++i) { + ret = via_wait_single_barrier(dev_priv, fence_type, + via_barrier_fence_type[i], + VIA_BARRIER_HQV0 + i); + if (unlikely(ret != 0)) + return ret; + } + return 0; +} + +static void via_update_single_barrier(struct drm_via_private *dev_priv, + uint32_t barrier_type, + enum via_barriers barrier, + struct ttm_fence_object *fence) +{ + if (likely(!(fence->fence_type & barrier_type))) + return; + + if (dev_priv->barriers[barrier] != NULL) + ttm_fence_object_unref(&dev_priv->barriers[barrier]); + + dev_priv->barriers[barrier] = ttm_fence_object_ref(fence); +} + +static void via_update_barriers(struct drm_via_private *dev_priv, + struct ttm_fence_object *fence) +{ + int i; + + if (likely(fence->fence_type == TTM_FENCE_TYPE_EXE)) + return; + + for (i = 0; i < VIA_NUM_BARRIERS; ++i) { + via_update_single_barrier(dev_priv, + via_barrier_fence_type[i], + VIA_BARRIER_HQV0 + i, fence); + } +} + +int via_execbuffer(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_via_private *dev_priv = via_priv(dev); + struct drm_via_ttm_execbuf_arg *exec_buf = + (struct drm_via_ttm_execbuf_arg *)data; + struct ttm_fence_object *barrier_fence; + int num_buffers; + int ret; + int po_correct; + uint32_t *cmdbuf_addr; + int cmdbuf_iomem; + uint32_t buffers_fence_types; + struct list_head validate_list; + uint32_t val_seq; + struct via_cpriv *cpriv = + via_context_lookup(dev_priv, exec_buf->context); + struct drm_via_ttm_execbuf_control control; + struct drm_via_ttm_execbuf_control __user *user_control; + struct drm_via_ttm_fence_rep *fence_rep = &control.rep; + + if (unlikely(cpriv == NULL)) { + DRM_ERROR("Illegal DRM context: %d.\n", exec_buf->context); + return -EINVAL; + } + + if (unlikely(!atomic_inc_and_test(&cpriv->in_execbuf))) { + DRM_ERROR("Multiple thread simultaneous use of DRM " + "context: %d.\n", exec_buf->context); + ret = -EINVAL; + goto out_err_ctx1; + } + + if (unlikely(exec_buf->num_buffers > dev_priv->max_validate_buffers)) { + DRM_ERROR("Too many buffers on validate list: %d\n", + exec_buf->num_buffers); + ret = -EINVAL; + goto out_err_ctx1; + } + + user_control = (struct drm_via_ttm_execbuf_control __user *) + (unsigned long)exec_buf->control; + + if (unlikely(!access_ok(VERIFY_WRITE, user_control, + sizeof(*user_control)))) { + DRM_ERROR("Invalid execbuf control block address.\n"); + ret = -EFAULT; + goto out_err_ctx1; + } + + ret = __get_user(control.first_clip, &user_control->first_clip); + if (unlikely(ret != 0)) + goto out_err_ctx1; + + /* + * The tlock makes it possible for a root process to block + * all command submission by taking this lock in write mode. + * Used to block command submission on vt switches. + */ + + ret = ttm_read_lock(&dev_priv->ttm_lock, 1); + if (unlikely(ret != 0)) + goto out_err_ctx1; + + if (unlikely(cpriv->val_bufs == NULL)) { + cpriv->val_bufs = vmalloc(sizeof(struct via_validate_buffer) * + dev_priv->max_validate_buffers); + if (unlikely(cpriv->val_bufs == NULL)) { + DRM_ERROR("Failed allocating memory for " + "validate list.\n"); + ret = -ENOMEM; + goto out_err_ctx1; + } + } + + num_buffers = exec_buf->num_buffers; + ret = via_reference_buffers(file_priv, + &validate_list, + cpriv, exec_buf->buffer_list, &num_buffers); + + if (unlikely(ret != 0)) + goto out_err0; + + val_seq = atomic_add_return(1, &dev_priv->val_seq); + + ret = ttm_eu_reserve_buffers(&validate_list, val_seq); + + if (unlikely(ret != 0)) + goto out_err1; + + ret = via_validate_buffer_list(file_priv, VIA_ENGINE_CMD, + &validate_list, + &po_correct, &buffers_fence_types); + if (unlikely(ret != 0)) + goto out_err2; + + /* + * The cmdbuf_mutex protects the AGP ring buffer and the + * dev_priv->pci_buf command cache. Note that also the + * command verifier that operates on this command cache + * maintains a single simulated hardware state, so it cannot + * operate in parallel on different buffers. + */ + + ret = mutex_lock_interruptible(&dev_priv->cmdbuf_mutex); + if (unlikely(ret != 0)) { + ret = -ERESTART; + goto out_err2; + } + + ret = via_copy_cmdbuf(dev_priv, exec_buf->cmd_buffer, + exec_buf->cmd_buffer_size, + exec_buf->mechanism, &cmdbuf_addr, &cmdbuf_iomem); + + if (unlikely(ret != 0)) + goto out_err3; + + if (unlikely(!po_correct)) { + ret = via_apply_reloc_chain(dev_priv, exec_buf->reloc_list, + cmdbuf_addr, cmdbuf_iomem, + cpriv, num_buffers); + if (unlikely(ret != 0)) + goto out_err3; + } + + if (exec_buf->exec_flags & DRM_VIA_WAIT_BARRIER) { + ret = via_wait_barriers(dev_priv, buffers_fence_types); + if (unlikely(ret != 0)) + goto out_err3; + } + + ret = via_dispatch_clip(dev, exec_buf, &control, po_correct, cpriv, + num_buffers, cmdbuf_addr, cmdbuf_iomem); + + if (likely(ret == 0 || control.first_clip != 0)) { + via_fence_or_sync(file_priv, VIA_ENGINE_CMD, + buffers_fence_types, + exec_buf->exec_flags, + &validate_list, fence_rep, &barrier_fence); + if (likely(barrier_fence)) { + via_update_barriers(dev_priv, barrier_fence); + ttm_fence_object_unref(&barrier_fence); + } + if (__copy_to_user(user_control, &control, sizeof(control))) { + + /* + * User-space can't unref the fence object so do it here. + */ + + if (likely(fence_rep->handle != ~0)) + ttm_ref_object_base_unref(via_fpriv(file_priv)-> + tfile, + fence_rep->handle, + ttm_fence_type); + ret = -EFAULT; + (void)via_driver_dma_quiescent(dev); + } + } + out_err3: + mutex_unlock(&dev_priv->cmdbuf_mutex); + out_err2: + ret = via_handle_copyback(dev, &validate_list, ret); + out_err1: + via_unreference_buffers(&validate_list); + out_err0: + ttm_read_unlock(&dev_priv->ttm_lock); + out_err_ctx1: + atomic_dec(&cpriv->in_execbuf); + via_context_unref(&cpriv); + + return ret; +} diff --git a/linux-core/openchrome/via_init.c b/linux-core/openchrome/via_init.c new file mode 100644 index 00000000..c161935a --- /dev/null +++ b/linux-core/openchrome/via_init.c @@ -0,0 +1,1049 @@ +/************************************************************************** + * + * Copyright (c) 2008 Tungsten Graphics, Inc., Cedar Park, TX., USA + * All Rights Reserved. + * Copyright (c) 2009 VMware, Inc., Palo Alto, CA., USA, + * 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, sub license, 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 (including the + * next paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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: Thomas Hellstrom + */ + +#include "drmP.h" +#include "ochr_drm.h" +#include "via_drv.h" +#include "ttm/ttm_object.h" +#include "ttm/ttm_pat_compat.h" +#include "ttm/ttm_lock.h" + +#define VIA_AGP_MODE_MASK 0x17 +#define VIA_AGPV3_MODE 0x08 +#define VIA_AGPV3_8X_MODE 0x02 +#define VIA_AGPV3_4X_MODE 0x01 +#define VIA_AGP_4X_MODE 0x04 +#define VIA_AGP_2X_MODE 0x02 +#define VIA_AGP_1X_MODE 0x01 +#define VIA_AGP_FW_MODE 0x10 + +#define DEV_AGP_OFFSET (0*1024*1024) +#define DEV_VRAM_OFFSET (0*1024*1024) + +#define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT) + +/** + * If there's no thrashing. This is the preferred memory type order. + */ +static uint32_t via_mem_prios[] = + { TTM_PL_PRIV0, TTM_PL_VRAM, TTM_PL_TT, TTM_PL_SYSTEM }; + +/** + * If we have thrashing, most memory will be evicted to TT anyway, so we might as well + * just move the new buffer into TT from the start. + */ +static uint32_t via_busy_prios[] = + { TTM_PL_TT, TTM_PL_PRIV0, TTM_PL_VRAM, TTM_PL_SYSTEM }; + +static struct ttm_bo_driver via_bo_driver = { + .mem_type_prio = via_mem_prios, + .mem_busy_prio = via_busy_prios, + .num_mem_type_prio = ARRAY_SIZE(via_mem_prios), + .num_mem_busy_prio = ARRAY_SIZE(via_busy_prios), + .create_ttm_backend_entry = via_create_ttm_backend_entry, + .invalidate_caches = via_invalidate_caches, + .init_mem_type = via_init_mem_type, + .evict_flags = via_evict_flags, + .move = via_bo_move, + .verify_access = via_verify_access, + .sync_obj_signaled = ttm_fence_sync_obj_signaled, + .sync_obj_wait = ttm_fence_sync_obj_wait, + .sync_obj_flush = ttm_fence_sync_obj_flush, + .sync_obj_unref = ttm_fence_sync_obj_unref, + .sync_obj_ref = ttm_fence_sync_obj_ref, +}; + +/* + * From VIA's code. + */ +#if 0 +static void via_pcie_engine_init(struct drm_via_private *dev_priv) +{ + VIA_WRITE(0x41c, 0x00100000); + VIA_WRITE(0x420, 0x680A0000); + VIA_WRITE(0x420, 0x02000000); +} +#endif + +static void via_irq_init(struct drm_device *dev) +{ + if (dev->pdev->device == 0x3108 || dev->pdev->device == 0x7205) { + return; + } else { + (void)drm_irq_install(dev); + } +} + +static void via_agp_engine_init(struct drm_via_private *dev_priv) +{ + VIA_WRITE(VIA_REG_TRANSET, 0x00100000); + VIA_WRITE(VIA_REG_TRANSPACE, 0x00000000); + VIA_WRITE(VIA_REG_TRANSPACE, 0x00333004); + VIA_WRITE(VIA_REG_TRANSPACE, 0x60000000); + VIA_WRITE(VIA_REG_TRANSPACE, 0x61000000); + VIA_WRITE(VIA_REG_TRANSPACE, 0x62000000); + VIA_WRITE(VIA_REG_TRANSPACE, 0x63000000); + VIA_WRITE(VIA_REG_TRANSPACE, 0x64000000); + VIA_WRITE(VIA_REG_TRANSPACE, 0x7D000000); + + VIA_WRITE(VIA_REG_TRANSET, 0xfe020000); + VIA_WRITE(VIA_REG_TRANSPACE, 0x00000000); +} + +static void via_2d_engine_init(struct drm_via_private *dev_priv) +{ + int i; + + for (i = 0x04; i < 0x44; i += 4) { + VIA_WRITE(i, 0x0); + } +} + +static void via_soft_reset(struct drm_via_private *dev_priv) +{ +#if 0 + u8 tmp; + VIA_WRITE8(0x83c4, 0x1A); + tmp = VIA_READ8(0x83c5); + tmp |= 0x06; + VIA_WRITE8(0x83c5, tmp); + tmp = VIA_READ8(0x83c5); +#endif +} + +static void via_3d_engine_init(struct drm_via_private *dev_priv) +{ + int i; + + /* + * Most of these register values are taken from VIA's + * X server code. + */ + + VIA_WRITE(VIA_REG_TRANSET, 0x00010000); + for (i = 0; i <= 0x7D; i++) + VIA_WRITE(VIA_REG_TRANSPACE, (uint32_t) i << 24); + + VIA_WRITE(VIA_REG_TRANSET, 0x00020000); + for (i = 0; i <= 0x94; i++) + VIA_WRITE(VIA_REG_TRANSPACE, (uint32_t) i << 24); + VIA_WRITE(VIA_REG_TRANSPACE, 0x82400000); + + VIA_WRITE(VIA_REG_TRANSET, 0x01020000); + for (i = 0; i <= 0x94; i++) + VIA_WRITE(VIA_REG_TRANSPACE, (uint32_t) i << 24); + VIA_WRITE(VIA_REG_TRANSPACE, 0x82400000); + + VIA_WRITE(VIA_REG_TRANSET, 0xfe020000); + for (i = 0; i <= 0x03; i++) + VIA_WRITE(VIA_REG_TRANSPACE, (uint32_t) i << 24); + + VIA_WRITE(VIA_REG_TRANSET, 0x00030000); + for (i = 0; i <= 0xff; i++) + VIA_WRITE(VIA_REG_TRANSPACE, 0); + + VIA_WRITE(VIA_REG_TRANSET, 0x00100000); + VIA_WRITE(VIA_REG_TRANSPACE, 0x00333004); + VIA_WRITE(VIA_REG_TRANSPACE, 0x10000002); + VIA_WRITE(VIA_REG_TRANSPACE, 0x60000000); + VIA_WRITE(VIA_REG_TRANSPACE, 0x61000000); + VIA_WRITE(VIA_REG_TRANSPACE, 0x62000000); + VIA_WRITE(VIA_REG_TRANSPACE, 0x63000000); + VIA_WRITE(VIA_REG_TRANSPACE, 0x64000000); + + VIA_WRITE(VIA_REG_TRANSET, 0x00fe0000); +#if 0 + if (pVia->Chipset == VIA_CLE266 && pVia->ChipRev >= 3) + VIA_WRITE(VIA_REG_TRANSPACE, 0x40008c0f); + else +#endif + VIA_WRITE(VIA_REG_TRANSPACE, 0x4000800f); + VIA_WRITE(VIA_REG_TRANSPACE, 0x44000000); + VIA_WRITE(VIA_REG_TRANSPACE, 0x45080C04); + VIA_WRITE(VIA_REG_TRANSPACE, 0x46800408); + VIA_WRITE(VIA_REG_TRANSPACE, 0x50000000); + VIA_WRITE(VIA_REG_TRANSPACE, 0x51000000); + VIA_WRITE(VIA_REG_TRANSPACE, 0x52000000); + VIA_WRITE(VIA_REG_TRANSPACE, 0x53000000); + + VIA_WRITE(VIA_REG_TRANSET, 0x00fe0000); + VIA_WRITE(VIA_REG_TRANSPACE, 0x08000001); + VIA_WRITE(VIA_REG_TRANSPACE, 0x0A000183); + VIA_WRITE(VIA_REG_TRANSPACE, 0x0B00019F); + VIA_WRITE(VIA_REG_TRANSPACE, 0x0C00018B); + VIA_WRITE(VIA_REG_TRANSPACE, 0x0D00019B); + VIA_WRITE(VIA_REG_TRANSPACE, 0x0E000000); + VIA_WRITE(VIA_REG_TRANSPACE, 0x0F000000); + VIA_WRITE(VIA_REG_TRANSPACE, 0x10000000); + VIA_WRITE(VIA_REG_TRANSPACE, 0x11000000); + VIA_WRITE(VIA_REG_TRANSPACE, 0x20000000); +} + +static void via_vq_disable(struct drm_via_private *dev_priv) +{ + VIA_WRITE(VIA_REG_TRANSET, 0x00fe0000); + VIA_WRITE(VIA_REG_TRANSPACE, 0x00000004); + VIA_WRITE(VIA_REG_TRANSPACE, 0x40008c0f); + VIA_WRITE(VIA_REG_TRANSPACE, 0x44000000); + VIA_WRITE(VIA_REG_TRANSPACE, 0x45080c04); +} + +static void via_vq_enable(struct drm_via_private *dev_priv) +{ + uint32_t vq_start_offs = dev_priv->vq_bo->offset; + uint32_t vq_end_offs = vq_start_offs + VIA_VQ_SIZE - 1; + + VIA_WRITE(VIA_REG_TRANSET, 0x00fe0000); + VIA_WRITE(VIA_REG_TRANSPACE, 0x080003fe); + VIA_WRITE(VIA_REG_TRANSPACE, 0x0a00027c); + VIA_WRITE(VIA_REG_TRANSPACE, 0x0b000260); + VIA_WRITE(VIA_REG_TRANSPACE, 0x0c000274); + VIA_WRITE(VIA_REG_TRANSPACE, 0x0d000264); + VIA_WRITE(VIA_REG_TRANSPACE, 0x0e000000); + VIA_WRITE(VIA_REG_TRANSPACE, 0x0f000020); + VIA_WRITE(VIA_REG_TRANSPACE, 0x1000027e); + VIA_WRITE(VIA_REG_TRANSPACE, 0x110002fe); + VIA_WRITE(VIA_REG_TRANSPACE, 0x200f0060); + VIA_WRITE(VIA_REG_TRANSPACE, 0x00000006); + VIA_WRITE(VIA_REG_TRANSPACE, 0x40008c0f); + VIA_WRITE(VIA_REG_TRANSPACE, 0x44000000); + VIA_WRITE(VIA_REG_TRANSPACE, 0x45080c04); + VIA_WRITE(VIA_REG_TRANSPACE, 0x46800408); + + VIA_WRITE(VIA_REG_TRANSPACE, (0x52 << 24) | + ((vq_start_offs & 0xFF000000) >> 24) | + ((vq_end_offs & 0xFF000000) >> 16)); + VIA_WRITE(VIA_REG_TRANSPACE, (0x50 << 24) | + (vq_start_offs & 0x00FFFFFF)); + VIA_WRITE(VIA_REG_TRANSPACE, (0x51 << 24) | (vq_end_offs & 0x00FFFFFF)); + VIA_WRITE(VIA_REG_TRANSPACE, (0x53 << 24) | (VIA_VQ_SIZE >> 3)); +} + +static int via_exec_init(struct drm_device *dev) +{ + struct drm_via_private *dev_priv = via_priv(dev); + int ret; + int dummy; + + memset(dev_priv->barriers, 0, sizeof(dev_priv->barriers)); + + /* + * VRAM buffer for fence sequences. + */ + + ret = ttm_buffer_object_create(&dev_priv->bdev, PAGE_SIZE, + ttm_bo_type_kernel, + TTM_PL_FLAG_VRAM | + TTM_PL_FLAG_NO_EVICT, + 0, 0, 0, NULL, &dev_priv->fence_bo); + if (unlikely(ret)) + return ret; + + ret = ttm_bo_kmap(dev_priv->fence_bo, 0, dev_priv->fence_bo->num_pages, + &dev_priv->fence_bmo); + if (unlikely(ret)) + goto out_err0; + + dev_priv->fence_map = + ttm_kmap_obj_virtual(&dev_priv->fence_bmo, &dummy); + list_del_init(&dev_priv->fence_bo->lru); + + /* + * VRAM buffer for the virtual command queue, which corresponds + * to engine (fence_class) 2. + */ + + ret = ttm_buffer_object_create(&dev_priv->bdev, VIA_VQ_SIZE, + ttm_bo_type_kernel, + TTM_PL_FLAG_VRAM | + TTM_PL_FLAG_NO_EVICT, + 0, 0, 0, NULL, &dev_priv->vq_bo); + if (unlikely(ret)) + goto out_err1; + + list_del_init(&dev_priv->vq_bo->lru); + + /* + * AGP DMA command buffer. + */ + + ret = ttm_buffer_object_create(&dev_priv->bdev, VIA_AGPC_SIZE, + ttm_bo_type_kernel, + TTM_PL_FLAG_TT | + TTM_PL_FLAG_NO_EVICT, + 0, 0, 0, NULL, &dev_priv->agpc_bo); + if (unlikely(ret)) + goto out_err2; + + ret = ttm_bo_kmap(dev_priv->agpc_bo, 0, dev_priv->agpc_bo->num_pages, + &dev_priv->agpc_bmo); + + if (unlikely(ret)) + goto out_err3; + + list_del_init(&dev_priv->agpc_bo->lru); + + dev_priv->agpc_map = ttm_kmap_obj_virtual(&dev_priv->agpc_bmo, &dummy); + + via_soft_reset(dev_priv); + via_3d_engine_init(dev_priv); + via_2d_engine_init(dev_priv); + via_agp_engine_init(dev_priv); + via_vq_enable(dev_priv); + via_dma_initialize(dev_priv); + + iowrite32(atomic_read(&dev_priv->fence_seq[VIA_ENGINE_CMD]), + dev_priv->fence_map + (VIA_FENCE_OFFSET_CMD >> 2)); + + return 0; + + out_err3: + ttm_bo_unref(&dev_priv->agpc_bo); + out_err2: + ttm_bo_unref(&dev_priv->vq_bo); + out_err1: + dev_priv->fence_map = NULL; + ttm_bo_kunmap(&dev_priv->fence_bmo); + out_err0: + ttm_bo_unref(&dev_priv->fence_bo); + + return ret; +} + +void via_exec_takedown(struct drm_via_private *dev_priv) +{ + int i; + + for (i = 0; i < VIA_NUM_BARRIERS; ++i) { + if (dev_priv->barriers[i]) + ttm_fence_object_unref(&dev_priv->barriers[i]); + } + + via_dma_takedown(dev_priv); + via_vq_disable(dev_priv); + dev_priv->agpc_map = NULL; + ttm_bo_kunmap(&dev_priv->agpc_bmo); + + BUG_ON(!list_empty(&dev_priv->agpc_bo->lru)); + BUG_ON(dev_priv->agpc_bo->sync_obj != NULL); + ttm_bo_unref(&dev_priv->agpc_bo); + + BUG_ON(!list_empty(&dev_priv->vq_bo->lru)); + BUG_ON(dev_priv->vq_bo->sync_obj != NULL); + ttm_bo_unref(&dev_priv->vq_bo); + + /* + * Unmap and free the fence sequence buffer object. + */ + + dev_priv->fence_map = NULL; + ttm_bo_kunmap(&dev_priv->fence_bmo); + + BUG_ON(!list_empty(&dev_priv->fence_bo->lru)); + BUG_ON(dev_priv->fence_bo->sync_obj != NULL); + ttm_bo_unref(&dev_priv->fence_bo); +} + +/* + * Probe the RAM controller and PCI resources for VRAM start and size. + */ + +static int via_detect_vram(struct drm_device *dev) +{ + struct drm_via_private *dev_priv = via_priv(dev); + struct pci_dev *fn3 = NULL; + struct pci_dev *fn0 = NULL; + struct pci_bus *bus; + u8 size; + int ret; + + bus = pci_find_bus(0, 0); + if (bus == NULL) { + ret = -EINVAL; + goto out_err; + } + + fn0 = pci_get_slot(bus, PCI_DEVFN(0, 0)); + fn3 = pci_get_slot(bus, PCI_DEVFN(0, 3)); + + switch (dev->pdev->device) { + case 0x3122: + case 0x7205: + if (fn0 == NULL) { + ret = -EINVAL; + goto out_err; + } + ret = pci_read_config_byte(fn0, 0xE1, &size); + if (ret) + goto out_err; + dev_priv->vram_size = (1 << ((size & 0x70) >> 4)) << 10; + break; + case 0x3118: + case 0x3344: + case 0x3108: + if (fn3 == NULL) { + ret = -EINVAL; + goto out_err; + } + ret = pci_read_config_byte(fn3, 0xA1, &size); + if (ret) + goto out_err; + dev_priv->vram_size = (1 << ((size & 0x70) >> 4)) << 10; + break; + case 0x3230: + case 0x3343: + case 0x3371: + case 0x3157: + if (fn3 == NULL) { + ret = -EINVAL; + goto out_err; + } + ret = pci_read_config_byte(fn3, 0xA1, &size); + if (ret) + goto out_err; + dev_priv->vram_size = (1 << ((size & 0x70) >> 4)) << 12; + break; + default: + DRM_ERROR("Unknown device 0x%04x. " + "Could not detect VRAM size.\n", dev->pdev->device); + ret = -EINVAL; + goto out_err; + } + + /* + * Detect VRAM start. + */ + + dev_priv->vram_direct = 0; + switch (fn3->device) { + case 0x3204: + pci_read_config_byte(fn3, 0x47, &size); + dev_priv->vram_start = size << 24; + dev_priv->vram_start -= dev_priv->vram_size * 1024; + dev_priv->vram_direct = 1; + break; + default: + dev_priv->vram_start = pci_resource_start(dev->pdev, 0); + break; + } + + if (dev_priv->vram_direct) + DRM_INFO("TTM is mapping Video RAM directly.\n"); + + if (fn0) + pci_dev_put(fn0); + if (fn3) + pci_dev_put(fn3); + + DRM_INFO("Detected %llu kiBytes of Video RAM at " + "physical address 0x%08llx.\n", + (unsigned long long)dev_priv->vram_size, + (unsigned long long)dev_priv->vram_start); + + return 0; + out_err: + + if (fn0) + pci_dev_put(fn0); + if (fn3) + pci_dev_put(fn3); + + DRM_ERROR("Failed reading PCI configuration to detect VRAM size.\n"); + return ret; +} + +/* + * Called at load time to set up the VRAM and TT memory types. + */ + +static int via_detect_agp(struct drm_device *dev) +{ + struct drm_via_private *dev_priv = via_priv(dev); + struct drm_agp_mode mode; + struct drm_agp_info agp_info; + int ret; + + ret = drm_agp_acquire(dev); + if (ret) { + DRM_ERROR("Failed acquiring AGP device.\n"); + return ret; + } + + ret = drm_agp_info(dev, &agp_info); + if (ret) { + DRM_ERROR("Failed detecting AGP aperture size.\n"); + goto out_err0; + } + + mode.mode = agp_info.mode & ~VIA_AGP_MODE_MASK; + if (mode.mode & VIA_AGPV3_MODE) + mode.mode |= VIA_AGPV3_8X_MODE; + else + mode.mode |= VIA_AGP_4X_MODE; + + mode.mode |= VIA_AGP_FW_MODE; + ret = drm_agp_enable(dev, mode); + if (ret) { + DRM_ERROR("Failed to enable the AGP bus.\n"); + goto out_err0; + } + + DRM_INFO("Detected %lu MiBytes of AGP Aperture at " + "physical address 0x%08lx.\n", + agp_info.aperture_size / (1024 * 1024), + agp_info.aperture_base); + + dev_priv->tt_size = agp_info.aperture_size; + dev_priv->tt_start = agp_info.aperture_base; + + return 0; + + out_err0: + drm_agp_release(dev); + return ret; +} + +static int via_ttm_init(struct drm_device *dev) +{ + struct drm_via_private *dev_priv = via_priv(dev); + int ret; + + ret = via_detect_vram(dev); + if (ret) + return ret; + + ret = via_detect_agp(dev); + if (ret) + return ret; + + dev_priv->vram_mtrr = drm_mtrr_add(dev_priv->vram_start, + dev_priv->vram_size * 1024, + DRM_MTRR_WC); + + ret = ttm_bo_device_init(&dev_priv->bdev, + dev_priv->mem_global_ref.object, + &via_bo_driver, DRM_FILE_PAGE_OFFSET); + if (ret) { + DRM_ERROR("Failed initializing buffer object driver.\n"); + goto out_err0; + } + ttm_lock_init(&dev_priv->ttm_lock); + + ret = ttm_bo_init_mm(&dev_priv->bdev, TTM_PL_VRAM, + DEV_VRAM_OFFSET >> PAGE_SHIFT, + (dev_priv->vram_size * 1024 - + DEV_VRAM_OFFSET) >> PAGE_SHIFT); + if (ret) { + DRM_ERROR("Failed initializing VRAM heap.\n"); + goto out_err1; + } + + ret = ttm_bo_init_mm(&dev_priv->bdev, TTM_PL_TT, + DEV_AGP_OFFSET >> PAGE_SHIFT, + (dev_priv->tt_size - + DEV_AGP_OFFSET) >> PAGE_SHIFT); + + if (ret) { + DRM_ERROR("Failed initializing AGP aperture heap.\n"); + goto out_err2; + } + + return 0; + out_err2: + (void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_VRAM); + out_err1: + (void)ttm_bo_device_release(&dev_priv->bdev); + out_err0: + drm_mtrr_del(dev_priv->vram_mtrr, dev_priv->vram_start, + dev_priv->vram_size * 1024, DRM_MTRR_WC); + drm_agp_release(dev); + return ret; +} + +/* + * Called at unload time to take down VRAM and TT memory types. + */ + +static void via_ttm_takedown(struct drm_device *dev) +{ + struct drm_via_private *dev_priv = via_priv(dev); + + (void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_VRAM); + (void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_TT); + (void)ttm_bo_device_release(&dev_priv->bdev); + drm_mtrr_del(dev_priv->vram_mtrr, dev_priv->vram_start, + dev_priv->vram_size * 1024, DRM_MTRR_WC); + drm_agp_release(dev); +} + +static int via_setup_mmio_map(struct drm_device *dev) +{ + struct drm_via_private *dev_priv = via_priv(dev); + unsigned long mmio_start; + unsigned long mmio_len; + + mmio_start = pci_resource_start(dev->pdev, 1); + mmio_len = pci_resource_len(dev->pdev, 1); + dev_priv->mmio_map = ioremap_nocache(mmio_start, mmio_len); + if (dev_priv->mmio_map == NULL) { + DRM_ERROR("Failed mapping graphics controller registers.\n"); + return -ENOMEM; + } + return 0; +} + +int via_driver_load(struct drm_device *dev, unsigned long chipset) +{ + struct drm_via_private *dev_priv; + int ret = 0; + int i; + + dev_priv = + drm_calloc(1, sizeof(struct drm_via_private), DRM_MEM_DRIVER); + if (dev_priv == NULL) + return -ENOMEM; + + ttm_pat_init(); + dev->dev_private = (void *)dev_priv; + dev_priv->dev = dev; + dev_priv->max_validate_buffers = 1000; + dev_priv->chipset = chipset; + + mutex_init(&dev_priv->init_mutex); + mutex_init(&dev_priv->cmdbuf_mutex); + INIT_LIST_HEAD(&dev_priv->dma_trackers); + dev_priv->irq_lock = SPIN_LOCK_UNLOCKED; + rwlock_init(&dev_priv->context_lock); + + ret = via_ttm_global_init(dev_priv); + if (ret) + goto out_err0; + + dev_priv->tdev = ttm_object_device_init + (dev_priv->mem_global_ref.object, 12); + + if (unlikely(dev_priv->tdev == NULL)) { + DRM_ERROR("Unable to initialize TTM object management.\n"); + ret = -ENOMEM; + goto out_err1; + } + + hrtimer_init(&dev_priv->fence_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + dev_priv->fence_timer.function = via_ttm_fence_timer_func; + dev_priv->fence_timer.cb_mode = HRTIMER_CB_SOFTIRQ; + + for (i = 0; i < VIA_NUM_ENGINES; ++i) { + atomic_set(&dev_priv->fence_seq[i], 0); + } + + ret = via_setup_mmio_map(dev); + if (ret) + goto out_err2; + + ret = via_ttm_fence_device_init(dev_priv); + if (ret) + goto out_err3; + + ret = via_ttm_init(dev); + if (ret) + goto out_err4; + + via_init_dmablit(dev); + return 0; + + out_err4: + ttm_fence_device_release(&dev_priv->fdev); + out_err3: + iounmap(dev_priv->mmio_map); + out_err2: + ttm_object_device_release(&dev_priv->tdev); + out_err1: + via_ttm_global_release(dev_priv); + out_err0: + drm_free(dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER); + return ret; +} + +int via_driver_unload(struct drm_device *dev) +{ + struct drm_via_private *dev_priv = via_priv(dev); + + via_ttm_takedown(dev); + ttm_fence_device_release(&dev_priv->fdev); + iounmap(dev_priv->mmio_map); + ttm_object_device_release(&dev_priv->tdev); + via_ttm_global_release(dev_priv); + drm_free(dev_priv, sizeof(struct drm_via_private), DRM_MEM_DRIVER); + ttm_pat_takedown(); + + return 0; +} + +int via_open(struct inode *inode, struct file *filp) +{ + struct drm_file *file_priv; + struct drm_via_private *dev_priv; + struct via_fpriv *via_fp; + int ret; + + ret = drm_open(inode, filp); + if (unlikely(ret)) + return ret; + + via_fp = drm_calloc(1, sizeof(*via_fp), DRM_MEM_FILES); + + if (unlikely(via_fp == NULL)) + goto out_err0; + + file_priv = (struct drm_file *)filp->private_data; + dev_priv = via_priv(file_priv->minor->dev); + + via_fp->tfile = ttm_object_file_init(dev_priv->tdev, 10); + if (unlikely(via_fp->tfile == NULL)) + goto out_err1; + + file_priv->driver_priv = via_fp; + + if (unlikely(dev_priv->bdev.dev_mapping == NULL)) + dev_priv->bdev.dev_mapping = dev_priv->dev->dev_mapping; + + return 0; + + out_err1: + drm_free(via_fp, sizeof(*via_fp), DRM_MEM_FILES); + out_err0: + (void)drm_release(inode, filp); + return ret; +} + +int via_release(struct inode *inode, struct file *filp) +{ + struct drm_file *file_priv; + struct via_fpriv *via_fp; + + file_priv = (struct drm_file *)filp->private_data; + via_fp = via_fpriv(file_priv); + ttm_object_file_release(&via_fp->tfile); + drm_free(via_fp, sizeof(*via_fp), DRM_MEM_FILES); + return drm_release(inode, filp); +} + +static int via_firstopen_locked(struct drm_device *dev) +{ + struct drm_via_private *dev_priv = via_priv(dev); + int ret; + + /* + * Allocate a BO for the AGP memory region. + */ + + ret = ttm_buffer_object_create(&dev_priv->bdev, VIA_AGPBO_SIZE, + ttm_bo_type_kernel, + TTM_PL_FLAG_TT | + TTM_PL_FLAG_NO_EVICT, + 0, 0, 0, NULL, &dev_priv->agp_bo); + if (ret) + return ret; + + list_del_init(&dev_priv->agp_bo->lru); + + ret = ttm_bo_init_mm(&dev_priv->bdev, TTM_PL_PRIV0, + 0, dev_priv->agp_bo->num_pages); + if (ret) + goto out_err1; + + via_irq_init(dev); + ret = via_exec_init(dev); + if (ret) + goto out_err2; + + return 0; + out_err2: + (void)drm_irq_uninstall(dev); + (void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_PRIV0); + out_err1: + ttm_bo_unref(&dev_priv->agp_bo); + + return ret; +} + +int via_firstopen(struct drm_device *dev) +{ + struct drm_via_private *dev_priv = via_priv(dev); + int ret; + + mutex_lock(&dev_priv->init_mutex); + ret = via_firstopen_locked(dev); + + ret = drm_ht_create(&dev_priv->context_hash, 10); + if (unlikely(ret != 0)) + goto out_err0; + + mutex_unlock(&dev_priv->init_mutex); + + return ret; + out_err0: + via_lastclose(dev); + return ret; +} + +void via_lastclose(struct drm_device *dev) +{ + struct drm_via_private *dev_priv = via_priv(dev); + + if (!dev_priv) + return; + + mutex_lock(&dev_priv->init_mutex); + dev_priv->sarea = NULL; + dev_priv->sarea_priv = NULL; + + via_ttm_signal_fences(dev_priv); + (void)ttm_bo_evict_mm(&dev_priv->bdev, TTM_PL_VRAM); + (void)ttm_bo_evict_mm(&dev_priv->bdev, TTM_PL_TT); + hrtimer_cancel(&dev_priv->fence_timer); + if (dev_priv->agp_bo) { + (void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_PRIV0); + + BUG_ON(dev_priv->agp_bo->sync_obj != NULL); + BUG_ON(!list_empty(&dev_priv->agp_bo->lru)); + + ttm_bo_unref(&dev_priv->agp_bo); + via_exec_takedown(dev_priv); + + if (unlikely + (!drm_mm_clean(&dev_priv->bdev.man[TTM_PL_VRAM].manager))) + DRM_ERROR("Vram manager was not clean.\n"); + if (unlikely + (!drm_mm_clean(&dev_priv->bdev.man[TTM_PL_TT].manager))) + DRM_ERROR("TT manager was not clean.\n"); + } + (void)drm_irq_uninstall(dev); + drm_ht_remove(&dev_priv->context_hash); + mutex_unlock(&dev_priv->init_mutex); +} + +static int via_leavevt(struct drm_device *dev, struct drm_file *file_priv) +{ + struct drm_via_private *dev_priv = via_priv(dev); + int ret; + + /* + * Block all TTM operations. + */ + + ret = ttm_write_lock(&dev_priv->ttm_lock, 1, + via_fpriv(file_priv)->tfile); + if (ret) + return ret; + + /* + * Clear and disable the VIA_AGP memory type. + */ + + mutex_lock(&dev_priv->init_mutex); + via_ttm_signal_fences(dev_priv); + hrtimer_cancel(&dev_priv->fence_timer); + + ret = ttm_bo_evict_mm(&dev_priv->bdev, TTM_PL_VRAM); + if (ret) + goto out_unlock; + + ret = ttm_bo_evict_mm(&dev_priv->bdev, TTM_PL_TT); + if (ret) + goto out_unlock; + + if (dev_priv->agp_bo) { + (void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_PRIV0); + + BUG_ON(dev_priv->agp_bo->sync_obj != NULL); + BUG_ON(!list_empty(&dev_priv->agp_bo->lru)); + + ttm_bo_unref(&dev_priv->agp_bo); + via_exec_takedown(dev_priv); + if (unlikely + (!drm_mm_clean(&dev_priv->bdev.man[TTM_PL_PRIV0].manager))) + DRM_ERROR("AGP manager was not clean.\n"); + } + + if (unlikely(!drm_mm_clean(&dev_priv->bdev.man[TTM_PL_VRAM].manager))) + DRM_ERROR("Vram manager was not clean.\n"); + if (unlikely(!drm_mm_clean(&dev_priv->bdev.man[TTM_PL_TT].manager))) + DRM_ERROR("TT manager was not clean.\n"); + + ttm_bo_swapout_all(&dev_priv->bdev); + + (void)drm_irq_uninstall(dev); + mutex_unlock(&dev_priv->init_mutex); + return 0; + out_unlock: + mutex_unlock(&dev_priv->init_mutex); + ttm_write_unlock(&dev_priv->ttm_lock, via_fpriv(file_priv)->tfile); + return ret; +} + +static int via_entervt(struct drm_device *dev, struct drm_file *file_priv) +{ + struct drm_via_private *dev_priv = via_priv(dev); + int ret; + + mutex_lock(&dev_priv->init_mutex); + ret = via_firstopen_locked(dev); + mutex_unlock(&dev_priv->init_mutex); + if (ret) + return ret; + + return ttm_write_unlock(&dev_priv->ttm_lock, + via_fpriv(file_priv)->tfile); +} + +int via_vt_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + struct drm_via_vt *vt = (struct drm_via_vt *)data; + + if (vt->enter) + return via_entervt(dev, file_priv); + else + return via_leavevt(dev, file_priv); +} + +static unsigned long via_mm_largest(struct drm_mm *mm) +{ + unsigned long largest_size = 0; + const struct list_head *free_stack = &mm->fl_entry; + struct drm_mm_node *entry; + + list_for_each_entry(entry, free_stack, fl_entry) { + if (entry->size > largest_size) + largest_size = entry->size; + } + + return largest_size; +} + +int via_getparam_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct ttm_mem_type_manager *man; + struct drm_via_getparam_arg *arg = data; + struct drm_via_private *dev_priv = via_priv(dev); + struct ttm_bo_device *bdev = &dev_priv->bdev; + + switch (arg->param) { + case DRM_VIA_PARAM_VRAM_SIZE: + man = &bdev->man[TTM_PL_VRAM]; + arg->value = via_mm_largest(&man->manager) << PAGE_SHIFT; + return 0; + case DRM_VIA_PARAM_TT_SIZE: + man = &bdev->man[TTM_PL_TT]; + arg->value = via_mm_largest(&man->manager) << PAGE_SHIFT; + return 0; + case DRM_VIA_PARAM_AGP_SIZE: + man = &bdev->man[TTM_PL_PRIV0]; + arg->value = via_mm_largest(&man->manager) << PAGE_SHIFT; + return 0; + case DRM_VIA_PARAM_HAS_IRQ: + arg->value = dev->irq_enabled; + return 0; + case DRM_VIA_PARAM_SAREA_SIZE: + arg->value = (sizeof(struct via_sarea) + PAGE_SIZE - 1) & + ~PAGE_MASK; + return 0; + default: + break; + } + return -EINVAL; +} + +int via_extension_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + union drm_via_extension_arg *arg = data; + struct drm_via_extension_rep *rep = &arg->rep; + + if (strcmp(arg->extension, "via_ttm_placement_081121") == 0) { + rep->exists = 1; + rep->driver_ioctl_offset = DRM_VIA_PLACEMENT_OFFSET; + rep->driver_sarea_offset = 0; + rep->major = 1; + rep->minor = 0; + rep->pl = 0; + return 0; + } + if (strcmp(arg->extension, "via_ttm_fence_081121") == 0) { + rep->exists = 1; + rep->driver_ioctl_offset = DRM_VIA_FENCE_OFFSET; + rep->driver_sarea_offset = 0; + rep->major = 1; + rep->minor = 0; + rep->pl = 0; + return 0; + } + if (strcmp(arg->extension, "via_ttm_execbuf") == 0) { + rep->exists = 1; + rep->driver_ioctl_offset = DRM_VIA_TTM_EXECBUF; + rep->driver_sarea_offset = 0; + rep->major = 1; + rep->minor = 0; + rep->pl = 0; + return 0; + } + if (strcmp(arg->extension, "via_decoder_futex") == 0) { + rep->exists = 1; + rep->driver_ioctl_offset = DRM_VIA_DEC_FUTEX; + rep->driver_sarea_offset = offsetof(struct via_sarea, sa_xvmc); + rep->major = 1; + rep->minor = 0; + rep->pl = 0; + return 0; + } + + rep->exists = 0; + return 0; +} + +int via_resume(struct pci_dev *pdev) +{ + ttm_pat_resume(); + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + return pci_enable_device(pdev); +} + +int via_suspend(struct pci_dev *pdev, pm_message_t state) +{ + pci_save_state(pdev); + pci_disable_device(pdev); + pci_set_power_state(pdev, PCI_D3hot); + + return 0; +} diff --git a/linux-core/openchrome/via_irq.c b/linux-core/openchrome/via_irq.c new file mode 100644 index 00000000..469e1c9d --- /dev/null +++ b/linux-core/openchrome/via_irq.c @@ -0,0 +1,246 @@ +/* via_irq.c + * + * Copyright 2004 BEAM Ltd. + * Copyright (c) 2002-2008 Tungsten Graphics, Inc., Cedar Park, TX., USA, + * All Rights Reserved. + * Copyright 2005 Thomas Hellstrom. + * 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 (including the next + * paragraph) 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 + * BEAM LTD, TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS 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: + * Terry Barnaby + * Keith Whitwell + * Thomas Hellstrom + * + * This code provides standard DRM access to the Via Unichrome / Pro Vertical blank + * interrupt, as well as an infrastructure to handle other interrupts of the chip. + * The refresh rate is also calculated for video playback sync purposes. + */ + +#include "drmP.h" +#include "drm.h" +#include "ochr_drm.h" +#include "via_drv.h" + +#define VIA_REG_INTERRUPT 0x200 + +/* VIA_REG_INTERRUPT */ +#define VIA_IRQ_GLOBAL (1 << 31) +#define VIA_IRQ_VBLANK_ENABLE (1 << 19) +#define VIA_IRQ_VBLANK_PENDING (1 << 3) +#define VIA_IRQ_HQV0_ENABLE (1 << 11) +#define VIA_IRQ_HQV1_ENABLE (1 << 25) +#define VIA_IRQ_HQV0_PENDING (1 << 9) +#define VIA_IRQ_HQV1_PENDING (1 << 10) +#define VIA_IRQ_DMA0_DD_ENABLE (1 << 20) +#define VIA_IRQ_DMA0_TD_ENABLE (1 << 21) +#define VIA_IRQ_DMA1_DD_ENABLE (1 << 22) +#define VIA_IRQ_DMA1_TD_ENABLE (1 << 23) +#define VIA_IRQ_DMA0_DD_PENDING (1 << 4) +#define VIA_IRQ_DMA0_TD_PENDING (1 << 5) +#define VIA_IRQ_DMA1_DD_PENDING (1 << 6) +#define VIA_IRQ_DMA1_TD_PENDING (1 << 7) + +u32 via_get_vblank_counter(struct drm_device * dev, int crtc) +{ + struct drm_via_private *dev_priv = dev->dev_private; + if (crtc != 0) + return 0; + + return atomic_read(&dev_priv->vbl_received); +} + +irqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS) +{ + struct drm_device *dev = (struct drm_device *)arg; + struct drm_via_private *dev_priv = via_priv(dev); + u32 status; + u32 pending; + int handled = 0; + + spin_lock(&dev_priv->irq_lock); + status = VIA_READ(VIA_REG_INTERRUPT); + + pending = status & dev_priv->irq_pending_mask; + + if (pending & VIA_IRQ_HQV0_PENDING) { + via_ttm_fence_cmd_handler(dev_priv, VIA_FENCE_TYPE_HQV0); + handled = 1; + } + if (pending & VIA_IRQ_HQV1_PENDING) { + via_ttm_fence_cmd_handler(dev_priv, VIA_FENCE_TYPE_HQV1); + handled = 1; + } + + if (pending & VIA_IRQ_DMA0_TD_PENDING) { + via_ttm_fence_dmablit_handler(dev_priv, VIA_ENGINE_DMA0); + handled = 1; + } + if (pending & VIA_IRQ_DMA1_TD_PENDING) { + handled = 1; + via_ttm_fence_dmablit_handler(dev_priv, VIA_ENGINE_DMA1); + } + + if (status & VIA_IRQ_VBLANK_PENDING) { + atomic_inc(&dev_priv->vbl_received); + drm_handle_vblank(dev, 0); + handled = 1; + } + + /* Acknowlege interrupts */ + VIA_WRITE(VIA_REG_INTERRUPT, status); + (void)VIA_READ(VIA_REG_INTERRUPT); + + spin_unlock(&dev_priv->irq_lock); + + if (handled) + return IRQ_HANDLED; + else + return IRQ_NONE; +} + +static __inline__ void viadrv_acknowledge_irqs(struct drm_via_private *dev_priv) +{ + u32 status; + + if (dev_priv) { + /* Acknowlege interrupts */ + status = VIA_READ(VIA_REG_INTERRUPT); + VIA_WRITE(VIA_REG_INTERRUPT, status); + (void)VIA_READ(VIA_REG_INTERRUPT); + } +} + +int via_enable_vblank(struct drm_device *dev, int crtc) +{ + struct drm_via_private *dev_priv = via_priv(dev); + unsigned long irq_flags; + + if (crtc != 0) { + DRM_ERROR("%s: bad crtc %d\n", __FUNCTION__, crtc); + return -EINVAL; + } + + spin_lock_irqsave(&dev_priv->irq_lock, irq_flags); + dev_priv->irq_enable_mask |= VIA_IRQ_VBLANK_ENABLE; + VIA_WRITE(VIA_REG_INTERRUPT, dev_priv->irq_enable_mask); + (void)VIA_READ(VIA_REG_INTERRUPT); + spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags); + + return 0; +} + +void via_disable_vblank(struct drm_device *dev, int crtc) +{ + struct drm_via_private *dev_priv = via_priv(dev); + unsigned long irq_flags; + + if (crtc != 0) + DRM_ERROR("%s: bad crtc %d\n", __FUNCTION__, crtc); + + spin_lock_irqsave(&dev_priv->irq_lock, irq_flags); + dev_priv->irq_enable_mask &= ~VIA_IRQ_VBLANK_ENABLE; + VIA_WRITE(VIA_REG_INTERRUPT, dev_priv->irq_enable_mask); + (void)VIA_READ(VIA_REG_INTERRUPT); + spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags); +} + +/* + * drm_dma.h hooks + */ + +void via_driver_irq_preinstall(struct drm_device *dev) +{ + struct drm_via_private *dev_priv = via_priv(dev); + + DRM_DEBUG("dev_priv: %p\n", dev_priv); + if (dev_priv) { + spin_lock_irq(&dev_priv->irq_lock); + dev_priv->irq_enable_mask = + VIA_IRQ_GLOBAL | + VIA_IRQ_DMA0_TD_ENABLE | VIA_IRQ_DMA1_TD_ENABLE; + dev_priv->irq2_enable_mask = 0; + dev_priv->irq_pending_mask = + VIA_IRQ_VBLANK_PENDING | + VIA_IRQ_DMA0_TD_PENDING | VIA_IRQ_DMA1_TD_PENDING; + dev_priv->irq2_pending_mask = 0; + + if (dev_priv->chipset == VIA_PRO_GROUP_A || + dev_priv->chipset == VIA_DX9_0) { + dev_priv->irq_enable_mask |= + VIA_IRQ_HQV0_ENABLE | VIA_IRQ_HQV1_ENABLE; + dev_priv->irq_pending_mask |= + VIA_IRQ_HQV0_PENDING | VIA_IRQ_HQV1_PENDING; + } + + /* Clear bits if they're already high */ + viadrv_acknowledge_irqs(dev_priv); + spin_unlock_irq(&dev_priv->irq_lock); + } +} + +int via_driver_irq_postinstall(struct drm_device *dev) +{ + struct drm_via_private *dev_priv = via_priv(dev); + + DRM_DEBUG("via_driver_irq_postinstall\n"); + if (!dev_priv) + return -EINVAL; + + drm_vblank_init(dev, 1); + + spin_lock_irq(&dev_priv->irq_lock); + + VIA_WRITE(VIA_REG_INTERRUPT, dev_priv->irq_enable_mask); + wmb(); + + /* Some magic, oh for some data sheets ! */ + VIA_WRITE8(0x83d4, 0x11); + wmb(); + VIA_WRITE8(0x83d5, VIA_READ8(0x83d5) | 0x30); + (void)VIA_READ8(0x83d5); + + spin_unlock_irq(&dev_priv->irq_lock); + return 0; +} + +void via_driver_irq_uninstall(struct drm_device *dev) +{ + struct drm_via_private *dev_priv = via_priv(dev); + u32 status; + + DRM_DEBUG("\n"); + if (dev_priv) { + spin_lock_irq(&dev_priv->irq_lock); + /* Some more magic, oh for some data sheets ! */ + + VIA_WRITE8(0x83d4, 0x11); + wmb(); + VIA_WRITE8(0x83d5, VIA_READ8(0x83d5) & ~0x30); + + status = VIA_READ(VIA_REG_INTERRUPT); + VIA_WRITE(VIA_REG_INTERRUPT, + status & ~dev_priv->irq_enable_mask); + (void)VIA_READ(VIA_REG_INTERRUPT); + spin_unlock_irq(&dev_priv->irq_lock); + } +} diff --git a/linux-core/openchrome/via_ttm_fence.c b/linux-core/openchrome/via_ttm_fence.c new file mode 100644 index 00000000..cf15de37 --- /dev/null +++ b/linux-core/openchrome/via_ttm_fence.c @@ -0,0 +1,230 @@ +/************************************************************************** + * + * Copyright (c) 2008 Tungsten Graphics, Inc., Cedar Park, TX., USA, + * All Rights Reserved. + * Copyright (c) 2009 VMware, Inc., Palo Alto, CA., USA, + * 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, sub license, 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 (including the + * next paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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: Thomas Hellström + */ + +#include "drmP.h" +#include "ochr_drm.h" +#include "via_drv.h" +#include "ttm/ttm_fence_driver.h" + +#define VIA_POLL_DELAY_NS 100000 + +static void via_ttm_fence_poll(struct ttm_fence_device *fdev, + uint32_t engine, uint32_t waiting_types) +{ + struct drm_via_private *dev_priv = + container_of(fdev, struct drm_via_private, fdev); + uint32_t seq; + + if (unlikely(!waiting_types)) + return; + + switch (engine) { + case VIA_ENGINE_CMD: + { + struct ttm_fence_class_manager *fc = + &fdev->fence_class[engine]; + uint32_t offset = VIA_FENCE_OFFSET_CMD; + uint32_t type = TTM_FENCE_TYPE_EXE; + uint32_t error = 0; + + offset >>= 2; + + seq = ioread32(dev_priv->fence_map + offset); + + if (unlikely(waiting_types & VIA_FENCE_TYPE_HQV0)) { + if (!(VIA_READ(0x3D0) & (1 << 4))) + type |= VIA_FENCE_TYPE_HQV0; + } + + if (unlikely(waiting_types & VIA_FENCE_TYPE_HQV1)) { + if (!(VIA_READ(0x13D0) & (1 << 4))) + type |= VIA_FENCE_TYPE_HQV1; + } + + if (unlikely(waiting_types & VIA_FENCE_TYPE_MPEG0)) { + uint32_t mpeg_status = VIA_READ(0xC54); + if ((mpeg_status & 0x207) == 0x204) + type |= VIA_FENCE_TYPE_MPEG0; + if (unlikely((mpeg_status & 0x70) != 0)) { + type |= VIA_FENCE_TYPE_MPEG0; + error = mpeg_status & 0x70; + } + } + + /* + * FIXME: Info on MPEG1 engine? + */ + + ttm_fence_handler(fdev, engine, seq, type, error); + if (fc->waiting_types) { + hrtimer_start(&dev_priv->fence_timer, + ktime_set(0, VIA_POLL_DELAY_NS), + HRTIMER_MODE_REL); + } + break; + } + case VIA_ENGINE_DMA0: + case VIA_ENGINE_DMA1: + case VIA_ENGINE_DMA2: + case VIA_ENGINE_DMA3: + { + uint32_t dma_engine = engine - VIA_ENGINE_DMA0; + struct drm_via_blitq *blitq = + &dev_priv->blit_queues[dma_engine]; + unsigned long irq_flags; + + via_dmablit_handler(dev_priv->dev, dma_engine); + + spin_lock_irqsave(&blitq->blit_lock, irq_flags); + seq = blitq->completed_fence_seq; + spin_unlock_irqrestore(&blitq->blit_lock, irq_flags); + + ttm_fence_handler(fdev, engine, seq, TTM_FENCE_TYPE_EXE, + 0); + break; + } + default: + break; + } +} + +/** + * Emit a fence sequence. + */ + +static int via_ttm_fence_emit_sequence(struct ttm_fence_device *fdev, + uint32_t class, + uint32_t flags, + uint32_t * sequence, + unsigned long *timeout_jiffies) +{ + struct drm_via_private *dev_priv = + container_of(fdev, struct drm_via_private, fdev); + + *sequence = atomic_read(&dev_priv->fence_seq[class]); + *timeout_jiffies = jiffies + 3 * HZ; + + return 0; +} + +void via_ttm_fence_cmd_handler(struct drm_via_private *dev_priv, + uint32_t signal_types) +{ + struct ttm_fence_device *fdev = &dev_priv->fdev; + struct ttm_fence_class_manager *fc = &fdev->fence_class[VIA_ENGINE_CMD]; + + write_lock(&fc->lock); + via_ttm_fence_poll(fdev, VIA_ENGINE_CMD, + fc->waiting_types | signal_types); + write_unlock(&fc->lock); +} + +void via_ttm_fence_dmablit_handler(struct drm_via_private *dev_priv, int engine) +{ + struct ttm_fence_device *fdev = &dev_priv->fdev; + struct ttm_fence_class_manager *fc = &fdev->fence_class[engine]; + + write_lock(&fc->lock); + via_ttm_fence_poll(fdev, engine, fc->waiting_types); + write_unlock(&fc->lock); +} + +static int via_ttm_fence_has_irq(struct ttm_fence_device *fdev, + uint32_t engine, uint32_t flags) +{ + struct drm_via_private *dev_priv = + container_of(fdev, struct drm_via_private, fdev); + + if (engine >= VIA_ENGINE_DMA0) + return dev_priv->has_irq; + + return hrtimer_is_hres_active(&dev_priv->fence_timer); +} + +void via_ttm_signal_fences(struct drm_via_private *dev_priv) +{ + struct ttm_fence_class_manager *fc; + int i; + unsigned long irq_flags; + + if (via_driver_dma_quiescent(dev_priv->dev)) { + msleep(1000); + } + + for (i = 0; i < dev_priv->fdev.num_classes; ++i) { + fc = &dev_priv->fdev.fence_class[i]; + + write_lock_irqsave(&fc->lock, irq_flags); + ttm_fence_handler(&dev_priv->fdev, i, + atomic_read(&dev_priv->fence_seq[i]), + 0xFFFFFFFF, 0); + write_unlock_irqrestore(&fc->lock, irq_flags); + } +} + +enum hrtimer_restart via_ttm_fence_timer_func(struct hrtimer *timer) +{ + struct drm_via_private *dev_priv = + container_of(timer, struct drm_via_private, fence_timer); + struct ttm_fence_device *fdev = &dev_priv->fdev; + struct ttm_fence_class_manager *fc = &fdev->fence_class[VIA_ENGINE_CMD]; + unsigned long irq_flags; + + write_lock_irqsave(&fc->lock, irq_flags); + via_ttm_fence_poll(fdev, 0, fc->waiting_types); + write_unlock_irqrestore(&fc->lock, irq_flags); + + return HRTIMER_NORESTART; +} + +static struct ttm_fence_driver via_ttm_fence_driver = { + .has_irq = via_ttm_fence_has_irq, + .emit = via_ttm_fence_emit_sequence, + .flush = NULL, + .poll = via_ttm_fence_poll, + .needed_flush = NULL, + .wait = NULL, + .signaled = NULL, +}; + +int via_ttm_fence_device_init(struct drm_via_private *dev_priv) +{ + struct ttm_fence_class_init fci = {.wrap_diff = (1 << 30), + .flush_diff = (1 << 29), + .sequence_mask = 0xFFFFFFFF + }; + + return ttm_fence_device_init(5, + dev_priv->mem_global_ref.object, + &dev_priv->fdev, + &fci, 1, &via_ttm_fence_driver); +} diff --git a/linux-core/openchrome/via_ttm_glue.c b/linux-core/openchrome/via_ttm_glue.c new file mode 100644 index 00000000..c0e6a43d --- /dev/null +++ b/linux-core/openchrome/via_ttm_glue.c @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2007-2008 Tungsten Graphics, Inc., Cedar Park, TX., USA, + * All Rights Reserved. + * Copyright (c) 2009 VMware, Inc., Palo Alto, CA., USA, + * 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, sub license, + * 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 (including the + * next paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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. + */ + +#include "drmP.h" +#include "via_drv.h" +#include "ttm/ttm_userobj_api.h" + +static struct vm_operations_struct via_ttm_vm_ops; +static struct vm_operations_struct *ttm_vm_ops = NULL; + +int via_fence_signaled_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + return ttm_fence_signaled_ioctl(via_fpriv(file_priv)->tfile, data); +} + +int via_fence_finish_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + return ttm_fence_finish_ioctl(via_fpriv(file_priv)->tfile, data); +} + +int via_fence_unref_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + + return ttm_fence_unref_ioctl(via_fpriv(file_priv)->tfile, data); +} + +int via_pl_waitidle_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + return ttm_pl_waitidle_ioctl(via_fpriv(file_priv)->tfile, data); +} + +int via_pl_setstatus_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + return ttm_pl_setstatus_ioctl(via_fpriv(file_priv)->tfile, + &via_priv(dev)->ttm_lock, data); +} + +int via_pl_synccpu_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + return ttm_pl_synccpu_ioctl(via_fpriv(file_priv)->tfile, data); +} + +int via_pl_unref_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + return ttm_pl_unref_ioctl(via_fpriv(file_priv)->tfile, data); +} + +int via_pl_reference_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + return ttm_pl_reference_ioctl(via_fpriv(file_priv)->tfile, data); +} + +int via_pl_create_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_via_private *dev_priv = via_priv(dev); + + return ttm_pl_create_ioctl(via_fpriv(file_priv)->tfile, + &dev_priv->bdev, &dev_priv->ttm_lock, data); +} + +/** + * psb_ttm_fault - Wrapper around the ttm fault method. + * + * @vma: The struct vm_area_struct as in the vm fault() method. + * @vmf: The struct vm_fault as in the vm fault() method. + * + * Since ttm_fault() will reserve buffers while faulting, + * we need to take the ttm read lock around it, as this driver + * relies on the ttm_lock in write mode to exclude all threads from + * reserving and thus validating buffers in aperture- and memory shortage + * situations. + */ + +static int via_ttm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +{ + struct ttm_buffer_object *bo = (struct ttm_buffer_object *) + vma->vm_private_data; + struct drm_via_private *dev_priv = + container_of(bo->bdev, struct drm_via_private, bdev); + int ret; + + ret = ttm_read_lock(&dev_priv->ttm_lock, true); + if (unlikely(ret != 0)) + return VM_FAULT_NOPAGE; + + ret = ttm_vm_ops->fault(vma, vmf); + + ttm_read_unlock(&dev_priv->ttm_lock); + + return ret; +} + +int via_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct drm_file *file_priv; + struct drm_via_private *dev_priv; + int ret; + + if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET)) + return drm_mmap(filp, vma); + + file_priv = (struct drm_file *)filp->private_data; + dev_priv = via_priv(file_priv->minor->dev); + ret = ttm_bo_mmap(filp, vma, &dev_priv->bdev); + + if (unlikely(ret != 0)) + return ret; + + if (unlikely(ttm_vm_ops == NULL)) { + ttm_vm_ops = vma->vm_ops; + via_ttm_vm_ops = *ttm_vm_ops; + via_ttm_vm_ops.fault = &via_ttm_fault; + } + + vma->vm_ops = &via_ttm_vm_ops; + return 0; +} + +ssize_t via_ttm_write(struct file * filp, const char __user * buf, + size_t count, loff_t * f_pos) +{ + struct drm_file *file_priv = (struct drm_file *)filp->private_data; + struct drm_via_private *dev_priv = via_priv(file_priv->minor->dev); + + return ttm_bo_io(&dev_priv->bdev, filp, buf, NULL, count, f_pos, 1); +} + +ssize_t via_ttm_read(struct file * filp, char __user * buf, + size_t count, loff_t * f_pos) +{ + struct drm_file *file_priv = (struct drm_file *)filp->private_data; + struct drm_via_private *dev_priv = via_priv(file_priv->minor->dev); + + return ttm_bo_io(&dev_priv->bdev, filp, NULL, buf, count, f_pos, 1); +} + +int via_verify_access(struct ttm_buffer_object *bo, struct file *filp) +{ + struct drm_file *file_priv = (struct drm_file *)filp->private_data; + + return ttm_pl_verify_access(bo, via_fpriv(file_priv)->tfile); +} + +static int via_ttm_mem_global_init(struct drm_global_reference *ref) +{ + return ttm_mem_global_init(ref->object); +} + +static void via_ttm_mem_global_release(struct drm_global_reference *ref) +{ + ttm_mem_global_release(ref->object); +} + +int via_ttm_global_init(struct drm_via_private *dev_priv) +{ + struct drm_global_reference *global_ref; + int ret; + + global_ref = &dev_priv->mem_global_ref; + global_ref->global_type = DRM_GLOBAL_TTM_MEM; + global_ref->size = sizeof(struct ttm_mem_global); + global_ref->init = &via_ttm_mem_global_init; + global_ref->release = &via_ttm_mem_global_release; + + ret = drm_global_item_ref(global_ref); + if (unlikely(ret != 0)) { + DRM_ERROR("Failed referencing a global TTM memory object.\n"); + return ret; + } + + return 0; +} + +void via_ttm_global_release(struct drm_via_private *dev_priv) +{ + drm_global_item_unref(&dev_priv->mem_global_ref); +} diff --git a/linux-core/openchrome/via_verifier.c b/linux-core/openchrome/via_verifier.c new file mode 100644 index 00000000..59a786e4 --- /dev/null +++ b/linux-core/openchrome/via_verifier.c @@ -0,0 +1,1104 @@ +/* + * Copyright 2004 The Unichrome Project. All Rights Reserved. + * Copyright 2005 Thomas Hellstrom. 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, sub license, + * 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 (including the + * next paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHOR(S), AND/OR THE COPYRIGHT HOLDER(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. + * + * Author: Thomas Hellstrom 2004, 2005. + * This code was written using docs obtained under NDA from VIA Inc. + * + * Don't run this code directly on an AGP buffer. Due to cache problems it will + * be very slow. + */ + +#include "via_3d_reg.h" +#include "drmP.h" +#include "drm.h" +#include "ochr_drm.h" +#include "via_verifier.h" +#include "via_drv.h" + +typedef enum { + state_command, + state_header2, + state_header1, + state_vheader5, + state_vheader6, + state_error +} verifier_state_t; + +typedef enum { + no_check = 0, + check_for_header2, + check_for_header1, + check_for_header2_err, + check_for_header1_err, + check_for_fire, + check_z_buffer_addr0, + check_z_buffer_addr1, + check_z_buffer_addr_mode, + check_destination_addr0, + check_destination_addr1, + check_destination_addr_mode, + check_for_dummy, + check_for_dd, + check_texture_addr0, + check_texture_addr1, + check_texture_addr2, + check_texture_addr3, + check_texture_addr4, + check_texture_addr5, + check_texture_addr6, + check_texture_addr7, + check_texture_addr8, + check_texture_addr_mode, + check_for_vertex_count, + check_number_texunits, + forbidden_command +} hazard_t; + +/* + * Associates each hazard above with a possible multi-command + * sequence. For example an address that is split over multiple + * commands and that needs to be checked at the first command + * that does not include any part of the address. + */ + +static drm_via_sequence_t seqs[] = { + no_sequence, + no_sequence, + no_sequence, + no_sequence, + no_sequence, + no_sequence, + z_address, + z_address, + z_address, + dest_address, + dest_address, + dest_address, + no_sequence, + no_sequence, + tex_address, + tex_address, + tex_address, + tex_address, + tex_address, + tex_address, + tex_address, + tex_address, + tex_address, + tex_address, + no_sequence +}; + +typedef struct { + unsigned int code; + hazard_t hz; +} hz_init_t; + +static hz_init_t init_table1[] = { + {0xf2, check_for_header2_err}, + {0xf0, check_for_header1_err}, + {0xee, check_for_fire}, + {0xcc, check_for_dummy}, + {0xdd, check_for_dd}, + {0x00, no_check}, + {0x10, check_z_buffer_addr0}, + {0x11, check_z_buffer_addr1}, + {0x12, check_z_buffer_addr_mode}, + {0x13, no_check}, + {0x14, no_check}, + {0x15, no_check}, + {0x23, no_check}, + {0x24, no_check}, + {0x33, no_check}, + {0x34, no_check}, + {0x35, no_check}, + {0x36, no_check}, + {0x37, no_check}, + {0x38, no_check}, + {0x39, no_check}, + {0x3A, no_check}, + {0x3B, no_check}, + {0x3C, no_check}, + {0x3D, no_check}, + {0x3E, no_check}, + {0x40, check_destination_addr0}, + {0x41, check_destination_addr1}, + {0x42, check_destination_addr_mode}, + {0x43, no_check}, + {0x44, no_check}, + {0x50, no_check}, + {0x51, no_check}, + {0x52, no_check}, + {0x53, no_check}, + {0x54, no_check}, + {0x55, no_check}, + {0x56, no_check}, + {0x57, no_check}, + {0x58, no_check}, + {0x70, no_check}, + {0x71, no_check}, + {0x78, no_check}, + {0x79, no_check}, + {0x7A, no_check}, + {0x7B, no_check}, + {0x7C, no_check}, + {0x7D, check_for_vertex_count} +}; + +static hz_init_t init_table2[] = { + {0xf2, check_for_header2_err}, + {0xf0, check_for_header1_err}, + {0xee, check_for_fire}, + {0xcc, check_for_dummy}, + {0x00, check_texture_addr0}, + {0x01, check_texture_addr0}, + {0x02, check_texture_addr0}, + {0x03, check_texture_addr0}, + {0x04, check_texture_addr0}, + {0x05, check_texture_addr0}, + {0x06, check_texture_addr0}, + {0x07, check_texture_addr0}, + {0x08, check_texture_addr0}, + {0x09, check_texture_addr0}, + {0x0A, check_texture_addr0}, + {0x0B, check_texture_addr0}, + {0x20, check_texture_addr1}, + {0x21, check_texture_addr1}, + {0x22, check_texture_addr1}, + {0x23, check_texture_addr4}, + {0x2B, check_texture_addr3}, + {0x2C, check_texture_addr3}, + {0x2D, check_texture_addr3}, + {0x2E, check_texture_addr3}, + {0x2F, check_texture_addr3}, + {0x30, check_texture_addr3}, + {0x31, check_texture_addr3}, + {0x32, check_texture_addr3}, + {0x33, check_texture_addr3}, + {0x34, check_texture_addr3}, + {0x35, check_texture_addr3}, + {0x36, check_texture_addr3}, + {0x4B, check_texture_addr5}, + {0x4C, check_texture_addr6}, + {0x51, check_texture_addr7}, + {0x52, check_texture_addr8}, + {0x77, check_texture_addr2}, + {0x78, no_check}, + {0x79, no_check}, + {0x7A, no_check}, + {0x7B, check_texture_addr_mode}, + {0x7C, no_check}, + {0x7D, no_check}, + {0x7E, no_check}, + {0x7F, no_check}, + {0x80, no_check}, + {0x81, no_check}, + {0x82, no_check}, + {0x83, no_check}, + {0x85, no_check}, + {0x86, no_check}, + {0x87, no_check}, + {0x88, no_check}, + {0x89, no_check}, + {0x8A, no_check}, + {0x90, no_check}, + {0x91, no_check}, + {0x92, no_check}, + {0x93, no_check} +}; + +static hz_init_t init_table3[] = { + {0xf2, check_for_header2_err}, + {0xf0, check_for_header1_err}, + {0xcc, check_for_dummy}, + {0x00, check_number_texunits} +}; + +static hazard_t table1[256]; +static hazard_t table2[256]; +static hazard_t table3[256]; + +static __inline__ int +eat_words(const uint32_t ** buf, const uint32_t * buf_end, unsigned num_words) +{ + if ((buf_end - *buf) >= num_words) { + *buf += num_words; + return 0; + } + DRM_ERROR("Illegal termination of DMA command buffer\n"); + return 1; +} + +/* + * Partially stolen from drm_memory.h + */ + +static __inline__ drm_local_map_t *via_drm_lookup_agp_map(drm_via_state_t * seq, + unsigned long offset, + unsigned long size, + struct drm_device + *dev) +{ +#ifdef __linux__ + struct drm_map_list *r_list; +#endif + drm_local_map_t *map = seq->map_cache; + + if (map && map->offset <= offset + && (offset + size) <= (map->offset + map->size)) { + return map; + } +#ifdef __linux__ + list_for_each_entry(r_list, &dev->maplist, head) { + map = r_list->map; + if (!map) + continue; +#else + TAILQ_FOREACH(map, &dev->maplist, link) { +#endif + if (map->offset <= offset + && (offset + size) <= (map->offset + map->size) + && !(map->flags & _DRM_RESTRICTED) + && (map->type == _DRM_AGP)) { + seq->map_cache = map; + return map; + } + } + return NULL; +} + +/* + * Require that all AGP texture levels reside in the same AGP map which should + * be mappable by the client. This is not a big restriction. + * FIXME: To actually enforce this security policy strictly, drm_rmmap + * would have to wait for dma quiescent before removing an AGP map. + * The via_drm_lookup_agp_map call in reality seems to take + * very little CPU time. + */ + +static __inline__ int finish_current_sequence(drm_via_state_t * cur_seq) +{ + switch (cur_seq->unfinished) { + case z_address: + DRM_DEBUG("Z Buffer start address is 0x%x\n", cur_seq->z_addr); + break; + case dest_address: + DRM_DEBUG("Destination start address is 0x%x\n", + cur_seq->d_addr); + break; + case tex_address: + break; + default: + break; + } + cur_seq->unfinished = no_sequence; + return 0; +} + +static __inline__ int +investigate_hazard(uint32_t cmd, hazard_t hz, drm_via_state_t * cur_seq) +{ + register uint32_t tmp, *tmp_addr; + + if (cur_seq->unfinished && (cur_seq->unfinished != seqs[hz])) { + int ret; + if ((ret = finish_current_sequence(cur_seq))) + return ret; + } + + switch (hz) { + case check_for_header2: + if (cmd == HALCYON_HEADER2) + return 1; + return 0; + case check_for_header1: + if ((cmd & HALCYON_HEADER1MASK) == HALCYON_HEADER1) + return 1; + return 0; + case check_for_header2_err: + if (cmd == HALCYON_HEADER2) + return 1; + DRM_ERROR("Illegal DMA HALCYON_HEADER2 command\n"); + break; + case check_for_header1_err: + if ((cmd & HALCYON_HEADER1MASK) == HALCYON_HEADER1) + return 1; + DRM_ERROR("Illegal DMA HALCYON_HEADER1 command\n"); + break; + case check_for_fire: + if ((cmd & HALCYON_FIREMASK) == HALCYON_FIRECMD) + return 1; + DRM_ERROR("Illegal DMA HALCYON_FIRECMD command\n"); + break; + case check_for_dummy: + if (HC_DUMMY == cmd) + return 0; + DRM_ERROR("Illegal DMA HC_DUMMY command\n"); + break; + case check_for_dd: + if (0xdddddddd == cmd) + return 0; + DRM_ERROR("Illegal DMA 0xdddddddd command\n"); + break; + case check_z_buffer_addr0: + cur_seq->unfinished = z_address; + cur_seq->z_addr = (cur_seq->z_addr & 0xFF000000) | + (cmd & 0x00FFFFFF); + return 0; + case check_z_buffer_addr1: + cur_seq->unfinished = z_address; + cur_seq->z_addr = (cur_seq->z_addr & 0x00FFFFFF) | + ((cmd & 0xFF) << 24); + return 0; + case check_z_buffer_addr_mode: + cur_seq->unfinished = z_address; + if ((cmd & 0x0000C000) == 0) + return 0; + DRM_ERROR("Attempt to place Z buffer in system memory\n"); + return 2; + case check_destination_addr0: + cur_seq->unfinished = dest_address; + cur_seq->d_addr = (cur_seq->d_addr & 0xFF000000) | + (cmd & 0x00FFFFFF); + return 0; + case check_destination_addr1: + cur_seq->unfinished = dest_address; + cur_seq->d_addr = (cur_seq->d_addr & 0x00FFFFFF) | + ((cmd & 0xFF) << 24); + return 0; + case check_destination_addr_mode: + cur_seq->unfinished = dest_address; + if ((cmd & 0x0000C000) == 0) + return 0; + DRM_ERROR + ("Attempt to place 3D drawing buffer in system memory\n"); + return 2; + case check_texture_addr0: + cur_seq->unfinished = tex_address; + tmp = (cmd >> 24); + tmp_addr = &cur_seq->t_addr[cur_seq->texture][tmp]; + *tmp_addr = (*tmp_addr & 0xFF000000) | (cmd & 0x00FFFFFF); + return 0; + case check_texture_addr1: + cur_seq->unfinished = tex_address; + tmp = ((cmd >> 24) - 0x20); + tmp += tmp << 1; + tmp_addr = &cur_seq->t_addr[cur_seq->texture][tmp]; + *tmp_addr = (*tmp_addr & 0x00FFFFFF) | ((cmd & 0xFF) << 24); + tmp_addr++; + *tmp_addr = (*tmp_addr & 0x00FFFFFF) | ((cmd & 0xFF00) << 16); + tmp_addr++; + *tmp_addr = (*tmp_addr & 0x00FFFFFF) | ((cmd & 0xFF0000) << 8); + return 0; + case check_texture_addr2: + cur_seq->unfinished = tex_address; + cur_seq->tex_level_lo[tmp = cur_seq->texture] = cmd & 0x3F; + cur_seq->tex_level_hi[tmp] = (cmd & 0xFC0) >> 6; + return 0; + case check_texture_addr3: + cur_seq->unfinished = tex_address; + tmp = ((cmd >> 24) - HC_SubA_HTXnL0Pit); + if (tmp == 0 && (cmd & HC_HTXnEnPit_MASK)) { + cur_seq->pitch[cur_seq->texture][tmp] = + (cmd & HC_HTXnLnPit_MASK); + cur_seq->tex_npot[cur_seq->texture] = 1; + } else { + cur_seq->pitch[cur_seq->texture][tmp] = + (cmd & HC_HTXnLnPitE_MASK) >> HC_HTXnLnPitE_SHIFT; + cur_seq->tex_npot[cur_seq->texture] = 0; + if (cmd & 0x000FFFFF) { + DRM_ERROR + ("Unimplemented texture level 0 pitch mode.\n"); + return 2; + } + } + return 0; + case check_texture_addr4: + cur_seq->unfinished = tex_address; + tmp_addr = &cur_seq->t_addr[cur_seq->texture][9]; + *tmp_addr = (*tmp_addr & 0x00FFFFFF) | ((cmd & 0xFF) << 24); + return 0; + case check_texture_addr5: + case check_texture_addr6: + cur_seq->unfinished = tex_address; + /* + * Texture width. We don't care since we have the pitch. + */ + return 0; + case check_texture_addr7: + cur_seq->unfinished = tex_address; + tmp_addr = &(cur_seq->height[cur_seq->texture][0]); + tmp_addr[5] = 1 << ((cmd & 0x00F00000) >> 20); + tmp_addr[4] = 1 << ((cmd & 0x000F0000) >> 16); + tmp_addr[3] = 1 << ((cmd & 0x0000F000) >> 12); + tmp_addr[2] = 1 << ((cmd & 0x00000F00) >> 8); + tmp_addr[1] = 1 << ((cmd & 0x000000F0) >> 4); + tmp_addr[0] = 1 << (cmd & 0x0000000F); + return 0; + case check_texture_addr8: + cur_seq->unfinished = tex_address; + tmp_addr = &(cur_seq->height[cur_seq->texture][0]); + tmp_addr[9] = 1 << ((cmd & 0x0000F000) >> 12); + tmp_addr[8] = 1 << ((cmd & 0x00000F00) >> 8); + tmp_addr[7] = 1 << ((cmd & 0x000000F0) >> 4); + tmp_addr[6] = 1 << (cmd & 0x0000000F); + return 0; + case check_texture_addr_mode: + cur_seq->unfinished = tex_address; + if (2 == (tmp = cmd & 0x00000003)) { + DRM_ERROR + ("Attempt to fetch texture from system memory.\n"); + return 2; + } + cur_seq->agp_texture = (tmp == 3); + cur_seq->tex_palette_size[cur_seq->texture] = + (cmd >> 16) & 0x000000007; + return 0; + case check_for_vertex_count: + cur_seq->vertex_count = cmd & 0x0000FFFF; + return 0; + case check_number_texunits: + cur_seq->multitex = (cmd >> 3) & 1; + return 0; + default: + DRM_ERROR("Illegal DMA data: 0x%08x\n", cmd); + return 2; + } + return 2; +} + +static __inline__ int +via_check_prim_list(uint32_t const **buffer, const uint32_t * buf_end, + drm_via_state_t * cur_seq) +{ + struct drm_via_private *dev_priv = + (struct drm_via_private *)cur_seq->dev->dev_private; + uint32_t a_fire, bcmd, dw_count; + int ret = 0; + int have_fire; + const uint32_t *buf = *buffer; + + while (buf < buf_end) { + have_fire = 0; + if ((buf_end - buf) < 2) { + DRM_ERROR + ("Unexpected termination of primitive list.\n"); + ret = 1; + break; + } + if ((*buf & HC_ACMD_MASK) != HC_ACMD_HCmdB) + break; + bcmd = *buf++; + if ((*buf & HC_ACMD_MASK) != HC_ACMD_HCmdA) { + DRM_ERROR("Expected Vertex List A command, got 0x%x\n", + *buf); + ret = 1; + break; + } + a_fire = + *buf++ | HC_HPLEND_MASK | HC_HPMValidN_MASK | + HC_HE3Fire_MASK; + + /* + * How many dwords per vertex ? + */ + + if (cur_seq->agp && ((bcmd & (0xF << 11)) == 0)) { + DRM_ERROR("Illegal B command vertex data for AGP.\n"); + ret = 1; + break; + } + + dw_count = 0; + if (bcmd & (1 << 7)) + dw_count += (cur_seq->multitex) ? 2 : 1; + if (bcmd & (1 << 8)) + dw_count += (cur_seq->multitex) ? 2 : 1; + if (bcmd & (1 << 9)) + dw_count++; + if (bcmd & (1 << 10)) + dw_count++; + if (bcmd & (1 << 11)) + dw_count++; + if (bcmd & (1 << 12)) + dw_count++; + if (bcmd & (1 << 13)) + dw_count++; + if (bcmd & (1 << 14)) + dw_count++; + + while (buf < buf_end) { + if (*buf == a_fire) { + if (dev_priv->num_fire_offsets >= + VIA_FIRE_BUF_SIZE) { + DRM_ERROR("Fire offset buffer full.\n"); + ret = 1; + break; + } + dev_priv->fire_offsets[dev_priv-> + num_fire_offsets++] = + buf; + have_fire = 1; + buf++; + if (buf < buf_end && *buf == a_fire) + buf++; + break; + } + if ((*buf == HALCYON_HEADER2) || + ((*buf & HALCYON_FIREMASK) == HALCYON_FIRECMD)) { + DRM_ERROR("Missing Vertex Fire command, " + "Stray Vertex Fire command or verifier " + "lost sync.\n"); + ret = 1; + break; + } + if ((ret = eat_words(&buf, buf_end, dw_count))) + break; + } + if (buf >= buf_end && !have_fire) { + DRM_ERROR("Missing Vertex Fire command or verifier " + "lost sync.\n"); + ret = 1; + break; + } + if (cur_seq->agp && ((buf - cur_seq->buf_start) & 0x01)) { + DRM_ERROR("AGP Primitive list end misaligned.\n"); + ret = 1; + break; + } + } + *buffer = buf; + return ret; +} + +static __inline__ verifier_state_t +via_check_header2(uint32_t const **buffer, const uint32_t * buf_end, + drm_via_state_t * hc_state) +{ + uint32_t cmd; + int hz_mode; + hazard_t hz; + const uint32_t *buf = *buffer; + const hazard_t *hz_table; + + if ((buf_end - buf) < 2) { + DRM_ERROR + ("Illegal termination of DMA HALCYON_HEADER2 sequence.\n"); + return state_error; + } + buf++; + cmd = (*buf++ & 0xFFFF0000) >> 16; + + switch (cmd) { + case HC_ParaType_CmdVdata: + if (via_check_prim_list(&buf, buf_end, hc_state)) + return state_error; + *buffer = buf; + return state_command; + case HC_ParaType_NotTex: + hz_table = table1; + break; + case HC_ParaType_Tex: + hc_state->texture = 0; + hz_table = table2; + break; + case (HC_ParaType_Tex | (HC_SubType_Tex1 << 8)): + hc_state->texture = 1; + hz_table = table2; + break; + case (HC_ParaType_Tex | (HC_SubType_TexGeneral << 8)): + hz_table = table3; + break; + case HC_ParaType_Auto: + if (eat_words(&buf, buf_end, 2)) + return state_error; + *buffer = buf; + return state_command; + case (HC_ParaType_Palette | (HC_SubType_Stipple << 8)): + if (eat_words(&buf, buf_end, 32)) + return state_error; + *buffer = buf; + return state_command; + case (HC_ParaType_Palette | (HC_SubType_TexPalette0 << 8)): + case (HC_ParaType_Palette | (HC_SubType_TexPalette1 << 8)): + DRM_ERROR("Texture palettes are rejected because of " + "lack of info how to determine their size.\n"); + return state_error; + case (HC_ParaType_Palette | (HC_SubType_FogTable << 8)): + DRM_ERROR("Fog factor palettes are rejected because of " + "lack of info how to determine their size.\n"); + return state_error; + default: + + /* + * There are some unimplemented HC_ParaTypes here, that + * need to be implemented if the Mesa driver is extended. + */ + + DRM_ERROR("Invalid or unimplemented HALCYON_HEADER2 " + "DMA subcommand: 0x%x. Previous dword: 0x%x\n", + cmd, *(buf - 2)); + *buffer = buf; + return state_error; + } + + while (buf < buf_end) { + cmd = *buf++; + if ((hz = hz_table[cmd >> 24])) { + if ((hz_mode = investigate_hazard(cmd, hz, hc_state))) { + if (hz_mode == 1) { + buf--; + break; + } + return state_error; + } + } else if (hc_state->unfinished && + finish_current_sequence(hc_state)) { + return state_error; + } + } + if (hc_state->unfinished && finish_current_sequence(hc_state)) { + return state_error; + } + *buffer = buf; + return state_command; +} + +static __inline__ verifier_state_t +via_parse_header2(struct drm_via_private *dev_priv, uint32_t const **buffer, + const uint32_t * buf_end, int *fire_count) +{ + uint32_t cmd; + const uint32_t *buf = *buffer; + const uint32_t *next_fire; + int burst = 0; + + next_fire = dev_priv->fire_offsets[*fire_count]; + buf++; + cmd = (*buf & 0xFFFF0000) >> 16; + VIA_WRITE(HC_REG_TRANS_SET + HC_REG_BASE, *buf++); + switch (cmd) { + case HC_ParaType_CmdVdata: + while ((buf < buf_end) && + (*fire_count < dev_priv->num_fire_offsets) && + (*buf & HC_ACMD_MASK) == HC_ACMD_HCmdB) { + while (buf <= next_fire) { + VIA_WRITE(HC_REG_TRANS_SPACE + HC_REG_BASE + + (burst & 63), *buf++); + burst += 4; + } + if ((buf < buf_end) + && ((*buf & HALCYON_FIREMASK) == HALCYON_FIRECMD)) + buf++; + + if (++(*fire_count) < dev_priv->num_fire_offsets) + next_fire = dev_priv->fire_offsets[*fire_count]; + } + break; + default: + while (buf < buf_end) { + + if (*buf == HC_HEADER2 || + (*buf & HALCYON_HEADER1MASK) == HALCYON_HEADER1 || + (*buf & VIA_VIDEOMASK) == VIA_VIDEO_HEADER5 || + (*buf & VIA_VIDEOMASK) == VIA_VIDEO_HEADER6) + break; + + VIA_WRITE(HC_REG_TRANS_SPACE + HC_REG_BASE + + (burst & 63), *buf++); + burst += 4; + } + } + *buffer = buf; + return state_command; +} + +static __inline__ int verify_mmio_address(uint32_t address) +{ + if ((address > 0x3FF) && (address < 0xC00)) { + DRM_ERROR("Invalid VIDEO DMA command. " + "Attempt to access 3D- or command burst area.\n"); + return 1; + } else if ((address > 0xCFF) && (address < 0x1300)) { + DRM_ERROR("Invalid VIDEO DMA command. " + "Attempt to access PCI DMA area.\n"); + return 1; + } else if (address > 0x13FF) { + DRM_ERROR("Invalid VIDEO DMA command. " + "Attempt to access VGA registers.\n"); + return 1; + } + return 0; +} + +static __inline__ int +verify_video_tail(uint32_t const **buffer, const uint32_t * buf_end, + uint32_t dwords) +{ + const uint32_t *buf = *buffer; + + if (buf_end - buf < dwords) { + DRM_ERROR("Illegal termination of video command.\n"); + return 1; + } + while (dwords--) { + if (*buf++) { + DRM_ERROR("Illegal video command tail.\n"); + return 1; + } + } + *buffer = buf; + return 0; +} + +static __inline__ verifier_state_t +via_check_header1(uint32_t const **buffer, const uint32_t * buf_end) +{ + uint32_t cmd; + const uint32_t *buf = *buffer; + verifier_state_t ret = state_command; + + while (buf < buf_end) { + cmd = *buf; + if ((cmd > ((0x3FF >> 2) | HALCYON_HEADER1)) && + (cmd < ((0xC00 >> 2) | HALCYON_HEADER1))) { + if ((cmd & HALCYON_HEADER1MASK) != HALCYON_HEADER1) + break; + DRM_ERROR("Invalid HALCYON_HEADER1 command. " + "Attempt to access 3D- or command burst area.\n"); + ret = state_error; + break; + } else if (cmd > ((0xCFF >> 2) | HALCYON_HEADER1)) { + if ((cmd & HALCYON_HEADER1MASK) != HALCYON_HEADER1) + break; + DRM_ERROR("Invalid HALCYON_HEADER1 command. " + "Attempt to access VGA registers.\n"); + ret = state_error; + break; + } else { + buf += 2; + } + } + *buffer = buf; + return ret; +} + +static __inline__ verifier_state_t +via_parse_header1(struct drm_via_private *dev_priv, uint32_t const **buffer, + const uint32_t * buf_end) +{ + register uint32_t cmd = VIA_REG_GECMD; + const uint32_t *buf = *buffer; + + while (buf < buf_end) { + + /* + * Wait idle to avoid lenghty PCI stalls. + * There is no on-chip queue for these MMIO commands, so + * without this idle wait, the chip will simply + * stall the PCI bus until the engines are idle. + */ + + if (unlikely(cmd == VIA_REG_GECMD)) + via_wait_idle(dev_priv); + + cmd = *buf; + if ((cmd & HALCYON_HEADER1MASK) != HALCYON_HEADER1) + break; + cmd &= ~HALCYON_HEADER1MASK; + VIA_WRITE(cmd << 2, *++buf); + buf++; + } + *buffer = buf; + return state_command; +} + +static __inline__ verifier_state_t +via_check_vheader5(uint32_t const **buffer, const uint32_t * buf_end) +{ + uint32_t data; + const uint32_t *buf = *buffer; + + if (buf_end - buf < 4) { + DRM_ERROR("Illegal termination of video header5 command\n"); + return state_error; + } + + data = *buf++ & ~VIA_VIDEOMASK; + if (verify_mmio_address(data)) + return state_error; + + data = *buf++; + if (*buf++ != 0x00F50000) { + DRM_ERROR("Illegal header5 header data\n"); + return state_error; + } + if (*buf++ != 0x00000000) { + DRM_ERROR("Illegal header5 header data\n"); + return state_error; + } + if (eat_words(&buf, buf_end, data)) + return state_error; + if ((data & 3) && verify_video_tail(&buf, buf_end, 4 - (data & 3))) + return state_error; + *buffer = buf; + return state_command; + +} + +static __inline__ verifier_state_t +via_parse_vheader5(struct drm_via_private *dev_priv, uint32_t const **buffer, + const uint32_t * buf_end) +{ + uint32_t addr, count, i; + const uint32_t *buf = *buffer; + + addr = *buf++ & ~VIA_VIDEOMASK; + i = count = *buf; + buf += 3; + while (i--) { + VIA_WRITE(addr, *buf++); + } + if (count & 3) + buf += 4 - (count & 3); + *buffer = buf; + return state_command; +} + +static __inline__ verifier_state_t +via_check_vheader6(uint32_t const **buffer, const uint32_t * buf_end) +{ + uint32_t data; + const uint32_t *buf = *buffer; + uint32_t i; + + if (buf_end - buf < 4) { + DRM_ERROR("Illegal termination of video header6 command\n"); + return state_error; + } + buf++; + data = *buf++; + if (*buf++ != 0x00F60000) { + DRM_ERROR("Illegal header6 header data\n"); + return state_error; + } + if (*buf++ != 0x00000000) { + DRM_ERROR("Illegal header6 header data\n"); + return state_error; + } + if ((buf_end - buf) < (data << 1)) { + DRM_ERROR("Illegal termination of video header6 command\n"); + return state_error; + } + for (i = 0; i < data; ++i) { + if (verify_mmio_address(*buf++)) + return state_error; + buf++; + } + data <<= 1; + if ((data & 3) && verify_video_tail(&buf, buf_end, 4 - (data & 3))) + return state_error; + *buffer = buf; + return state_command; +} + +static __inline__ verifier_state_t +via_parse_vheader6(struct drm_via_private *dev_priv, uint32_t const **buffer, + const uint32_t * buf_end) +{ + + uint32_t addr, count, i; + const uint32_t *buf = *buffer; + + i = count = *++buf; + buf += 3; + while (i--) { + addr = *buf++; + VIA_WRITE(addr, *buf++); + } + count <<= 1; + if (count & 3) + buf += 4 - (count & 3); + *buffer = buf; + return state_command; +} + +int +via_verify_command_stream(const uint32_t * buf, unsigned int size, + struct drm_device *dev, int agp) +{ + + struct drm_via_private *dev_priv = via_priv(dev); + drm_via_state_t *hc_state = &dev_priv->hc_state; + drm_via_state_t saved_state = *hc_state; + uint32_t cmd; + const uint32_t *buf_end = buf + (size >> 2); + verifier_state_t state = state_command; + int cme_video; + int supported_3d; + + /* + * For simplicity, support CME video headers on the PCI + * parser on all hardware. + */ + + cme_video = (dev_priv->chipset == VIA_PRO_GROUP_A || + dev_priv->chipset == VIA_DX9_0 || !agp); + + supported_3d = dev_priv->chipset != VIA_DX9_0; + + hc_state->dev = dev; + hc_state->unfinished = no_sequence; + hc_state->map_cache = NULL; + hc_state->agp = agp; + hc_state->buf_start = buf; + dev_priv->num_fire_offsets = 0; + + while (buf < buf_end) { + + switch (state) { + case state_header2: + state = via_check_header2(&buf, buf_end, hc_state); + break; + case state_header1: + state = via_check_header1(&buf, buf_end); + break; + case state_vheader5: + state = via_check_vheader5(&buf, buf_end); + break; + case state_vheader6: + state = via_check_vheader6(&buf, buf_end); + break; + case state_command: + if ((HALCYON_HEADER2 == (cmd = *buf)) && supported_3d) + state = state_header2; + else if ((cmd & HALCYON_HEADER1MASK) == HALCYON_HEADER1) + state = state_header1; + else if (cme_video + && (cmd & VIA_VIDEOMASK) == VIA_VIDEO_HEADER5) + state = state_vheader5; + else if (cme_video + && (cmd & VIA_VIDEOMASK) == VIA_VIDEO_HEADER6) + state = state_vheader6; + else if ((cmd == HALCYON_HEADER2) && !supported_3d) { + DRM_ERROR + ("Accelerated 3D is not supported on this chipset yet.\n"); + state = state_error; + } else { + DRM_ERROR + ("Invalid / Unimplemented DMA HEADER command. 0x%x\n", + cmd); + state = state_error; + } + break; + case state_error: + default: + *hc_state = saved_state; + return -EINVAL; + } + } + if (state == state_error) { + *hc_state = saved_state; + return -EINVAL; + } + return 0; +} + +int +via_parse_command_stream(struct drm_device *dev, const uint32_t * buf, + unsigned int size) +{ + + struct drm_via_private *dev_priv = via_priv(dev); + uint32_t cmd; + const uint32_t *buf_end = buf + (size >> 2); + verifier_state_t state = state_command; + int fire_count = 0; + + while (buf < buf_end) { + + switch (state) { + case state_header2: + state = + via_parse_header2(dev_priv, &buf, buf_end, + &fire_count); + break; + case state_header1: + state = via_parse_header1(dev_priv, &buf, buf_end); + break; + case state_vheader5: + state = via_parse_vheader5(dev_priv, &buf, buf_end); + break; + case state_vheader6: + state = via_parse_vheader6(dev_priv, &buf, buf_end); + break; + case state_command: + if (HALCYON_HEADER2 == (cmd = *buf)) + state = state_header2; + else if ((cmd & HALCYON_HEADER1MASK) == HALCYON_HEADER1) + state = state_header1; + else if ((cmd & VIA_VIDEOMASK) == VIA_VIDEO_HEADER5) + state = state_vheader5; + else if ((cmd & VIA_VIDEOMASK) == VIA_VIDEO_HEADER6) + state = state_vheader6; + else { + DRM_ERROR + ("Invalid / Unimplemented DMA HEADER command. 0x%x\n", + cmd); + state = state_error; + } + break; + case state_error: + default: + return -EINVAL; + } + } + if (state == state_error) { + return -EINVAL; + } + return 0; +} + +static void +setup_hazard_table(hz_init_t init_table[], hazard_t table[], int size) +{ + int i; + + for (i = 0; i < 256; ++i) { + table[i] = forbidden_command; + } + + for (i = 0; i < size; ++i) { + table[init_table[i].code] = init_table[i].hz; + } +} + +void via_init_command_verifier(void) +{ + setup_hazard_table(init_table1, table1, + sizeof(init_table1) / sizeof(hz_init_t)); + setup_hazard_table(init_table2, table2, + sizeof(init_table2) / sizeof(hz_init_t)); + setup_hazard_table(init_table3, table3, + sizeof(init_table3) / sizeof(hz_init_t)); +} diff --git a/linux-core/openchrome/via_verifier.h b/linux-core/openchrome/via_verifier.h new file mode 100644 index 00000000..56c3523c --- /dev/null +++ b/linux-core/openchrome/via_verifier.h @@ -0,0 +1,62 @@ +/* + * Copyright 2004 The Unichrome Project. 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, sub license, + * 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 (including the + * next paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL + * THE UNICHROME PROJECT, AND/OR ITS SUPPLIERS 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. + * + * Author: Thomas Hellström 2004. + */ + +#ifndef _VIA_VERIFIER_H_ +#define _VIA_VERIFIER_H_ + +typedef enum { + no_sequence = 0, + z_address, + dest_address, + tex_address +} drm_via_sequence_t; + +typedef struct { + unsigned texture; + uint32_t z_addr; + uint32_t d_addr; + uint32_t t_addr[2][12]; + uint32_t pitch[2][12]; + uint32_t height[2][12]; + uint32_t tex_level_lo[2]; + uint32_t tex_level_hi[2]; + uint32_t tex_palette_size[2]; + uint32_t tex_npot[2]; + drm_via_sequence_t unfinished; + int agp_texture; + int multitex; + struct drm_device *dev; + drm_local_map_t *map_cache; + uint32_t vertex_count; + int agp; + const uint32_t *buf_start; +} drm_via_state_t; + +extern int via_verify_command_stream(const uint32_t * buf, unsigned int size, + struct drm_device *dev, int agp); +extern int via_parse_command_stream(struct drm_device *dev, + const uint32_t * buf, unsigned int size); + +#endif diff --git a/linux-core/openchrome/via_video.c b/linux-core/openchrome/via_video.c new file mode 100644 index 00000000..c8cfb3f8 --- /dev/null +++ b/linux-core/openchrome/via_video.c @@ -0,0 +1,105 @@ +/* + * Copyright 2005 Thomas Hellstrom. 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, sub license, + * 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 (including the + * next paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHOR(S), AND/OR THE COPYRIGHT HOLDER(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. + * + * Author: Thomas Hellstrom 2005. + * + * Video and XvMC related functions. + */ + +#include "drmP.h" +#include "ochr_drm.h" +#include "via_drv.h" +#include "drm_sarea.h" + +void via_init_futex(struct drm_via_private *dev_priv) +{ + unsigned int i; + volatile struct via_sarea *sa_priv = dev_priv->sarea_priv; + + DRM_DEBUG("\n"); + + for (i = 0; i < DRM_VIA_NR_XVMC_LOCKS; ++i) { + DRM_INIT_WAITQUEUE(&(dev_priv->decoder_queue[i])); + DRM_VIA_XVMCLOCKPTR(&sa_priv->sa_xvmc, i)->lock = 0; + } +} + +void via_release_futex(struct drm_via_private *dev_priv, int context) +{ + unsigned int i; + volatile int *lock; + volatile struct via_sarea *sa_priv = dev_priv->sarea_priv; + + if (!dev_priv->sarea_priv) + return; + + for (i = 0; i < DRM_VIA_NR_XVMC_LOCKS; ++i) { + lock = + (volatile int *)DRM_VIA_XVMCLOCKPTR(&sa_priv->sa_xvmc, i); + if ((_DRM_LOCKING_CONTEXT(*lock) == context)) { + if (_DRM_LOCK_IS_HELD(*lock) + && (*lock & _DRM_LOCK_CONT)) { + DRM_WAKEUP(&(dev_priv->decoder_queue[i])); + } + *lock = 0; + } + } +} + +int via_decoder_futex(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_via_futex *fx = data; + volatile int *lock; + struct drm_via_private *dev_priv = via_priv(dev); + struct via_sarea *sa_priv = dev_priv->sarea_priv; + int ret = 0; + + DRM_DEBUG("\n"); + if (unlikely(sa_priv == NULL)) { + dev_priv->sarea = drm_getsarea(dev); + if (!dev_priv->sarea) { + DRM_ERROR("Could not find sarea.\n"); + return -EINVAL; + } + dev_priv->sarea_priv = + (struct via_sarea *)((u8 *) dev_priv->sarea->handle + + sizeof(struct drm_sarea)); + sa_priv = dev_priv->sarea_priv; + } + + if (fx->lock > DRM_VIA_NR_XVMC_LOCKS) + return -EFAULT; + + lock = (volatile int *)DRM_VIA_XVMCLOCKPTR(&sa_priv->sa_xvmc, fx->lock); + + switch (fx->op) { + case VIA_FUTEX_WAIT: + DRM_WAIT_ON(ret, dev_priv->decoder_queue[fx->lock], + (fx->ms / 10) * (DRM_HZ / 100), *lock != fx->val); + return ret; + case VIA_FUTEX_WAKE: + DRM_WAKEUP(&(dev_priv->decoder_queue[fx->lock])); + return 0; + } + return 0; +} diff --git a/shared-core/Makefile.am b/shared-core/Makefile.am index 5b65bc67..0593ae23 100644 --- a/shared-core/Makefile.am +++ b/shared-core/Makefile.am @@ -39,4 +39,6 @@ klibdrminclude_HEADERS = \ via_drm.h \ r300_reg.h \ via_3d_reg.h \ - xgi_drm.h + xgi_drm.h \ + ochr_drm.h + diff --git a/shared-core/ochr_drm.h b/shared-core/ochr_drm.h new file mode 100644 index 00000000..64fcb5b9 --- /dev/null +++ b/shared-core/ochr_drm.h @@ -0,0 +1,472 @@ +/* + * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved. + * Copyright 2001-2003 S3 Graphics, Inc. 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, sub license, + * 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 (including the + * next paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL + * VIA, S3 GRAPHICS, AND/OR ITS SUPPLIERS 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. + */ +#ifndef _OCHR_DRM_H_ +#define _OCHR_DRM_H_ +#include +#include + +/* + * With the arrival of libdrm there is a need to version this file. + * As usual, bump MINOR for new features, MAJOR for changes that create + * backwards incompatibilities, (which should be avoided whenever possible). + */ + +#define DRM_VIA_DRIVER_DATE "20090119" + +#define DRM_VIA_DRIVER_MAJOR 0 +#define DRM_VIA_DRIVER_MINOR 1 +#define DRM_VIA_DRIVER_PATCHLEVEL 0 +#define DRM_VIA_DRIVER_VERSION (((VIA_DRM_DRIVER_MAJOR) << 16) | (VIA_DRM_DRIVER_MINOR)) + +#define DRM_VIA_MAX_MIP 16 +#define DRM_VIA_NR_SCANOUTS 4 +#define DRM_VIA_NR_XVMC_PORTS 10 +#define DRM_VIA_NR_XVMC_LOCKS 5 +#define DRM_VIA_MAX_CACHELINE_SIZE 64 +#define DRM_VIA_XVMCLOCKPTR(saPriv,lockNo) \ + ((volatile struct drm_hw_lock *)(((((unsigned long) (saPriv)->xvmc_lock_area) + \ + (DRM_VIA_MAX_CACHELINE_SIZE - 1)) & \ + ~(DRM_VIA_MAX_CACHELINE_SIZE - 1)) + \ + DRM_VIA_MAX_CACHELINE_SIZE*(lockNo))) + +/* VIA specific ioctls */ +#define DRM_VIA_VT 0x00 +#define DRM_VIA_GET_PARAM 0x01 +#define DRM_VIA_EXTENSION 0x02 + +#define DRM_IOCTL_VIA_VT DRM_IOW( DRM_COMMAND_BASE + DRM_VIA_VT, struct drm_via_vt) +#define DRM_IOCTL_VIA_GET_PARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_GET_PARAM, struct drm_via_getparam_arg) +#define DRM_IOCTL_VIA_EXTENSION DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_EXTENSION, struct drm_via_extension_arg) + +/* + * TTM Ioctls. + */ + +/* Indices into buf.Setup where various bits of state are mirrored per + * context and per buffer. These can be fired at the card as a unit, + * or in a piecewise fashion as required. + */ + +struct drm_via_futex { + uint32_t ms; + uint32_t lock; + uint32_t val; + enum { + VIA_FUTEX_WAIT = 0x00, + VIA_FUTEX_WAKE = 0X01 + } op; +}; + +/** + * struct drm_via_vt + * + * @enter: 1 for entervt, 0 for leavevt. + * + * Argument to the DRM_VIA_VT ioctl. + */ + +struct drm_via_vt { + int32_t enter; +}; + +/** + * struct drm_via_scanout + * + * @stamp: sequence number identifying last change. + * @handle: buffer object handle + * @width: scanout buffer width + * @height: scanout buffer height + * @stride: scanout buffer stride in bytes + * @depth: scanout buffer depth. 15, 16 or 32. + * + * Part of the shared memory area. + */ + +struct drm_via_scanout { + uint64_t stamp; + uint32_t handle; + uint32_t width; + uint32_t height; + uint32_t stride; + uint32_t depth; + uint32_t pad64; +}; + +/** + * struct drm_via_sarea_xvmc + * + * @xvmc_lock_area: Area for XvMC decoder locks. + * @xvmc_displaying: Currently displaying surface. + * @xvmc_sub_pic_on: 1: subpicture on, 0: subpicture off. + * @xvmc_ctx: Last context to upload Mpeg state + * + * The xvmc part of the driver-specific sarea. The offset of this structure + * into the driver specific sarea can be obtained using the extension + * ioctl. + */ + +struct drm_via_sarea_xvmc { + char xvmc_lock_area[DRM_VIA_MAX_CACHELINE_SIZE * + (DRM_VIA_NR_XVMC_LOCKS + 1)]; + uint32_t xvmc_displaying[DRM_VIA_NR_XVMC_PORTS]; + uint32_t xvmc_sub_pic_on[DRM_VIA_NR_XVMC_PORTS]; + uint32_t xvmc_ctx[DRM_VIA_NR_XVMC_LOCKS]; +}; + +/** + * struct drm_via_sarea + * + * @scanouts: Scanout buffer info. + * @pfCurrentOffset: For page-flipping. + * + * via shared memory area. + */ + +struct drm_via_sarea { + struct drm_via_scanout scanouts[DRM_VIA_NR_SCANOUTS]; + + /* + * Below is for XvMC. + * We want the lock integers alone on, and aligned to, a cache line. + * Therefore this somewhat strange construct. + */ + + /* Used by the 3d driver only at this point, for pageflipping: + */ + uint32_t pfCurrentOffset; +}; + +enum drm_via_params { + DRM_VIA_PARAM_VRAM_SIZE, + DRM_VIA_PARAM_TT_SIZE, + DRM_VIA_PARAM_AGP_SIZE, + DRM_VIA_PARAM_HAS_IRQ, + DRM_VIA_PARAM_SAREA_SIZE, +}; + +/** + * struct drm_via_getparam_arg + * + * @value: Returned value. + * @param: Requested parameter. (int) enum drm_via_params + * + * Argument to the DRM_VIA_GET_PARAM Ioctl. + */ + +struct drm_via_getparam_arg { + uint64_t value; + uint32_t param; + uint32_t pad64; +}; + +/** + * struct drm_via_extension_rep + * + * @exists: extension exists. + * @driver_ioctl_offset: Driver ioctl number of first ioctl in the extension. + * @major: Major version of the extension. + * @minor: Minor version of the extension. + * @pl: Patchlevel version of the extension. + * + * Output from the DRM_VIA_EXTENSION ioctl. + */ + +struct drm_via_extension_rep { + int32_t exists; + uint32_t driver_ioctl_offset; + uint32_t driver_sarea_offset; + uint32_t major; + uint32_t minor; + uint32_t pl; + uint32_t pad64; +}; + +#define DRM_VIA_EXT_NAME_LEN 128 + +/** + * union drm_via_extension_arg + * + * @extension: Input: Name of the extension. + * @rep: Ouput: Reply. + */ + +union drm_via_extension_arg { + char extension[DRM_VIA_EXT_NAME_LEN]; + struct drm_via_extension_rep rep; +}; + +/* + * Below is execbuf stuff. + */ + +#define VIA_RELOC_BUF_SIZE 8192 + +enum drm_via_reloc_type { + VIA_RELOC_ZBUF, + VIA_RELOC_DSTBUF, + VIA_RELOC_PF, + VIA_RELOC_2D, + VIA_RELOC_TEX, + VIA_RELOC_YUV, + VIA_RELOC_NUMTYPE +}; + +#define VIA_USE_PRESUMED (1 << 0) +#define VIA_PRESUMED_AGP (1 << 1) + +/** + * struct drm_via_validate_req + * + * @set_flags: Validation flags to set. + * @clear_flags: Validation flags to clear. + * @next: User space pointer to the next struct drm_via_validate_req in + * a linked list, cast to an uint64_t. + * @presumed_gpu_offset: Presumed gpu offset, used in the command stream, + * of the buffer. + * @presumed_flags: Flags indicating how the presumed gpu offset should be + * interpreted (and if). + * @cmdbuf_first: dword offset of the first state packet referencing this + * buffer in the command stream. Used for command stream splitting. + * @cmdbuf_last: dword offset of the last + 1 state packet referencing this + * buffer in the command stream. Used for command stream splitting. + * + * Information record used for buffer object validation prior to + * command submission. + */ + +struct drm_via_validate_req { + uint64_t set_flags; + uint64_t clear_flags; + uint64_t next; + uint64_t presumed_gpu_offset; + uint32_t buffer_handle; + uint32_t presumed_flags; + uint32_t cmdbuf_first; + uint32_t cmdbuf_last; +}; + +/** + * struct drm_via_validate_rep + * + * @gpu_offset: Last known gpu offset of the buffer. + * @placement: Last known TTM placement flag of the buffer. + * @fence_type_mask: Set of fence type flags used to determine buffer idle. + */ + +struct drm_via_validate_rep { + uint64_t gpu_offset; + uint32_t placement; + uint32_t fence_type_mask; +}; + +/** + * struct drm_via_validate_rep + */ + +struct drm_via_validate_arg { + int handled; + int ret; + union { + struct drm_via_validate_req req; + struct drm_via_validate_rep rep; + } d; +}; + +struct drm_via_reloc_header { + uint64_t next_header; + uint32_t used; + uint32_t num_relocs; +}; + +struct drm_via_reloc_bufaddr { + uint32_t index; + uint32_t delta; +}; + +/* + * Relocation types. + */ + +struct drm_via_base_reloc { + enum drm_via_reloc_type type; + uint32_t offset; +}; + +struct drm_via_yuv_reloc { + struct drm_via_base_reloc base; + struct drm_via_reloc_bufaddr addr; + int32_t planes; + uint32_t shift; + uint32_t plane_offs[4]; +}; + +struct drm_via_zbuf_reloc { + struct drm_via_base_reloc base; + struct drm_via_reloc_bufaddr addr; +}; + +struct drm_via_2d_reloc { + struct drm_via_base_reloc base; + struct drm_via_reloc_bufaddr addr; + uint32_t bpp; + uint32_t pos; +}; + +struct drm_via_texture_reloc { + struct drm_via_base_reloc base; + uint32_t low_mip; + uint32_t hi_mip; + uint32_t reg_tex_fm; + uint32_t pad64; + struct drm_via_reloc_bufaddr addr[DRM_VIA_MAX_MIP]; +}; + +/* + * Execbuf arg. + */ + +struct drm_via_ttm_fence_rep { + uint32_t handle; + uint32_t fence_class; + uint32_t fence_type; + uint32_t signaled_types; + uint32_t error; +}; + +struct drm_via_clip_rect { + int32_t x1, x2, y1, y2; +}; + +#define DRM_VIA_HAVE_CLIP (1 << 0) +#define DRM_VIA_FENCE_NO_USER (1 << 1) +#define DRM_VIA_WAIT_BARRIER (1 << 2) + +struct drm_via_ttm_execbuf_control { + struct drm_via_ttm_fence_rep rep; + uint32_t first_clip; + uint32_t vram_avail; + uint32_t agp_avail; + uint32_t pad_64; +}; + +struct drm_via_ttm_execbuf_arg { + + uint64_t buffer_list; + uint64_t reloc_list; + uint64_t cmd_buffer; + + uint64_t ls_buffer_list; + uint64_t ls_reloc_list; + uint64_t ls_buffer; + + uint64_t cliprect_addr; + uint64_t control; + + uint32_t num_buffers; + uint32_t num_ls_buffers; + uint32_t cmd_buffer_size; + uint32_t lost_state_size; + uint32_t mechanism; + uint32_t exec_flags; + + uint32_t cliprect_offset; + uint32_t num_cliprects; + + uint32_t context; + uint32_t pad64; +}; + +/* + * Flag layout in the 64-bit VIA validate + * flag argument. + */ + +#define VIA_PLACEMENT_MASK 0x000000000000FFFFULL +#define VIA_PLACEMENT_SHIFT 0 + +/* + * Additional placement domain. A pre-bound + * area of AGP memory for fast buffer object + * creation and destruction. + */ + +#define VIA_PL_FLAG_AGP TTM_PL_FLAG_PRIV0 + +#define VIA_ACCESS_MASK 0x0000000000FF0000ULL +#define VIA_ACCESS_SHIFT 16 +#define VIA_ACCESS_READ (TTM_ACCESS_READ << VIA_ACCESS_SHIFT) +#define VIA_ACCESS_WRITE (TTM_ACCESS_WRITE << VIA_ACCESS_SHIFT) + +/* + * Validation access flags that indicate that the buffer + * will be accessed by the following engines: + */ + +#define VIA_VALMODE_MASK 0x0000FFFF00000000ULL +#define VIA_VALMODE_SHIFT 32 + +#define VIA_VAL_FLAG_HQV0 (1ULL << (VIA_VALMODE_SHIFT + 0)) +#define VIA_VAL_FLAG_HQV1 (1ULL << (VIA_VALMODE_SHIFT + 1)) +#define VIA_VAL_FLAG_MPEG0 (1ULL << (VIA_VALMODE_SHIFT + 2)) +#define VIA_VAL_FLAG_MPEG1 (1ULL << (VIA_VALMODE_SHIFT + 3)) + +/* + * The command reader. + * It has two submission mechanisms that are serialized, + * and user-space can indicate when a specific mechanism + * needs to be used. + */ + +#define VIA_ENGINE_CMD 0 +#define _VIA_MECHANISM_AGP 0 +#define _VIA_MECHANISM_PCI 1 + +#define VIA_ENGINE_DMA0 1 +#define VIA_ENGINE_DMA1 2 +#define VIA_ENGINE_DMA2 3 +#define VIA_ENGINE_DMA3 4 +#define VIA_NUM_ENGINES 5 + +/* + * These fence types are defined + * for engine 0 and 1. Indicates that the + * HQV or MPEG engine is done with the + * commands associated with the fence. + */ + +#define VIA_FENCE_TYPE_HQV0 (1 << 1) +#define VIA_FENCE_TYPE_HQV1 (1 << 2) +#define VIA_FENCE_TYPE_MPEG0 (1 << 3) +#define VIA_FENCE_TYPE_MPEG1 (1 << 4) + +/* + * This fence type is for the DMA engines. + * Indicates that the device mapping for + * the system memory is released, and it's + * OK for reuse by the CPU. + */ + +#define VIA_FENCE_TYPE_SYSMEM (1 << 1) + +#endif /* _VIA_DRM_H_ */