[RFC PATCH v3 2/4] drm/ipvr: drm driver for VED
Yao Cheng
yao.cheng at intel.com
Fri Nov 21 11:06:58 PST 2014
Probes VED and creates a new drm device for hardware accelerated
video decoding.
Currently support VP8 decoding on valleyview.
v2:
take David's comments
- add mmap support and remove mmap_ioctl
- remove postclose since it's deprecated
- NULL set_busid
v3:
take David, Daniel and Jesse's comments and massive refine
- use drm_dev_alloc+drm_dev_register to replace drm_platform_init
- same as above in exit side
- remove fd based explit fence
- refine ipvr_drm.h, use __u32 series and refine paddings
- add doc to describe ipvr/ved terminology
- ioctl refine: remove unused code
- use uintptr_t series for address/number conversion
- runtime pm refine: guarantee get/put paring
- add PRIME feature and remove USERPTR ioctl
- implement relocation fixup in EXEC ioctl
- call drm_gem_get_pages to replace my own implementation
- code cleanup: remove unused code
Signed-off-by: Yao Cheng <yao.cheng at intel.com>
---
Documentation/DocBook/drm.tmpl | 39 ++
drivers/gpu/drm/Kconfig | 2 +
drivers/gpu/drm/Makefile | 1 +
drivers/gpu/drm/ipvr/Kconfig | 9 +
drivers/gpu/drm/ipvr/Makefile | 18 +
drivers/gpu/drm/ipvr/ipvr_bo.c | 546 +++++++++++++++++
drivers/gpu/drm/ipvr/ipvr_bo.h | 80 +++
drivers/gpu/drm/ipvr/ipvr_debug.c | 347 +++++++++++
drivers/gpu/drm/ipvr/ipvr_debug.h | 76 +++
drivers/gpu/drm/ipvr/ipvr_drm.h | 278 +++++++++
drivers/gpu/drm/ipvr/ipvr_drv.c | 614 +++++++++++++++++++
drivers/gpu/drm/ipvr/ipvr_drv.h | 294 +++++++++
drivers/gpu/drm/ipvr/ipvr_exec.c | 623 +++++++++++++++++++
drivers/gpu/drm/ipvr/ipvr_exec.h | 57 ++
drivers/gpu/drm/ipvr/ipvr_fence.c | 490 +++++++++++++++
drivers/gpu/drm/ipvr/ipvr_fence.h | 74 +++
drivers/gpu/drm/ipvr/ipvr_gem.c | 341 +++++++++++
drivers/gpu/drm/ipvr/ipvr_gem.h | 48 ++
drivers/gpu/drm/ipvr/ipvr_mmu.c | 752 +++++++++++++++++++++++
drivers/gpu/drm/ipvr/ipvr_mmu.h | 111 ++++
drivers/gpu/drm/ipvr/ipvr_trace.c | 11 +
drivers/gpu/drm/ipvr/ipvr_trace.h | 326 ++++++++++
drivers/gpu/drm/ipvr/ved_cmd.c | 882 +++++++++++++++++++++++++++
drivers/gpu/drm/ipvr/ved_cmd.h | 71 +++
drivers/gpu/drm/ipvr/ved_fw.c | 1199 +++++++++++++++++++++++++++++++++++++
drivers/gpu/drm/ipvr/ved_fw.h | 81 +++
drivers/gpu/drm/ipvr/ved_msg.h | 256 ++++++++
drivers/gpu/drm/ipvr/ved_pm.c | 332 ++++++++++
drivers/gpu/drm/ipvr/ved_pm.h | 36 ++
drivers/gpu/drm/ipvr/ved_reg.h | 561 +++++++++++++++++
30 files changed, 8555 insertions(+)
create mode 100644 drivers/gpu/drm/ipvr/Kconfig
create mode 100644 drivers/gpu/drm/ipvr/Makefile
create mode 100644 drivers/gpu/drm/ipvr/ipvr_bo.c
create mode 100644 drivers/gpu/drm/ipvr/ipvr_bo.h
create mode 100644 drivers/gpu/drm/ipvr/ipvr_debug.c
create mode 100644 drivers/gpu/drm/ipvr/ipvr_debug.h
create mode 100644 drivers/gpu/drm/ipvr/ipvr_drm.h
create mode 100644 drivers/gpu/drm/ipvr/ipvr_drv.c
create mode 100644 drivers/gpu/drm/ipvr/ipvr_drv.h
create mode 100644 drivers/gpu/drm/ipvr/ipvr_exec.c
create mode 100644 drivers/gpu/drm/ipvr/ipvr_exec.h
create mode 100644 drivers/gpu/drm/ipvr/ipvr_fence.c
create mode 100644 drivers/gpu/drm/ipvr/ipvr_fence.h
create mode 100644 drivers/gpu/drm/ipvr/ipvr_gem.c
create mode 100644 drivers/gpu/drm/ipvr/ipvr_gem.h
create mode 100644 drivers/gpu/drm/ipvr/ipvr_mmu.c
create mode 100644 drivers/gpu/drm/ipvr/ipvr_mmu.h
create mode 100644 drivers/gpu/drm/ipvr/ipvr_trace.c
create mode 100644 drivers/gpu/drm/ipvr/ipvr_trace.h
create mode 100644 drivers/gpu/drm/ipvr/ved_cmd.c
create mode 100644 drivers/gpu/drm/ipvr/ved_cmd.h
create mode 100644 drivers/gpu/drm/ipvr/ved_fw.c
create mode 100644 drivers/gpu/drm/ipvr/ved_fw.h
create mode 100644 drivers/gpu/drm/ipvr/ved_msg.h
create mode 100644 drivers/gpu/drm/ipvr/ved_pm.c
create mode 100644 drivers/gpu/drm/ipvr/ved_pm.h
create mode 100644 drivers/gpu/drm/ipvr/ved_reg.h
diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl
index b2d109f..07b5b19 100644
--- a/Documentation/DocBook/drm.tmpl
+++ b/Documentation/DocBook/drm.tmpl
@@ -4008,5 +4008,44 @@ int num_ioctls;</synopsis>
</sect1>
</chapter>
!Cdrivers/gpu/drm/i915/i915_irq.c
+ <chapter id="drmIpvr">
+ <title>drm/ipvr Intel's driver for PowerVR video core</title>
+ <para>
+ The drm/ipvr driver intends to support the PowerVR video cores
+ integrated on Intel's platforms. The video cores can be categorized
+ into 3 types:
+ <variablelist>
+ <varlistentry>
+ <term>VED</term>
+ <listitem>
+ <para>
+ multi-format video decoder, various video codecs are supported,
+ e.g. H.264, VP8, etc..
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>VEC</term>
+ <listitem>
+ <para>
+ multi-format video encoder, various video codecs support like
+ VED.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>VSP</term>
+ <listitem>
+ <para>
+ multi-function video processing. supports various features such
+ as color-space-conversion, sharpening, deblocking, etc..
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ Now ipvr only supports VED on Valleyview platform. The names "VEC"
+ and "VSP" are reserved for possible future support.
+ </para>
+ </chapter>
</part>
</book>
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index e3b4b0f..ad7585d 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -165,6 +165,8 @@ config DRM_SAVAGE
Choose this option if you have a Savage3D/4/SuperSavage/Pro/Twister
chipset. If M is selected the module will be called savage.
+source "drivers/gpu/drm/ipvr/Kconfig"
+
source "drivers/gpu/drm/exynos/Kconfig"
source "drivers/gpu/drm/vmwgfx/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index c3cf64c..f877208 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -41,6 +41,7 @@ obj-$(CONFIG_DRM_RADEON)+= radeon/
obj-$(CONFIG_DRM_MGA) += mga/
obj-$(CONFIG_DRM_I810) += i810/
obj-$(CONFIG_DRM_I915) += i915/
+obj-$(CONFIG_DRM_IPVR) += ipvr/
obj-$(CONFIG_DRM_MGAG200) += mgag200/
obj-$(CONFIG_DRM_CIRRUS_QEMU) += cirrus/
obj-$(CONFIG_DRM_SIS) += sis/
diff --git a/drivers/gpu/drm/ipvr/Kconfig b/drivers/gpu/drm/ipvr/Kconfig
new file mode 100644
index 0000000..869bad4
--- /dev/null
+++ b/drivers/gpu/drm/ipvr/Kconfig
@@ -0,0 +1,9 @@
+config DRM_IPVR
+ tristate "IPVR video decode driver"
+ depends on DRM
+ select SHMEM
+ select TMPFS
+ default m
+ help
+ Choose this option if you want to enable accelerated video decode with VED hardware.
+ Currently support VP8 decoding on valleyview.
diff --git a/drivers/gpu/drm/ipvr/Makefile b/drivers/gpu/drm/ipvr/Makefile
new file mode 100644
index 0000000..280647e
--- /dev/null
+++ b/drivers/gpu/drm/ipvr/Makefile
@@ -0,0 +1,18 @@
+ccflags-y := -Iinclude/drm
+
+ipvr-y := \
+ ipvr_drv.o \
+ ipvr_bo.o \
+ ipvr_exec.o \
+ ipvr_fence.o \
+ ipvr_gem.o \
+ ipvr_mmu.o \
+ ipvr_debug.o \
+ ipvr_trace.o \
+ ved_pm.o \
+ ved_cmd.o \
+ ved_fw.o
+
+obj-$(CONFIG_DRM_IPVR) += ipvr.o
+
+CFLAGS_ipvr_trace.o := -I$(src)
diff --git a/drivers/gpu/drm/ipvr/ipvr_bo.c b/drivers/gpu/drm/ipvr/ipvr_bo.c
new file mode 100644
index 0000000..fb30cec
--- /dev/null
+++ b/drivers/gpu/drm/ipvr/ipvr_bo.c
@@ -0,0 +1,546 @@
+/**************************************************************************
+ * ipvr_bo.c: IPVR buffer creation/destory, import/export, map etc
+ *
+ * Copyright (c) 2014 Intel Corporation, Hillsboro, OR, USA
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Authors:
+ * Yao Cheng <yao.cheng at intel.com>
+ *
+ **************************************************************************/
+
+#include "ipvr_bo.h"
+#include "ipvr_trace.h"
+#include "ipvr_debug.h"
+#include <drmP.h>
+#include <linux/dma-buf.h>
+
+static inline bool cpu_cache_is_coherent(enum ipvr_cache_level level)
+{
+ /* on valleyview no cache snooping */
+ return (level != IPVR_CACHE_WRITEBACK);
+}
+
+static inline bool clflush_object(struct drm_ipvr_gem_object *obj, bool force)
+{
+ if (obj->sg_table == NULL)
+ return false;
+
+ /* no need to flush if cache is coherent */
+ if (!force && cpu_cache_is_coherent(obj->cache_level))
+ return false;
+
+ drm_clflush_sg(obj->sg_table);
+
+ return true;
+}
+
+static void
+ipvr_object_free(struct drm_ipvr_gem_object *obj)
+{
+ struct drm_ipvr_private *dev_priv = obj->base.dev->dev_private;
+ kmem_cache_free(dev_priv->ipvr_bo_slab, obj);
+}
+
+static struct drm_ipvr_gem_object *
+ipvr_object_alloc(struct drm_ipvr_private *dev_priv, size_t size)
+{
+ struct drm_ipvr_gem_object *obj;
+
+ obj = kmem_cache_alloc(dev_priv->ipvr_bo_slab, GFP_KERNEL | __GFP_ZERO);
+ if (obj == NULL)
+ return NULL;
+ memset(obj, 0, sizeof(*obj));
+
+ return obj;
+}
+
+static int ipvr_gem_mmu_bind_object(struct drm_ipvr_gem_object *obj)
+{
+ struct drm_ipvr_private *dev_priv = obj->base.dev->dev_private;
+ u32 mask = 0;
+ const unsigned long entry = ipvr_gem_object_mmu_offset(obj);
+
+ if (IPVR_IS_ERR(entry)) {
+ return IPVR_OFFSET_ERR(entry);
+ }
+
+ IPVR_DEBUG_GENERAL("entry is 0x%lx, size is %zu, nents is %d.\n",
+ entry, obj->base.size, obj->sg_table->nents);
+
+ /**
+ * from vxd spec we should be able to set:
+ * mask |= IPVR_MMU_CACHED_MEMORY
+ * for those object marked as IPVR_CACHE_NOACCESS
+ * but test failed.
+ * so we force all pages flaged as non-cached by vxd now
+ */
+ return ipvr_mmu_insert_pages(dev_priv->mmu->default_pd,
+ obj->pages, entry, obj->base.size >> PAGE_SHIFT,
+ 0, 0, mask);
+}
+
+static void ipvr_gem_mmu_unbind_object(struct drm_ipvr_gem_object *obj)
+{
+ struct drm_ipvr_private *dev_priv = obj->base.dev->dev_private;
+ const unsigned long entry = ipvr_gem_object_mmu_offset(obj);
+ IPVR_DEBUG_GENERAL("entry is 0x%lx, size is %zu.\n",
+ entry, obj->base.size);
+ ipvr_mmu_remove_pages(dev_priv->mmu->default_pd,
+ entry, obj->base.size >> PAGE_SHIFT, 0, 0);
+}
+
+static void ipvr_gem_object_pin_pages(struct drm_ipvr_gem_object *obj)
+{
+ BUG_ON(obj->sg_table == NULL);
+ obj->pages_pin_count++;
+}
+
+static void ipvr_gem_object_unpin_pages(struct drm_ipvr_gem_object *obj)
+{
+ BUG_ON(obj->pages_pin_count == 0);
+ obj->pages_pin_count--;
+}
+
+static int ipvr_gem_bind_to_drm_mm(struct drm_ipvr_gem_object* obj,
+ struct ipvr_address_space *vm)
+{
+ int ret;
+ struct drm_mm *mm;
+ unsigned long start, end;
+ /* bind to VPU address space */
+ if (obj->tiling) {
+ mm = &vm->tiling_mm;
+ start = vm->tiling_start;
+ end = vm->tiling_start + vm->tiling_total;
+ } else {
+ mm = &vm->linear_mm;
+ start = vm->linear_start;
+ end = vm->linear_start + vm->linear_total;
+ }
+ IPVR_DEBUG_GENERAL("call drm_mm_insert_node_in_range_generic.\n");
+ ret = drm_mm_insert_node_in_range_generic(mm, &obj->mm_node, obj->base.size,
+ PAGE_SIZE, obj->cache_level,
+ start, end,
+ DRM_MM_SEARCH_DEFAULT,
+ DRM_MM_CREATE_DEFAULT);
+ if (ret) {
+ /* no shrinker implemented yet so simply return error */
+ IPVR_ERROR("failed on drm_mm_insert_node_in_range_generic: %d\n", ret);
+ return ret;
+ }
+
+ IPVR_DEBUG_GENERAL("drm_mm_insert_node_in_range_generic success: "
+ "start=0x%lx, size=%lu, color=%lu.\n",
+ obj->mm_node.start, obj->mm_node.size, obj->mm_node.color);
+
+ return 0;
+}
+
+struct drm_ipvr_gem_object* ipvr_gem_create(struct drm_ipvr_private *dev_priv,
+ size_t size, u32 tiling, u32 cache_level)
+{
+ struct drm_ipvr_gem_object *obj;
+ int ret = 0;
+ int npages;
+ struct address_space *mapping;
+ gfp_t mask;
+
+ BUG_ON(size & (PAGE_SIZE - 1));
+ npages = size >> PAGE_SHIFT;
+ IPVR_DEBUG_GENERAL("create bo size is %zu, tiling is %u, "
+ "cache level is %u.\n", size, tiling, cache_level);
+
+ /* Allocate the new object */
+ obj = ipvr_object_alloc(dev_priv, size);
+ if (!obj)
+ return ERR_PTR(-ENOMEM);
+
+ /* initialization */
+ ret = drm_gem_object_init(dev_priv->dev, &obj->base, size);
+ if (ret) {
+ IPVR_ERROR("failed on drm_gem_object_init: %d\n", ret);
+ goto err_free_obj;
+ }
+ init_waitqueue_head(&obj->event_queue);
+ /* todo: need set correct mask */
+ mask = GFP_HIGHUSER | __GFP_RECLAIMABLE;
+
+ /* ipvr cannot relocate objects above 4GiB. */
+ mask &= ~__GFP_HIGHMEM;
+ mask |= __GFP_DMA32;
+
+ mapping = file_inode(obj->base.filp)->i_mapping;
+ mapping_set_gfp_mask(mapping, mask);
+
+ obj->base.write_domain = IPVR_GEM_DOMAIN_CPU;
+ obj->base.read_domains = IPVR_GEM_DOMAIN_CPU;
+ obj->drv_name = "ipvr";
+ obj->fence = NULL;
+ obj->tiling = tiling;
+ obj->cache_level = cache_level;
+
+ /* get physical pages */
+ obj->pages = drm_gem_get_pages(&obj->base);
+ if (IS_ERR(obj->pages)) {
+ ret = PTR_ERR(obj->pages);
+ IPVR_ERROR("failed on drm_gem_get_pages: %d\n", ret);
+ goto err_free_obj;
+ }
+
+ obj->sg_table = drm_prime_pages_to_sg(obj->pages, obj->base.size >> PAGE_SHIFT);
+ if (IS_ERR(obj->sg_table)) {
+ ret = PTR_ERR(obj->sg_table);
+ IPVR_ERROR("failed on drm_gem_get_pages: %d\n", ret);
+ goto err_put_pages;
+ }
+
+ /* set cacheability */
+ switch (obj->cache_level) {
+ case IPVR_CACHE_NOACCESS:
+ case IPVR_CACHE_UNCACHED:
+ ret = set_pages_array_uc(obj->pages, npages);
+ break;
+ case IPVR_CACHE_WRITECOMBINE:
+ ret = set_pages_array_wc(obj->pages, npages);
+ break;
+ case IPVR_CACHE_WRITEBACK:
+ ret = set_pages_array_wb(obj->pages, npages);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ if (ret) {
+ IPVR_DEBUG_WARN("failed to set page cache: %d.\n", ret);
+ goto err_put_sg;
+ }
+
+ ipvr_gem_object_pin_pages(obj);
+
+ /* bind to VPU address space */
+ ret = ipvr_gem_bind_to_drm_mm(obj, &dev_priv->addr_space);
+ if (ret) {
+ IPVR_ERROR("failed to call ipvr_gem_bind_to_drm_mm: %d.\n", ret);
+ goto err_put_sg;
+ }
+
+ ret = ipvr_gem_mmu_bind_object(obj);
+ if (ret) {
+ IPVR_ERROR("failed to call ipvr_gem_mmu_bind_object: %d.\n", ret);
+ goto err_remove_node;
+ }
+
+ ipvr_stat_add_object(dev_priv, obj);
+ trace_ipvr_create_object(obj, ipvr_gem_object_mmu_offset(obj));
+ return obj;
+err_remove_node:
+ drm_mm_remove_node(&obj->mm_node);
+err_put_sg:
+ sg_free_table(obj->sg_table);
+ kfree(obj->sg_table);
+err_put_pages:
+ drm_gem_put_pages(&obj->base, obj->pages, false, false);
+err_free_obj:
+ ipvr_object_free(obj);
+ return ERR_PTR(ret);
+}
+
+void *ipvr_gem_object_vmap(struct drm_ipvr_gem_object *obj)
+{
+ pgprot_t pg = PAGE_KERNEL;
+ switch (obj->cache_level) {
+ case IPVR_CACHE_WRITECOMBINE:
+ pg = pgprot_writecombine(pg);
+ break;
+ case IPVR_CACHE_NOACCESS:
+ case IPVR_CACHE_UNCACHED:
+ pg = pgprot_noncached(pg);
+ break;
+ default:
+ break;
+ }
+ return vmap(obj->pages, obj->base.size >> PAGE_SHIFT, VM_MAP, pg);
+}
+
+/*
+ * When the last reference to a GEM object is released the GEM core calls the
+ * drm_driver .gem_free_object() operation. That operation is mandatory for
+ * GEM-enabled drivers and must free the GEM object and all associated
+ * resources.
+*/
+void ipvr_gem_free_object(struct drm_gem_object *gem_obj)
+{
+ struct drm_device *dev = gem_obj->dev;
+ struct drm_ipvr_gem_object *obj = to_ipvr_bo(gem_obj);
+ drm_ipvr_private_t *dev_priv = dev->dev_private;
+ int ret;
+ unsigned long mmu_offset;
+ int npages = gem_obj->size >> PAGE_SHIFT;
+
+ /* fixme: consider unlocked case */
+ WARN_ON(!mutex_is_locked(&dev->struct_mutex));
+
+ mmu_offset = ipvr_gem_object_mmu_offset(obj);
+ ipvr_gem_mmu_unbind_object(obj);
+
+ if (unlikely(obj->fence)) {
+ ret = ipvr_fence_wait(obj->fence, true, false);
+ if (ret)
+ IPVR_DEBUG_WARN("Failed to wait fence signaled: %d.\n", ret);
+ }
+
+ drm_mm_remove_node(&obj->mm_node);
+ ipvr_gem_object_unpin_pages(obj);
+
+ if (WARN_ON(obj->pages_pin_count))
+ obj->pages_pin_count = 0;
+
+ BUG_ON(!obj->pages || !obj->sg_table);
+ /* set back to page_wb */
+ set_pages_array_wb(obj->pages, npages);
+ if (obj->base.import_attach) {
+ IPVR_DEBUG_GENERAL("free imported object (mmu_offset 0x%lx)\n", mmu_offset);
+ drm_prime_gem_destroy(&obj->base, obj->sg_table);
+ drm_free_large(obj->pages);
+ ipvr_stat_remove_imported(dev_priv, obj);
+ }
+ else {
+ IPVR_DEBUG_GENERAL("free object (mmu_offset 0x%lx)\n", mmu_offset);
+ sg_free_table(obj->sg_table);
+ kfree(obj->sg_table);
+ drm_gem_put_pages(&obj->base, obj->pages, obj->dirty, true);
+ ipvr_stat_remove_object(dev_priv, obj);
+ }
+
+ /* mmap offset is freed by drm_gem_object_release */
+ drm_gem_object_release(&obj->base);
+
+ trace_ipvr_free_object(obj);
+
+ ipvr_object_free(obj);
+}
+
+static inline struct page *get_object_page(struct drm_ipvr_gem_object *obj, int n)
+{
+ struct sg_page_iter sg_iter;
+
+ for_each_sg_page(obj->sg_table->sgl, &sg_iter, obj->sg_table->nents, n)
+ return sg_page_iter_page(&sg_iter);
+
+ return NULL;
+}
+
+int ipvr_gem_object_apply_reloc(struct drm_ipvr_gem_object *obj,
+ u64 offset_in_bo, u32 value)
+{
+ u64 page_offset = offset_in_page(offset_in_bo);
+ char *vaddr;
+ struct page *target_page;
+
+ /* set to cpu domain */
+ target_page = get_object_page(obj, offset_in_bo >> PAGE_SHIFT);
+ if (!target_page)
+ return -EINVAL;
+
+ /**
+ * for efficiency we'd better record the page index,
+ * and avoid frequent map/unmap on the same page
+ */
+ vaddr = kmap_atomic(target_page);
+ if (!vaddr)
+ return -ENOMEM;
+ *(u32 *)(vaddr + page_offset) = value;
+
+ kunmap_atomic(vaddr);
+
+ return 0;
+}
+
+int ipvr_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+ struct drm_gem_object *obj = vma->vm_private_data;
+ struct drm_device *dev = obj->dev;
+ unsigned long pfn;
+ pgoff_t pgoff;
+ int ret;
+ struct drm_ipvr_gem_object *ipvr_obj = to_ipvr_bo(obj);
+
+ /* Make sure we don't parallel update on a fault, nor move or remove
+ * something from beneath our feet
+ */
+ ret = mutex_lock_interruptible(&dev->struct_mutex);
+ if (ret)
+ goto out;
+
+ if (!ipvr_obj->sg_table) {
+ ret = -ENODATA;
+ goto out_unlock;
+ }
+
+ /* We don't use vmf->pgoff since that has the fake offset: */
+ pgoff = ((unsigned long)vmf->virtual_address -
+ vma->vm_start) >> PAGE_SHIFT;
+
+ pfn = page_to_pfn(ipvr_obj->pages[pgoff]);
+
+ IPVR_DEBUG_GENERAL("Inserting %p pfn %lx, pa %lx\n", vmf->virtual_address,
+ pfn, pfn << PAGE_SHIFT);
+
+ ret = vm_insert_pfn(vma, (unsigned long)vmf->virtual_address, pfn);
+
+out_unlock:
+ mutex_unlock(&dev->struct_mutex);
+out:
+ switch (ret) {
+ case -EAGAIN:
+ case 0:
+ case -ERESTARTSYS:
+ case -EINTR:
+ case -EBUSY:
+ /*
+ * EBUSY is ok: this just means that another thread
+ * already did the job.
+ */
+ return VM_FAULT_NOPAGE;
+ case -ENOMEM:
+ return VM_FAULT_OOM;
+ default:
+ return VM_FAULT_SIGBUS;
+ }
+}
+
+struct sg_table *ipvr_gem_prime_get_sg_table(struct drm_gem_object *obj)
+{
+ struct drm_ipvr_gem_object *ipvr_obj = to_ipvr_bo(obj);
+ struct sg_table *sgt = NULL;
+ int ret;
+
+ if (!ipvr_obj->sg_table) {
+ ret = -ENOENT;
+ goto out;
+ }
+
+ sgt = drm_prime_pages_to_sg(ipvr_obj->pages, obj->size >> PAGE_SHIFT);
+ if (IS_ERR(sgt)) {
+ goto out;
+ }
+
+ IPVR_DEBUG_GENERAL("exported sg_table for obj (mmu_offset 0x%lx)\n",
+ ipvr_gem_object_mmu_offset(ipvr_obj));
+out:
+ return sgt;
+}
+
+struct drm_gem_object *ipvr_gem_prime_import_sg_table(struct drm_device *dev,
+ struct dma_buf_attachment *attach, struct sg_table *sg)
+{
+ struct drm_ipvr_gem_object *obj;
+ int ret = 0;
+ int i, npages;
+ unsigned long pfn;
+ struct drm_ipvr_private *dev_priv = dev->dev_private;
+
+ if (!sg || !attach || (attach->dmabuf->size & (PAGE_SIZE - 1)))
+ return ERR_PTR(-EINVAL);
+
+ IPVR_DEBUG_ENTRY("enter, size=0x%zx\n", attach->dmabuf->size);
+
+ obj = ipvr_object_alloc(dev_priv, attach->dmabuf->size);
+ if (!obj)
+ return ERR_PTR(-ENOMEM);
+
+ memset(obj, 0, sizeof(*obj));
+
+ drm_gem_private_object_init(dev, &obj->base, attach->dmabuf->size);
+
+ init_waitqueue_head(&obj->event_queue);
+
+ obj->drv_name = "ipvr";
+ obj->fence = NULL;
+ obj->cache_level = IPVR_CACHE_NOACCESS;
+ obj->tiling = 0;
+
+ npages = attach->dmabuf->size >> PAGE_SHIFT;
+
+ obj->sg_table = sg;
+ obj->pages = drm_malloc_ab(npages, sizeof(struct page *));
+ if (!obj->pages) {
+ ret = -ENOMEM;
+ goto err_free_obj;
+ }
+
+ ret = drm_prime_sg_to_page_addr_arrays(sg, obj->pages, NULL, npages);
+ if (ret)
+ goto err_put_pages;
+
+ /* validate sg_table
+ * should be under 4GiB
+ */
+ for (i = 0; i < npages; ++i) {
+ pfn = page_to_pfn(obj->pages[i]);
+ if (pfn >= 0x00100000UL) {
+ IPVR_ERROR("cannot support pfn 0x%lx.\n", pfn);
+ ret = -EINVAL; /* what's the better err code? */
+ goto err_put_pages;
+ }
+ }
+
+ ret = ipvr_gem_bind_to_drm_mm(obj, &dev_priv->addr_space);
+ if (ret) {
+ IPVR_ERROR("failed to call ipvr_gem_bind_to_drm_mm: %d.\n", ret);
+ goto err_put_pages;
+ }
+
+ /* do we really have to set the external pages uncached?
+ * this might causes perf issue in exporter side */
+ ret = set_pages_array_uc(obj->pages, npages);
+ if (ret)
+ IPVR_DEBUG_WARN("failed to set imported pages as uncached: %d, ignore\n", ret);
+
+ ret = ipvr_gem_mmu_bind_object(obj);
+ if (ret) {
+ IPVR_ERROR("failed to call ipvr_gem_mmu_bind_object: %d.\n", ret);
+ goto err_remove_node;
+ }
+ IPVR_DEBUG_GENERAL("imported sg_table, new bo mmu offset=0x%lx.\n",
+ ipvr_gem_object_mmu_offset(obj));
+ ipvr_stat_add_imported(dev_priv, obj);
+ ipvr_gem_object_pin_pages(obj);
+ return &obj->base;
+err_remove_node:
+ drm_mm_remove_node(&obj->mm_node);
+err_put_pages:
+ drm_free_large(obj->pages);
+err_free_obj:
+ ipvr_object_free(obj);
+ return ERR_PTR(ret);
+}
+
+int ipvr_gem_prime_pin(struct drm_gem_object *obj)
+{
+ struct drm_ipvr_private *dev_priv = obj->dev->dev_private;
+ IPVR_DEBUG_ENTRY("mmu offset 0x%lx\n", ipvr_gem_object_mmu_offset(to_ipvr_bo(obj)));
+ ipvr_stat_add_exported(dev_priv, to_ipvr_bo(obj));
+ return 0;
+}
+
+void ipvr_gem_prime_unpin(struct drm_gem_object *obj)
+{
+ struct drm_ipvr_private *dev_priv = obj->dev->dev_private;
+ IPVR_DEBUG_ENTRY("mmu offset 0x%lx\n", ipvr_gem_object_mmu_offset(to_ipvr_bo(obj)));
+ ipvr_stat_remove_exported(dev_priv, to_ipvr_bo(obj));
+}
diff --git a/drivers/gpu/drm/ipvr/ipvr_bo.h b/drivers/gpu/drm/ipvr/ipvr_bo.h
new file mode 100644
index 0000000..4981587
--- /dev/null
+++ b/drivers/gpu/drm/ipvr/ipvr_bo.h
@@ -0,0 +1,80 @@
+/**************************************************************************
+ * ipvr_bo.h: IPVR buffer creation/destory, import/export, map etc
+ *
+ * Copyright (c) 2014 Intel Corporation, Hillsboro, OR, USA
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Authors:
+ * Yao Cheng <yao.cheng at intel.com>
+ *
+ **************************************************************************/
+
+
+#ifndef _IPVR_BO_H_
+#define _IPVR_BO_H_
+
+#include "ipvr_drv.h"
+#include "ipvr_drm.h"
+#include "ipvr_fence.h"
+#include <drmP.h>
+#include <drm_gem.h>
+
+struct ipvr_fence;
+
+struct drm_ipvr_gem_object {
+ struct drm_gem_object base;
+
+ /* used to disinguish between i915 and ipvr */
+ char *drv_name;
+
+ /** MM related */
+ struct drm_mm_node mm_node;
+
+ bool tiling;
+
+ enum ipvr_cache_level cache_level;
+
+ bool dirty;
+
+ struct sg_table *sg_table;
+ struct page **pages;
+ int pages_pin_count;
+
+ struct ipvr_fence *fence;
+ atomic_t reserved;
+ wait_queue_head_t event_queue;
+};
+
+struct drm_ipvr_gem_object* ipvr_gem_create(struct drm_ipvr_private *dev_priv,
+ size_t size, u32 tiling, u32 cache_level);
+void ipvr_gem_free_object(struct drm_gem_object *obj);
+void *ipvr_gem_object_vmap(struct drm_ipvr_gem_object *obj);
+int ipvr_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
+int ipvr_gem_object_apply_reloc(struct drm_ipvr_gem_object *obj,
+ u64 offset_in_bo, u32 value);
+struct sg_table *ipvr_gem_prime_get_sg_table(struct drm_gem_object *obj);
+struct drm_gem_object *ipvr_gem_prime_import_sg_table(struct drm_device *dev,
+ struct dma_buf_attachment *attach, struct sg_table *sg);
+int ipvr_gem_prime_pin(struct drm_gem_object *obj);
+void ipvr_gem_prime_unpin(struct drm_gem_object *obj);
+
+static inline unsigned long
+ipvr_gem_object_mmu_offset(struct drm_ipvr_gem_object *obj)
+{
+ return obj->mm_node.start;
+}
+
+#endif
diff --git a/drivers/gpu/drm/ipvr/ipvr_debug.c b/drivers/gpu/drm/ipvr/ipvr_debug.c
new file mode 100644
index 0000000..29e40fa
--- /dev/null
+++ b/drivers/gpu/drm/ipvr/ipvr_debug.c
@@ -0,0 +1,347 @@
+/**************************************************************************
+ * ipvr_debug.c: IPVR debugfs support to assist bug triage
+ *
+ * Copyright (c) 2014 Intel Corporation, Hillsboro, OR, USA
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Authors:
+ * Fei Jiang <fei.jiang at intel.com>
+ * Yao Cheng <yao.cheng at intel.com>
+ *
+ **************************************************************************/
+
+#if defined(CONFIG_DEBUG_FS)
+
+#include "ipvr_debug.h"
+#include "ipvr_drv.h"
+#include "ved_reg.h"
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+
+union ipvr_debugfs_vars debugfs_vars;
+
+static int ipvr_debug_info(struct seq_file *m, void *data)
+{
+ seq_printf(m, "ipvr platform\n");
+ return 0;
+}
+
+/* some bookkeeping */
+void
+ipvr_stat_add_object(struct drm_ipvr_private *dev_priv, struct drm_ipvr_gem_object *obj)
+{
+ spin_lock(&dev_priv->ipvr_stat.object_stat_lock);
+ if (obj->base.import_attach) {
+ dev_priv->ipvr_stat.imported_count++;
+ dev_priv->ipvr_stat.imported_memory += obj->base.size;
+ }
+ else {
+ dev_priv->ipvr_stat.allocated_count++;
+ dev_priv->ipvr_stat.allocated_memory += obj->base.size;
+ }
+ spin_unlock(&dev_priv->ipvr_stat.object_stat_lock);
+}
+
+void
+ipvr_stat_remove_object(struct drm_ipvr_private *dev_priv, struct drm_ipvr_gem_object *obj)
+{
+ spin_lock(&dev_priv->ipvr_stat.object_stat_lock);
+ if (obj->base.import_attach) {
+ dev_priv->ipvr_stat.imported_count--;
+ dev_priv->ipvr_stat.imported_memory -= obj->base.size;
+ }
+ else {
+ dev_priv->ipvr_stat.allocated_count--;
+ dev_priv->ipvr_stat.allocated_memory -= obj->base.size;
+ }
+ spin_unlock(&dev_priv->ipvr_stat.object_stat_lock);
+}
+
+void
+ipvr_stat_add_imported(struct drm_ipvr_private *dev_priv, struct drm_ipvr_gem_object *obj)
+{
+ spin_lock(&dev_priv->ipvr_stat.object_stat_lock);
+ dev_priv->ipvr_stat.imported_count++;
+ dev_priv->ipvr_stat.imported_memory += obj->base.size;
+ spin_unlock(&dev_priv->ipvr_stat.object_stat_lock);
+}
+
+void
+ipvr_stat_remove_imported(struct drm_ipvr_private *dev_priv, struct drm_ipvr_gem_object *obj)
+{
+ spin_lock(&dev_priv->ipvr_stat.object_stat_lock);
+ dev_priv->ipvr_stat.imported_count--;
+ dev_priv->ipvr_stat.imported_memory -= obj->base.size;
+ spin_unlock(&dev_priv->ipvr_stat.object_stat_lock);
+}
+
+void
+ipvr_stat_add_exported(struct drm_ipvr_private *dev_priv, struct drm_ipvr_gem_object *obj)
+{
+ spin_lock(&dev_priv->ipvr_stat.object_stat_lock);
+ dev_priv->ipvr_stat.exported_count++;
+ dev_priv->ipvr_stat.exported_memory += obj->base.size;
+ spin_unlock(&dev_priv->ipvr_stat.object_stat_lock);
+}
+
+void
+ipvr_stat_remove_exported(struct drm_ipvr_private *dev_priv, struct drm_ipvr_gem_object *obj)
+{
+ spin_lock(&dev_priv->ipvr_stat.object_stat_lock);
+ dev_priv->ipvr_stat.exported_count--;
+ dev_priv->ipvr_stat.exported_memory -= obj->base.size;
+ spin_unlock(&dev_priv->ipvr_stat.object_stat_lock);
+}
+
+void ipvr_stat_add_mmu_bind(struct drm_ipvr_private *dev_priv, size_t size)
+{
+ spin_lock(&dev_priv->ipvr_stat.object_stat_lock);
+ dev_priv->ipvr_stat.mmu_used_size += size;
+ spin_unlock(&dev_priv->ipvr_stat.object_stat_lock);
+}
+
+void ipvr_stat_remove_mmu_bind(struct drm_ipvr_private *dev_priv, size_t size)
+{
+ spin_lock(&dev_priv->ipvr_stat.object_stat_lock);
+ dev_priv->ipvr_stat.mmu_used_size -= size;
+ spin_unlock(&dev_priv->ipvr_stat.object_stat_lock);
+}
+
+static int ipvr_debug_gem_object_info(struct seq_file *m, void* data)
+{
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_device *dev = node->minor->dev;
+ struct drm_ipvr_private *dev_priv = dev->dev_private;
+ int ret;
+
+ ret = mutex_lock_interruptible(&dev->struct_mutex);
+ if (ret)
+ return ret;
+
+ seq_printf(m, "total allocate %u objects, %zu bytes\n\n",
+ dev_priv->ipvr_stat.allocated_count,
+ dev_priv->ipvr_stat.allocated_memory);
+ seq_printf(m, "total imported %u objects, %zu bytes\n\n",
+ dev_priv->ipvr_stat.imported_count,
+ dev_priv->ipvr_stat.imported_memory);
+ seq_printf(m, "total exported %u objects, %zu bytes\n\n",
+ dev_priv->ipvr_stat.exported_count,
+ dev_priv->ipvr_stat.exported_memory);
+ seq_printf(m, "total used MMU size %zu bytes\n\n",
+ dev_priv->ipvr_stat.mmu_used_size);
+
+ mutex_unlock(&dev->struct_mutex);
+
+ return 0;
+}
+
+static int ipvr_debug_gem_seqno_info(struct seq_file *m, void *data)
+{
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_device *dev = node->minor->dev;
+ drm_ipvr_private_t *dev_priv = dev->dev_private;
+ int ret;
+
+ ret = mutex_lock_interruptible(&dev->struct_mutex);
+ if (ret)
+ return ret;
+
+ seq_printf(m, "last signaled seq is %d, last emitted seq is %d\n",
+ atomic_read(&dev_priv->fence_drv.signaled_seq),
+ dev_priv->fence_drv.sync_seq);
+
+ mutex_unlock(&dev->struct_mutex);
+
+ return 0;
+}
+
+static ssize_t ipvr_debug_ved_reg_read(struct file *filp, char __user *ubuf,
+ size_t max, loff_t *ppos)
+{
+ struct drm_device *dev = filp->private_data;
+ drm_ipvr_private_t *dev_priv = dev->dev_private;
+ char buf[200], offset[20], operation[10], format[20], val[20];
+ int len = 0, ret, no_of_tokens;
+ unsigned long reg_offset, reg_to_write;
+
+ if (debugfs_vars.reg.reg_input == 0)
+ return len;
+
+ snprintf(format, sizeof(format), "%%%zus %%%zus %%%zus",
+ sizeof(operation), sizeof(offset), sizeof(val));
+
+ no_of_tokens = sscanf(debugfs_vars.reg.reg_vars,
+ format, operation, offset, val);
+
+ if (no_of_tokens < 3)
+ return len;
+
+ len = sizeof(debugfs_vars.reg.reg_vars);
+
+ if (strcmp(operation, IPVR_READ_TOKEN) == 0) {
+ ret = kstrtoul(offset, 16, ®_offset);
+ if (ret)
+ return -EINVAL;
+
+ len = scnprintf(buf, sizeof(buf), "0x%x: 0x%x\n",
+ (u32)reg_offset,
+ IPVR_REG_READ32((u32)reg_offset));
+ } else if (strcmp(operation, IPVR_WRITE_TOKEN) == 0) {
+ ret = kstrtoul(offset, 16, ®_offset);
+ if (ret)
+ return -EINVAL;
+
+ ret = kstrtoul(val, 16, ®_to_write);
+ if (ret)
+ return -EINVAL;
+
+ IPVR_REG_WRITE32(reg_offset, reg_to_write);
+ len = scnprintf(buf, sizeof(buf),
+ "0x%x: 0x%x\n",
+ (u32)reg_offset,
+ (u32)IPVR_REG_READ32(reg_offset));
+ } else {
+ len = scnprintf(buf, sizeof(buf), "Operation Not Supported\n");
+ }
+
+ debugfs_vars.reg.reg_input = 0;
+
+ simple_read_from_buffer(ubuf, max, ppos, buf, len);
+
+ return len;
+}
+
+static ssize_t
+ipvr_debug_ved_reg_write(struct file *filp,const char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ /* reset the string */
+ memset(debugfs_vars.reg.reg_vars, 0, IPVR_MAX_BUFFER_STR_LEN);
+
+ if (cnt > 0) {
+ if (cnt > sizeof(debugfs_vars.reg.reg_vars) - 1)
+ return -EINVAL;
+
+ if (copy_from_user(debugfs_vars.reg.reg_vars, ubuf, cnt))
+ return -EFAULT;
+
+ debugfs_vars.reg.reg_vars[cnt] = 0;
+
+ /* Enable Read */
+ debugfs_vars.reg.reg_input = 1;
+ }
+
+ return cnt;
+}
+
+/* As the drm_debugfs_init() routines are called before dev->dev_private is
+ * allocated we need to hook into the minor for release. */
+static int ipvr_add_fake_info_node(struct drm_minor *minor,
+ struct dentry *ent, const void *key)
+{
+ struct drm_info_node *node;
+
+ node = kmalloc(sizeof(struct drm_info_node), GFP_KERNEL);
+ if (node == NULL) {
+ debugfs_remove(ent);
+ return -ENOMEM;
+ }
+
+ node->minor = minor;
+ node->dent = ent;
+ node->info_ent = (void *) key;
+
+ mutex_lock(&minor->debugfs_lock);
+ list_add(&node->list, &minor->debugfs_list);
+ mutex_unlock(&minor->debugfs_lock);
+
+ return 0;
+}
+
+static int ipvr_debugfs_create(struct dentry *root,
+ struct drm_minor *minor,
+ const char *name,
+ const struct file_operations *fops)
+{
+ struct drm_device *dev = minor->dev;
+ struct dentry *ent;
+
+ ent = debugfs_create_file(name,
+ S_IRUGO | S_IWUSR,
+ root, dev,
+ fops);
+ if (IS_ERR(ent))
+ return PTR_ERR(ent);
+
+ return ipvr_add_fake_info_node(minor, ent, fops);
+}
+
+static const struct file_operations ipvr_ved_reg_fops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .read = ipvr_debug_ved_reg_read,
+ .write = ipvr_debug_ved_reg_write,
+ .llseek = default_llseek,
+};
+
+static struct drm_info_list ipvr_debugfs_list[] = {
+ {"ipvr_capabilities", ipvr_debug_info, 0},
+ {"ipvr_gem_objects", ipvr_debug_gem_object_info, 0},
+ {"ipvr_gem_seqno", ipvr_debug_gem_seqno_info, 0},
+
+};
+#define IPVR_DEBUGFS_ENTRIES ARRAY_SIZE(ipvr_debugfs_list)
+
+static struct ipvr_debugfs_files {
+ const char *name;
+ const struct file_operations *fops;
+} ipvr_debugfs_files[] = {
+ {"ipvr_ved_reg_api", &ipvr_ved_reg_fops},
+};
+
+int ipvr_debugfs_init(struct drm_minor *minor)
+{
+ int ret, i;
+
+ for (i = 0; i < ARRAY_SIZE(ipvr_debugfs_files); i++) {
+ ret = ipvr_debugfs_create(minor->debugfs_root, minor,
+ ipvr_debugfs_files[i].name,
+ ipvr_debugfs_files[i].fops);
+ if (ret)
+ return ret;
+ }
+
+ return drm_debugfs_create_files(ipvr_debugfs_list,
+ IPVR_DEBUGFS_ENTRIES,
+ minor->debugfs_root, minor);
+}
+
+void ipvr_debugfs_cleanup(struct drm_minor *minor)
+{
+ int i;
+
+ drm_debugfs_remove_files(ipvr_debugfs_list,
+ IPVR_DEBUGFS_ENTRIES, minor);
+
+ for (i = 0; i < ARRAY_SIZE(ipvr_debugfs_files); i++) {
+ struct drm_info_list *info_list =
+ (struct drm_info_list *)ipvr_debugfs_files[i].fops;
+
+ drm_debugfs_remove_files(info_list, 1, minor);
+ }
+}
+
+#endif /* CONFIG_DEBUG_FS */
diff --git a/drivers/gpu/drm/ipvr/ipvr_debug.h b/drivers/gpu/drm/ipvr/ipvr_debug.h
new file mode 100644
index 0000000..a88382e
--- /dev/null
+++ b/drivers/gpu/drm/ipvr/ipvr_debug.h
@@ -0,0 +1,76 @@
+/**************************************************************************
+ * ipvr_debug.h: IPVR debugfs support header file
+ *
+ * Copyright (c) 2014 Intel Corporation, Hillsboro, OR, USA
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Authors:
+ * Fei Jiang <fei.jiang at intel.com>
+ * Yao Cheng <yao.cheng at intel.com>
+ *
+ **************************************************************************/
+
+
+#ifndef _IPVR_DEBUG_H_
+#define _IPVR_DEBUG_H_
+
+#include "ipvr_bo.h"
+#include "drmP.h"
+
+/* Operations supported */
+#define IPVR_MAX_BUFFER_STR_LEN 200
+
+#define IPVR_READ_TOKEN "READ"
+#define IPVR_WRITE_TOKEN "WRITE"
+
+/* DebugFS Variable declaration */
+struct ipvr_debugfs_reg_vars {
+ char reg_vars[IPVR_MAX_BUFFER_STR_LEN];
+ u32 reg_input;
+};
+
+union ipvr_debugfs_vars {
+ struct ipvr_debugfs_reg_vars reg;
+};
+
+int ipvr_debugfs_init(struct drm_minor *minor);
+void ipvr_debugfs_cleanup(struct drm_minor *minor);
+
+void ipvr_stat_add_object(struct drm_ipvr_private *dev_priv,
+ struct drm_ipvr_gem_object *obj);
+
+void ipvr_stat_remove_object(struct drm_ipvr_private *dev_priv,
+ struct drm_ipvr_gem_object *obj);
+
+void ipvr_stat_add_imported(struct drm_ipvr_private *dev_priv,
+ struct drm_ipvr_gem_object *obj);
+
+void ipvr_stat_remove_imported(struct drm_ipvr_private *dev_priv,
+ struct drm_ipvr_gem_object *obj);
+
+void ipvr_stat_add_exported(struct drm_ipvr_private *dev_priv,
+ struct drm_ipvr_gem_object *obj);
+
+void ipvr_stat_remove_exported(struct drm_ipvr_private *dev_priv,
+ struct drm_ipvr_gem_object *obj);
+
+void ipvr_stat_add_mmu_bind(struct drm_ipvr_private *dev_priv,
+ size_t size);
+
+void ipvr_stat_remove_mmu_bind(struct drm_ipvr_private *dev_priv,
+ size_t size);
+
+#endif
diff --git a/drivers/gpu/drm/ipvr/ipvr_drm.h b/drivers/gpu/drm/ipvr/ipvr_drm.h
new file mode 100644
index 0000000..d1916fa
--- /dev/null
+++ b/drivers/gpu/drm/ipvr/ipvr_drm.h
@@ -0,0 +1,278 @@
+/**************************************************************************
+ * ipvr_drm.h: IPVR header file exported to user space
+ *
+ * Copyright (c) 2014 Intel Corporation, Hillsboro, OR, USA
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Authors:
+ * Fei Jiang <fei.jiang at intel.com>
+ * Yao Cheng <yao.cheng at intel.com>
+ *
+ **************************************************************************/
+
+
+/* this file only define structs and macro which need export to user space */
+#ifndef _IPVR_DRM_H_
+#define _IPVR_DRM_H_
+
+#include <drm/drm.h>
+struct drm_ipvr_context_create {
+ /* passed ctx_info, including codec, profile info */
+#define IPVR_CONTEXT_TYPE_VED (0x1)
+ __u32 ctx_type;
+ /* returned back ctx_id */
+ __u32 ctx_id;
+ /*
+ * following tiling strides for VED are supported:
+ * stride 0: 512 for scheme 0, 1024 for scheme 1
+ * stride 1: 1024 for scheme 0, 2048 for scheme 1
+ * stride 2: 2048 for scheme 0, 4096 for scheme 1
+ * stride 3: 4096 for scheme 0
+ */
+ __u32 tiling_stride;
+ /*
+ * scheme 0: tile is 256x16, while minimal tile stride is 512
+ * scheme 1: tile is 512x8, while minimal tile stride is 1024
+ */
+ __u32 tiling_scheme;
+};
+
+struct drm_ipvr_context_destroy {
+ __u32 ctx_id;
+ __u32 pad64;
+};
+
+enum drm_ipvr_misc_key {
+ IPVR_DEVICE_INFO,
+ IPVR_UPDATE_TILING,
+ IPVR_SET_DISPLAYING_FRAME,
+ IPVR_GET_DISPLAYING_FRAME,
+ IPVR_QUERY_ENTRY
+};
+
+/*
+ * different context maybe has different tiling stride,
+ * then tiling info need be bound with ctx
+ */
+struct drm_ipvr_update_tiling {
+ __u32 ctx_id;
+ __u32 tiling_stride;
+ __u32 tiling_scheme;
+ __u32 pad64;
+};
+
+/* Ioctl to set/get misc params:
+ */
+struct drm_ipvr_misc {
+ __u64 key;
+ __u64 arg; /* argument pointer */
+ __u64 value; /* feed back pointer */
+};
+
+struct drm_ipvr_gem_relocation_entry {
+ /**
+ * Handle of the buffer being pointed to by this relocation entry.
+ *
+ * It's appealing to make this be an index into the mm_validate_entry
+ * list to refer to the buffer, but this allows the driver to create
+ * a relocation list for state buffers and not re-write it per
+ * exec using the buffer.
+ */
+ __u32 target_handle;
+
+ /**
+ * Value to be added to the offset of the target buffer to make up
+ * the relocation entry.
+ */
+ __u32 delta;
+
+ /** Offset in the buffer the relocation entry will be written into */
+ __u64 offset;
+
+ /**
+ * Offset value of the target buffer that the relocation entry was last
+ * written as.
+ *
+ * If the buffer has the same offset as last time, we can skip syncing
+ * and writing the relocation. This value is written back out by
+ * the execbuffer ioctl when the relocation is written.
+ */
+ __u64 presumed_offset;
+
+ /**
+ * Target memory domains read by this operation.
+ */
+ __u32 read_domains;
+
+ /**
+ * Target memory domains written by this operation.
+ *
+ * Note that only one domain may be written by the whole
+ * execbuffer operation, so that where there are conflicts,
+ * the application will get -EINVAL back.
+ */
+ __u32 write_domain;
+};
+
+struct drm_ipvr_gem_exec_object {
+ /**
+ * User's handle for a buffer to be bound into the MMU for this
+ * operation.
+ */
+ __u32 handle;
+
+ /** Number of relocations to be performed on this buffer */
+ __u32 relocation_count;
+ /**
+ * Pointer to array of struct drm_i915_gem_relocation_entry containing
+ * the relocations to be performed in this buffer.
+ */
+ __u64 relocs_ptr;
+
+ /** Required alignment in graphics aperture */
+ __u64 alignment;
+
+ /**
+ * Returned value of the updated offset of the object, for future
+ * presumed_offset writes.
+ */
+ __u64 offset;
+
+#define IPVR_EXEC_OBJECT_NEED_FENCE (1 << 0)
+#define IPVR_EXEC_OBJECT_SUBMIT (1 << 1)
+ __u64 flags;
+
+ __u64 rsvd1;
+ __u64 rsvd2;
+};
+
+struct drm_ipvr_gem_execbuffer {
+ /**
+ * List of gem_exec_object2 structs
+ */
+ __u64 buffers_ptr;
+ __u32 buffer_count;
+
+ /** Offset in the batchbuffer to start execution from. */
+ __u32 exec_start_offset;
+ /** Bytes used in batchbuffer from batch_start_offset */
+ __u32 exec_len;
+
+ /**
+ * ID of hardware context.
+ */
+ __u32 ctx_id;
+
+ __u64 flags;
+ __u64 rsvd1;
+ __u64 rsvd2;
+};
+
+enum ipvr_cache_level
+{
+ IPVR_CACHE_NOACCESS,
+ IPVR_CACHE_UNCACHED,
+ IPVR_CACHE_WRITEBACK,
+ IPVR_CACHE_WRITECOMBINE,
+ IPVR_CACHE_MAX,
+};
+
+struct drm_ipvr_gem_create {
+ /*
+ * Requested size for the object.
+ * The (page-aligned) allocated size for the object will be returned.
+ */
+ __u64 size;
+ __u64 rounded_size;
+ __u64 mmu_offset;
+ /*
+ * Returned handle for the object.
+ * Object handles are nonzero.
+ */
+ __u32 handle;
+ __u32 tiling;
+
+ __u32 cache_level;
+ __u32 pad64;
+ /*
+ * Handle used for user to mmap BO
+ */
+ __u64 map_offset;
+};
+
+struct drm_ipvr_gem_busy {
+ /* Handle of the buffer to check for busy */
+ __u32 handle;
+
+ /*
+ * Return busy status (1 if busy, 0 if idle).
+ * The high word is used to indicate on which rings the object
+ * currently resides:
+ * 16:31 - busy (r or r/w) rings (16 render, 17 bsd, 18 blt, etc)
+ */
+ __u32 busy;
+};
+
+struct drm_ipvr_gem_mmap_offset {
+ /** Handle for the object being mapped. */
+ __u32 handle;
+ __u32 pad64;
+ /**
+ * Fake offset to use for subsequent mmap call
+ *
+ * This is a fixed-size type for 32/64 compatibility.
+ */
+ __u64 offset;
+};
+
+struct drm_ipvr_gem_wait {
+ /* Handle of BO we shall wait on */
+ __u32 handle;
+ __u32 flags;
+ /** Number of nanoseconds to wait, Returns time remaining. */
+ __s64 timeout_ns;
+};
+
+/*
+ * IPVR GEM specific ioctls
+ */
+#define DRM_IPVR_CONTEXT_CREATE 0x00
+#define DRM_IPVR_CONTEXT_DESTROY 0x01
+#define DRM_IPVR_MISC 0x02
+#define DRM_IPVR_GEM_EXECBUFFER 0x03
+#define DRM_IPVR_GEM_BUSY 0x04
+#define DRM_IPVR_GEM_CREATE 0x05
+#define DRM_IPVR_GEM_WAIT 0x06
+#define DRM_IPVR_GEM_MMAP_OFFSET 0x07
+
+#define DRM_IOCTL_IPVR_CONTEXT_CREATE \
+ DRM_IOWR(DRM_COMMAND_BASE + DRM_IPVR_CONTEXT_CREATE, struct drm_ipvr_context_create)
+#define DRM_IOCTL_IPVR_CONTEXT_DESTROY \
+ DRM_IOW(DRM_COMMAND_BASE + DRM_IPVR_CONTEXT_DESTROY, struct drm_ipvr_context_destroy)
+#define DRM_IOCTL_IPVR_MISC \
+ DRM_IOWR(DRM_COMMAND_BASE + DRM_IPVR_MISC, struct drm_ipvr_misc)
+#define DRM_IOCTL_IPVR_GEM_EXECBUFFER \
+ DRM_IOWR(DRM_COMMAND_BASE + DRM_IPVR_GEM_EXECBUFFER, struct drm_ipvr_gem_execbuffer)
+#define DRM_IOCTL_IPVR_GEM_BUSY \
+ DRM_IOWR(DRM_COMMAND_BASE + DRM_IPVR_GEM_BUSY, struct drm_ipvr_gem_busy)
+#define DRM_IOCTL_IPVR_GEM_CREATE \
+ DRM_IOWR(DRM_COMMAND_BASE + DRM_IPVR_GEM_CREATE, struct drm_ipvr_gem_create)
+#define DRM_IOCTL_IPVR_GEM_WAIT \
+ DRM_IOWR(DRM_COMMAND_BASE + DRM_IPVR_GEM_WAIT, struct drm_ipvr_gem_wait)
+#define DRM_IOCTL_IPVR_GEM_MMAP_OFFSET \
+ DRM_IOWR(DRM_COMMAND_BASE + DRM_IPVR_GEM_MMAP_OFFSET, struct drm_ipvr_gem_mmap_offset)
+
+#endif
diff --git a/drivers/gpu/drm/ipvr/ipvr_drv.c b/drivers/gpu/drm/ipvr/ipvr_drv.c
new file mode 100644
index 0000000..9f71fbd
--- /dev/null
+++ b/drivers/gpu/drm/ipvr/ipvr_drv.c
@@ -0,0 +1,614 @@
+/**************************************************************************
+ * ipvr_drv.c: IPVR driver common file for initialization/de-initialization
+ *
+ * Copyright (c) 2014 Intel Corporation, Hillsboro, OR, USA
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Authors:
+ * Fei Jiang <fei.jiang at intel.com>
+ * Yao Cheng <yao.cheng at intel.com>
+ *
+ **************************************************************************/
+
+#include "ipvr_drv.h"
+#include "ipvr_gem.h"
+#include "ipvr_mmu.h"
+#include "ipvr_exec.h"
+#include "ipvr_bo.h"
+#include "ipvr_debug.h"
+#include "ipvr_trace.h"
+#include "ved_fw.h"
+#include "ved_pm.h"
+#include "ved_reg.h"
+#include "ved_cmd.h"
+#include <linux/device.h>
+#include <linux/version.h>
+#include <uapi/drm/drm.h>
+#include <linux/pm_runtime.h>
+#include <linux/console.h>
+#include <linux/module.h>
+#include <asm/uaccess.h>
+
+int drm_ipvr_debug = 0x80;
+int drm_ipvr_freq = 320;
+
+module_param_named(debug, drm_ipvr_debug, int, 0600);
+module_param_named(freq, drm_ipvr_freq, int, 0600);
+
+MODULE_PARM_DESC(debug,
+ "control debug info output"
+ "default: 0"
+ "0x01:IPVR_D_GENERAL, 0x02:IPVR_D_INIT, 0x04:IPVR_D_IRQ, 0x08:IPVR_D_ENTRY"
+ "0x10:IPVR_D_PM, 0x20:IPVR_D_REG, 0x40:IPVR_D_VED, 0x80:IPVR_D_WARN");
+MODULE_PARM_DESC(freq,
+ "prefered VED frequency"
+ "default: 320 MHz");
+
+static struct drm_ioctl_desc ipvr_gem_ioctls[] = {
+ DRM_IOCTL_DEF_DRV(IPVR_CONTEXT_CREATE,
+ ipvr_context_create_ioctl, DRM_UNLOCKED),
+ DRM_IOCTL_DEF_DRV(IPVR_CONTEXT_DESTROY,
+ ipvr_context_destroy_ioctl, DRM_UNLOCKED),
+ DRM_IOCTL_DEF_DRV(IPVR_MISC,
+ ipvr_misc_ioctl, DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(IPVR_GEM_EXECBUFFER,
+ ipvr_gem_execbuffer_ioctl, DRM_AUTH|DRM_UNLOCKED),
+ DRM_IOCTL_DEF_DRV(IPVR_GEM_BUSY,
+ ipvr_gem_busy_ioctl, DRM_AUTH|DRM_UNLOCKED),
+ DRM_IOCTL_DEF_DRV(IPVR_GEM_CREATE,
+ ipvr_gem_create_ioctl, DRM_UNLOCKED),
+ DRM_IOCTL_DEF_DRV(IPVR_GEM_WAIT,
+ ipvr_gem_wait_ioctl, DRM_AUTH|DRM_UNLOCKED),
+ DRM_IOCTL_DEF_DRV(IPVR_GEM_MMAP_OFFSET,
+ ipvr_gem_mmap_offset_ioctl, DRM_UNLOCKED),
+};
+
+static void ipvr_gem_init(struct drm_device *dev)
+{
+ struct drm_ipvr_private *dev_priv = dev->dev_private;
+
+ dev_priv->ipvr_bo_slab = kmem_cache_create("ipvr_gem_object",
+ sizeof(struct drm_ipvr_gem_object), 0,
+ SLAB_HWCACHE_ALIGN, NULL);
+
+ spin_lock_init(&dev_priv->ipvr_stat.object_stat_lock);
+ dev_priv->ipvr_stat.interruptible = true;
+}
+
+static void ipvr_gem_setup_mmu(struct drm_device *dev,
+ unsigned long linear_start,
+ unsigned long linear_end,
+ unsigned long tiling_start,
+ unsigned long tiling_end)
+{
+ /* Let GEM Manage all of the aperture.
+ */
+ struct drm_ipvr_private *dev_priv = dev->dev_private;
+ struct ipvr_address_space *addr_space = &dev_priv->addr_space;
+
+ addr_space->dev = dev_priv->dev;
+
+ /* Subtract the guard page ... */
+ drm_mm_init(&addr_space->linear_mm, linear_start,
+ linear_end - linear_start - PAGE_SIZE);
+ dev_priv->addr_space.linear_start = linear_start;
+ dev_priv->addr_space.linear_total = linear_end - linear_start;
+
+ drm_mm_init(&addr_space->tiling_mm, tiling_start,
+ tiling_end - tiling_start - PAGE_SIZE);
+ dev_priv->addr_space.tiling_start = tiling_start;
+ dev_priv->addr_space.tiling_total = tiling_end - tiling_start;
+}
+
+int ipvr_runtime_pm_get(struct drm_ipvr_private *dev_priv)
+{
+ int ret = 0;
+ int pending;
+ struct platform_device *platdev = dev_priv->dev->platformdev;
+ BUG_ON(!platdev);
+ BUG_ON(atomic_read(&dev_priv->pending_events) < 0);
+ if ((pending = atomic_inc_return(&dev_priv->pending_events)) == 1) {
+ ret = pm_runtime_get_sync(&platdev->dev);
+ if (ret < 0) {
+ IPVR_ERROR("pm_runtime_get_sync returns %d\n", ret);
+ pending = atomic_dec_return(&dev_priv->pending_events);
+ }
+ }
+ trace_ipvr_get_power(atomic_read(&platdev->dev.power.usage_count),
+ pending);
+ return ret;
+}
+
+int ipvr_runtime_pm_put(struct drm_ipvr_private *dev_priv, bool async)
+{
+ int ret = 0;
+ int pending;
+ struct platform_device *platdev = dev_priv->dev->platformdev;
+ BUG_ON(!platdev);
+ BUG_ON(atomic_read(&dev_priv->pending_events) <= 0);
+ if ((pending = atomic_dec_return(&dev_priv->pending_events)) == 0) {
+ if (async)
+ ret = pm_runtime_put(&platdev->dev);
+ else
+ ret = pm_runtime_put_sync(&platdev->dev);
+ if (ret < 0)
+ IPVR_ERROR("pm_runtime_put returns %d\n", ret);
+ }
+ trace_ipvr_put_power(atomic_read(&platdev->dev.power.usage_count),
+ pending);
+ return ret;
+}
+
+int ipvr_runtime_pm_put_all(struct drm_ipvr_private *dev_priv, bool async)
+{
+ int ret = 0;
+ struct platform_device *platdev = dev_priv->dev->platformdev;
+ BUG_ON(!platdev);
+ if (atomic_read(&dev_priv->pending_events) > 0) {
+ atomic_set(&dev_priv->pending_events, 0);
+ if (async)
+ ret = pm_runtime_put(&platdev->dev);
+ else
+ ret = pm_runtime_put_sync(&platdev->dev);
+ if (ret < 0)
+ IPVR_ERROR("pm_runtime_put returns %d\n", ret);
+ }
+ trace_ipvr_put_power(atomic_read(&platdev->dev.power.usage_count),
+ 0);
+ return ret;
+}
+
+static int ipvr_drm_unload(struct drm_device *dev)
+{
+ struct drm_ipvr_private *dev_priv = dev->dev_private;
+ IPVR_DEBUG_ENTRY("entered.");
+ BUG_ON(!dev->platformdev);
+
+ if (dev_priv) {
+ if (dev_priv->ipvr_bo_slab)
+ kmem_cache_destroy(dev_priv->ipvr_bo_slab);
+ ipvr_fence_driver_fini(dev_priv);
+
+ if (WARN_ON(ipvr_runtime_pm_get(dev_priv) < 0))
+ IPVR_DEBUG_WARN("Error getting ipvr power\n");
+ else {
+ ved_core_deinit(dev_priv);
+ if (WARN_ON(ipvr_runtime_pm_put_all(dev_priv, false) < 0))
+ IPVR_DEBUG_WARN("Error getting ipvr power\n");
+ }
+ if (dev_priv->validate_ctx.buffers)
+ vfree(dev_priv->validate_ctx.buffers);
+
+ if (dev_priv->mmu) {
+ ipvr_mmu_driver_takedown(dev_priv->mmu);
+ dev_priv->mmu = NULL;
+ }
+
+ if (dev_priv->reg_base) {
+ iounmap(dev_priv->reg_base);
+ dev_priv->reg_base = NULL;
+ }
+
+ list_del(&dev_priv->default_ctx.head);
+ idr_remove(&dev_priv->ipvr_ctx_idr, dev_priv->default_ctx.ctx_id);
+ kfree(dev_priv);
+
+ }
+ pm_runtime_disable(&dev->platformdev->dev);
+
+ return 0;
+}
+
+static int ipvr_drm_load(struct drm_device *dev, unsigned long flags)
+{
+ struct drm_ipvr_private *dev_priv;
+ u32 ctx_id;
+ int ret = 0;
+ struct resource *res_mmio;
+ void __iomem* mmio_start;
+
+ if (!dev->platformdev)
+ return -ENODEV;
+
+ dev_priv = kzalloc(sizeof(*dev_priv), GFP_KERNEL);
+ if (dev_priv == NULL)
+ return -ENOMEM;
+
+ dev->dev_private = dev_priv;
+ dev_priv->dev = dev;
+
+ INIT_LIST_HEAD(&dev_priv->validate_ctx.validate_list);
+
+ dev_priv->pci_root = pci_get_bus_and_slot(0, PCI_DEVFN(0, 0));
+ if (!dev_priv->pci_root) {
+ kfree(dev_priv);
+ return -ENODEV;
+ }
+
+ dev->irq = platform_get_irq(dev->platformdev, 0);
+ if (dev->irq < 0) {
+ kfree(dev_priv);
+ return -ENODEV;
+ }
+
+ res_mmio = platform_get_resource(dev->platformdev, IORESOURCE_MEM, 0);
+ if (!res_mmio) {
+ kfree(dev_priv);
+ return -ENXIO;
+ }
+
+ mmio_start = ioremap_nocache(res_mmio->start,
+ res_mmio->end - res_mmio->start);
+ if (!mmio_start) {
+ kfree(dev_priv);
+ return -EACCES;
+ }
+
+ dev_priv->reg_base = mmio_start;
+ IPVR_DEBUG_VED("reg_base is %p - 0x%p.\n",
+ dev_priv->reg_base,
+ dev_priv->reg_base + (res_mmio->end - res_mmio->start));
+
+ atomic_set(&dev_priv->pending_events, 0);
+ pm_runtime_enable(&dev->platformdev->dev);
+ if (WARN_ON(ipvr_runtime_pm_get(dev_priv) < 0)) {
+ IPVR_ERROR("Error getting ipvr power\n");
+ ret = -EBUSY;
+ goto out_err;
+ }
+
+ IPVR_DEBUG_INIT("MSVDX_CORE_REV_OFFSET by readl is 0x%x.\n",
+ readl(dev_priv->reg_base + 0x640));
+ IPVR_DEBUG_INIT("MSVDX_CORE_REV_OFFSET by VED_REG_READ32 is 0x%x.\n",
+ IPVR_REG_READ32(MSVDX_CORE_REV_OFFSET));
+
+ /* mmu init */
+ dev_priv->mmu = ipvr_mmu_driver_init(NULL, 0, dev_priv);
+ if (!dev_priv->mmu) {
+ ret = -EBUSY;
+ goto out_err;
+ }
+
+ ipvr_mmu_set_pd_context(ipvr_mmu_get_default_pd(dev_priv->mmu), 0);
+
+ /*
+ * Initialize sequence numbers for the different command
+ * submission mechanisms.
+ */
+ dev_priv->last_seq = 1;
+
+ ipvr_gem_init(dev);
+
+ ipvr_gem_setup_mmu(dev,
+ IPVR_MEM_MMU_LINEAR_START,
+ IPVR_MEM_MMU_LINEAR_END,
+ IPVR_MEM_MMU_TILING_START,
+ IPVR_MEM_MMU_TILING_END);
+
+ ved_core_init(dev_priv);
+
+ if (WARN_ON(ipvr_runtime_pm_put(dev_priv, false) < 0))
+ IPVR_DEBUG_WARN("Error putting ipvr power\n");
+
+ dev_priv->ved_private->ved_needs_reset = 1;
+
+ ipvr_fence_driver_init(dev_priv);
+
+ dev_priv->validate_ctx.buffers =
+ vmalloc(IPVR_NUM_VALIDATE_BUFFERS *
+ sizeof(struct ipvr_validate_buffer));
+ if (!dev_priv->validate_ctx.buffers) {
+ ret = -ENOMEM;
+ goto out_err;
+ }
+
+ /* ipvr context initialization */
+ INIT_LIST_HEAD(&dev_priv->ipvr_ctx_list);
+ spin_lock_init(&dev_priv->ipvr_ctx_lock);
+ idr_init(&dev_priv->ipvr_ctx_idr);
+ /* default ipvr context is used for scaling, rotation case */
+ ctx_id = idr_alloc(&dev_priv->ipvr_ctx_idr, &dev_priv->default_ctx,
+ IPVR_MIN_CONTEXT_ID, IPVR_MAX_CONTEXT_ID,
+ GFP_KERNEL);
+ if (ctx_id < 0) {
+ return -ENOMEM;
+ goto out_err;
+ }
+ dev_priv->default_ctx.ctx_id = ctx_id;
+ INIT_LIST_HEAD(&dev_priv->default_ctx.head);
+ dev_priv->default_ctx.ctx_type = 0;
+ dev_priv->default_ctx.ipvr_fpriv = NULL;
+
+ /* don't need protect with spinlock during module load stage */
+ list_add(&dev_priv->default_ctx.head, &dev_priv->ipvr_ctx_list);
+ dev_priv->default_ctx.tiling_scheme = 0;
+ dev_priv->default_ctx.tiling_stride = 0;
+
+ return 0;
+out_err:
+ ipvr_drm_unload(dev);
+ return ret;
+}
+
+/*
+ * The .open() method is called every time the device is opened by an
+ * application. Drivers can allocate per-file private data in this method and
+ * store them in the struct drm_file::driver_priv field. Note that the .open()
+ * method is called before .firstopen().
+ */
+static int
+ipvr_drm_open(struct drm_device *dev, struct drm_file *file_priv)
+{
+ struct drm_ipvr_file_private *ipvr_fp;
+ IPVR_DEBUG_ENTRY("enter\n");
+
+ ipvr_fp = kzalloc(sizeof(*ipvr_fp), GFP_KERNEL);
+ if (unlikely(ipvr_fp == NULL))
+ return -ENOMEM;
+
+ file_priv->driver_priv = ipvr_fp;
+
+ return 0;
+}
+
+/*
+ * The close operation is split into .preclose() and .postclose() methods.
+ * Since .postclose() is deprecated, all resource destruction related to file
+ * handle are now done in .preclose() method.
+ */
+static void
+ipvr_drm_preclose(struct drm_device *dev, struct drm_file *file_priv)
+{
+ /* if user didn't destory ctx explicitly, remove ctx here */
+ struct drm_ipvr_private *dev_priv;
+ struct drm_ipvr_file_private *ipvr_fpriv;
+ struct ved_private *ved_priv;
+ struct ipvr_context *ipvr_ctx = NULL;
+ unsigned long irq_flags;
+
+ IPVR_DEBUG_ENTRY("enter\n");
+ dev_priv = dev->dev_private;
+ ipvr_fpriv = file_priv->driver_priv;
+ ved_priv = dev_priv->ved_private;
+
+ if (ipvr_fpriv->ctx_id == IPVR_CONTEXT_INVALID_ID)
+ return;
+ ipvr_ctx = (struct ipvr_context *)
+ idr_find(&dev_priv->ipvr_ctx_idr, ipvr_fpriv->ctx_id);
+ if (!ipvr_ctx || (ipvr_ctx->ipvr_fpriv != ipvr_fpriv)) {
+ IPVR_DEBUG_GENERAL("ctx for id %d has already destroyed\n",
+ ipvr_fpriv->ctx_id);
+ return;
+ }
+
+ /**
+ * fixme: remove this work-around (WA the issue that calling
+ * close() with queued cmd might cause state machine issue).
+ * we should wait for only the cmds sent from contexts in this file
+ * instead of all cmds
+ */
+ ipvr_fence_wait_empty_locked(dev_priv);
+
+ IPVR_DEBUG_PM("Video:remove context type 0x%x\n", ipvr_ctx->ctx_type);
+ mutex_lock(&ved_priv->ved_mutex);
+ if (ved_priv->ipvr_ctx == ipvr_ctx )
+ ved_priv->ipvr_ctx = NULL;
+ mutex_unlock(&ved_priv->ved_mutex);
+
+ spin_lock_irqsave(&dev_priv->ipvr_ctx_lock, irq_flags);
+ list_del(&ipvr_ctx->head);
+ ipvr_fpriv->ctx_id = IPVR_CONTEXT_INVALID_ID;
+ spin_unlock_irqrestore(&dev_priv->ipvr_ctx_lock, irq_flags);
+
+ idr_remove(&dev_priv->ipvr_ctx_idr, ipvr_ctx->ctx_id);
+
+ kfree(ipvr_ctx );
+ kfree(ipvr_fpriv);
+}
+
+static irqreturn_t ipvr_irq_handler(int irq, void *arg)
+{
+ struct drm_device *dev = (struct drm_device *) arg;
+ struct drm_ipvr_private *dev_priv = dev->dev_private;
+ WARN_ON(ved_irq_handler(dev_priv->ved_private));
+ return IRQ_HANDLED;
+}
+
+static const struct file_operations ipvr_fops = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .release = drm_release,
+ .unlocked_ioctl = drm_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = drm_ioctl,
+#endif
+ .mmap = drm_gem_mmap,
+};
+
+static int ipvr_drm_freeze(struct drm_device *dev)
+{
+ int ret;
+ struct drm_ipvr_private *dev_priv = dev->dev_private;
+ IPVR_DEBUG_ENTRY("enter\n");
+
+ ret = ved_check_idle(dev_priv->ved_private);
+ if (ret) {
+ IPVR_DEBUG_PM("VED check idle fail: %d, skip freezing\n", ret);
+ /**
+ * fixme: better to schedule a workqueue for delayed power-off?
+ */
+ return 0;
+ }
+
+ if (dev->irq_enabled) {
+ ret = drm_irq_uninstall(dev);
+ if (ret) {
+ IPVR_ERROR("Failed to uninstall drm irq handler: %d\n", ret);
+ }
+ }
+
+ if (is_ved_on(dev_priv)) {
+ if (!ved_power_off(dev_priv)) {
+ IPVR_ERROR("Failed to power off VED\n");
+ return -EFAULT;
+ }
+ IPVR_DEBUG_PM("Successfully powered off\n");
+ } else {
+ IPVR_DEBUG_PM("Skiped power-off since already powered off\n");
+ }
+
+ return 0;
+}
+
+static int ipvr_drm_thaw(struct drm_device *dev)
+{
+ int ret;
+ struct drm_ipvr_private *dev_priv = dev->dev_private;
+ IPVR_DEBUG_ENTRY("enter\n");
+ if (!is_ved_on(dev_priv)) {
+ if (!ved_power_on(dev_priv)) {
+ IPVR_ERROR("Failed to power on VED\n");
+ return -EFAULT;
+ }
+ IPVR_DEBUG_PM("Successfully powered on\n");
+ } else {
+ IPVR_DEBUG_PM("Skiped power-on since already powered on\n");
+ }
+
+ if (!dev->irq_enabled) {
+ ret = drm_irq_install(dev, dev->irq);
+ if (ret) {
+ IPVR_ERROR("Failed to install drm irq handler: %d\n", ret);
+ }
+ }
+
+ return 0;
+}
+
+static int ipvr_pm_suspend(struct device *dev)
+{
+ struct platform_device *platformdev = to_platform_device(dev);
+ struct drm_device *drm_dev = platform_get_drvdata(platformdev);
+ IPVR_DEBUG_PM("PM suspend called\n");
+ return drm_dev? ipvr_drm_freeze(drm_dev): 0;
+}
+static int ipvr_pm_resume(struct device *dev)
+{
+ struct platform_device *platformdev = to_platform_device(dev);
+ struct drm_device *drm_dev = platform_get_drvdata(platformdev);
+ IPVR_DEBUG_PM("PM resume called\n");
+ return drm_dev? ipvr_drm_thaw(drm_dev): 0;
+}
+
+static const struct vm_operations_struct ipvr_gem_vm_ops = {
+ .fault = ipvr_gem_fault,
+ .open = drm_gem_vm_open,
+ .close = drm_gem_vm_close,
+};
+
+static struct drm_driver ipvr_drm_driver = {
+ .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_PRIME,
+ .load = ipvr_drm_load,
+ .unload = ipvr_drm_unload,
+ .open = ipvr_drm_open,
+ .preclose = ipvr_drm_preclose,
+ .irq_handler = ipvr_irq_handler,
+ .gem_free_object = ipvr_gem_free_object,
+ .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+ .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+ .gem_prime_export = drm_gem_prime_export,
+ .gem_prime_import = drm_gem_prime_import,
+ .gem_prime_get_sg_table = ipvr_gem_prime_get_sg_table,
+ .gem_prime_import_sg_table = ipvr_gem_prime_import_sg_table,
+ .gem_prime_pin = ipvr_gem_prime_pin,
+ .gem_prime_unpin = ipvr_gem_prime_unpin,
+#ifdef CONFIG_DEBUG_FS
+ .debugfs_init = ipvr_debugfs_init,
+ .debugfs_cleanup = ipvr_debugfs_cleanup,
+#endif
+ .gem_vm_ops = &ipvr_gem_vm_ops,
+ .ioctls = ipvr_gem_ioctls,
+ .num_ioctls = ARRAY_SIZE(ipvr_gem_ioctls),
+ .fops = &ipvr_fops,
+ .name = IPVR_DRIVER_NAME,
+ .desc = IPVR_DRIVER_DESC,
+ .date = IPVR_DRIVER_DATE,
+ .major = IPVR_DRIVER_MAJOR,
+ .minor = IPVR_DRIVER_MINOR,
+ .patchlevel = IPVR_DRIVER_PATCHLEVEL,
+};
+
+static int ipvr_plat_probe(struct platform_device *device)
+{
+ struct drm_device *drm_dev;
+ int ret;
+
+ drm_dev = drm_dev_alloc(&ipvr_drm_driver, &device->dev);
+ if (!drm_dev)
+ return -ENOMEM;
+
+ drm_dev->platformdev = device;
+ platform_set_drvdata(device, drm_dev);
+ ret = drm_dev_register(drm_dev, 0);
+ if (ret)
+ goto err_free;
+
+ DRM_INFO("Initialized IPVR on minor %d\n", drm_dev->primary->index);
+
+ return 0;
+err_free:
+ drm_dev_unref(drm_dev);
+ return ret;
+}
+
+static int ipvr_plat_remove(struct platform_device *device)
+{
+ struct drm_device *drm_dev = platform_get_drvdata(device);
+ if (drm_dev) {
+ drm_dev_unregister(drm_dev);
+ drm_dev_unref(drm_dev);
+ platform_set_drvdata(device, NULL);
+ }
+ return 0;
+}
+
+static struct dev_pm_ops ipvr_pm_ops = {
+ .suspend = ipvr_pm_suspend,
+ .resume = ipvr_pm_resume,
+ .freeze = ipvr_pm_suspend,
+ .thaw = ipvr_pm_resume,
+ .poweroff = ipvr_pm_suspend,
+ .restore = ipvr_pm_resume,
+#ifdef CONFIG_PM_RUNTIME
+ .runtime_suspend = ipvr_pm_suspend,
+ .runtime_resume = ipvr_pm_resume,
+#endif
+};
+
+static struct platform_driver ipvr_vlv_plat_driver = {
+ .driver = {
+ .name = "ipvr-ved-vlv",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &ipvr_pm_ops,
+#endif
+ },
+ .probe = ipvr_plat_probe,
+ .remove = ipvr_plat_remove,
+};
+
+module_platform_driver(ipvr_vlv_plat_driver);
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/ipvr/ipvr_drv.h b/drivers/gpu/drm/ipvr/ipvr_drv.h
new file mode 100644
index 0000000..87c7871
--- /dev/null
+++ b/drivers/gpu/drm/ipvr/ipvr_drv.h
@@ -0,0 +1,294 @@
+/**************************************************************************
+ * ipvr_drv.h: IPVR driver common header file
+ *
+ * Copyright (c) 2014 Intel Corporation, Hillsboro, OR, USA
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Authors:
+ * Fei Jiang <fei.jiang at intel.com>
+ * Yao Cheng <yao.cheng at intel.com>
+ *
+ **************************************************************************/
+
+#ifndef _IPVR_DRV_H_
+#define _IPVR_DRV_H_
+#include "drmP.h"
+#include "ipvr_drm.h"
+#include "ipvr_mmu.h"
+#include <linux/version.h>
+#include <linux/io-mapping.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/backlight.h>
+#include <linux/intel-iommu.h>
+#include <linux/kref.h>
+#include <linux/pm_qos.h>
+#include <linux/mmu_notifier.h>
+
+#define IPVR_DRIVER_AUTHOR "Intel, Inc."
+#define IPVR_DRIVER_NAME "ipvr"
+#define IPVR_DRIVER_DESC "PowerVR video drm driver"
+#define IPVR_DRIVER_DATE "20141113"
+#define IPVR_DRIVER_MAJOR 0
+#define IPVR_DRIVER_MINOR 1
+#define IPVR_DRIVER_PATCHLEVEL 0
+
+/* read/write domains */
+#define IPVR_GEM_DOMAIN_CPU 0x00000001
+#define IPVR_GEM_DOMAIN_VPU 0x00000002
+
+/* context ID and type */
+#define IPVR_CONTEXT_INVALID_ID 0
+#define IPVR_MIN_CONTEXT_ID 1
+#define IPVR_MAX_CONTEXT_ID 0xff
+
+/*
+ *Debug print bits setting
+ */
+#define IPVR_D_GENERAL (1 << 0)
+#define IPVR_D_INIT (1 << 1)
+#define IPVR_D_IRQ (1 << 2)
+#define IPVR_D_ENTRY (1 << 3)
+#define IPVR_D_PM (1 << 4)
+#define IPVR_D_REG (1 << 5)
+#define IPVR_D_VED (1 << 6)
+#define IPVR_D_WARN (1 << 7)
+
+#define IPVR_DEBUG_GENERAL(_fmt, _arg...) \
+ IPVR_DEBUG(IPVR_D_GENERAL, _fmt, ##_arg)
+#define IPVR_DEBUG_INIT(_fmt, _arg...) \
+ IPVR_DEBUG(IPVR_D_INIT, _fmt, ##_arg)
+#define IPVR_DEBUG_IRQ(_fmt, _arg...) \
+ IPVR_DEBUG(IPVR_D_IRQ, _fmt, ##_arg)
+#define IPVR_DEBUG_ENTRY(_fmt, _arg...) \
+ IPVR_DEBUG(IPVR_D_ENTRY, _fmt, ##_arg)
+#define IPVR_DEBUG_PM(_fmt, _arg...) \
+ IPVR_DEBUG(IPVR_D_PM, _fmt, ##_arg)
+#define IPVR_DEBUG_REG(_fmt, _arg...) \
+ IPVR_DEBUG(IPVR_D_REG, _fmt, ##_arg)
+#define IPVR_DEBUG_VED(_fmt, _arg...) \
+ IPVR_DEBUG(IPVR_D_VED, _fmt, ##_arg)
+#define IPVR_DEBUG_WARN(_fmt, _arg...) \
+ IPVR_DEBUG(IPVR_D_WARN, _fmt, ##_arg)
+
+#define IPVR_DEBUG(_flag, _fmt, _arg...) \
+ do { \
+ if (unlikely((_flag) & drm_ipvr_debug)) \
+ printk(KERN_INFO \
+ "[ipvr:0x%02x:%s] " _fmt , _flag, \
+ __func__ , ##_arg); \
+ } while (0)
+
+#define IPVR_ERROR(_fmt, _arg...) \
+ do { \
+ printk(KERN_ERR \
+ "[ipvr:ERROR:%s] " _fmt, \
+ __func__ , ##_arg); \
+ } while (0)
+
+#define IPVR_UDELAY(usec) \
+ do { \
+ cpu_relax(); \
+ } while (0)
+
+#define IPVR_REG_WRITE32(_val, _offs) \
+ iowrite32(_val, dev_priv->reg_base + (_offs))
+#define IPVR_REG_READ32(_offs) \
+ ioread32(dev_priv->reg_base + (_offs))
+
+typedef struct ipvr_validate_buffer ipvr_validate_buffer_t;
+
+#define to_ipvr_bo(x) container_of(x, struct drm_ipvr_gem_object, base)
+
+extern int drm_ipvr_debug;
+extern int drm_ipvr_freq;
+
+struct ipvr_validate_context {
+ ipvr_validate_buffer_t *buffers;
+ int used_buffers;
+ struct list_head validate_list;
+};
+
+struct ipvr_mmu_driver;
+struct ipvr_mmu_pd;
+
+struct ipvr_gem_stat {
+ /**
+ * Are we in a non-interruptible section of code?
+ */
+ bool interruptible;
+
+ /* accounting, useful for userland debugging */
+ spinlock_t object_stat_lock;
+ size_t allocated_memory;
+ int allocated_count;
+ size_t imported_memory;
+ int imported_count;
+ size_t exported_memory;
+ int exported_count;
+ size_t mmu_used_size;
+};
+
+struct ipvr_address_space {
+ struct drm_mm linear_mm;
+ struct drm_mm tiling_mm;
+ struct drm_device *dev;
+ unsigned long linear_start;
+ size_t linear_total;
+ unsigned long tiling_start;
+ size_t tiling_total;
+
+ /* need it during clear_range */
+ struct {
+ dma_addr_t addr;
+ struct page *page;
+ } scratch;
+};
+
+struct ipvr_fence_driver {
+ u16 sync_seq;
+ atomic_t signaled_seq;
+ unsigned long last_activity;
+ bool initialized;
+ spinlock_t fence_lock;
+};
+
+struct ipvr_context {
+ /* used to link into ipvr_ctx_list */
+ struct list_head head;
+ u32 ctx_id;
+ /* used to double check ctx when find with idr, may be removed */
+ struct drm_ipvr_file_private *ipvr_fpriv; /* DRM device file pointer */
+ u32 ctx_type;
+
+ u16 cur_seq;
+
+ /* for IMG DDK, only use tiling for 2k and 4k buffer stride */
+ /*
+ * following tiling strides for VED are supported:
+ * stride 0: 512 for scheme 0, 1024 for scheme 1
+ * stride 1: 1024 for scheme 0, 2048 for scheme 1
+ * stride 2: 2048 for scheme 0, 4096 for scheme 1
+ * stride 3: 4096 for scheme 0
+ */
+ u8 tiling_stride;
+ /*
+ * scheme 0: tile is 256x16, while minimal tile stride is 512
+ * scheme 1: tile is 512x8, while minimal tile stride is 1024
+ */
+ u8 tiling_scheme;
+};
+
+typedef struct drm_ipvr_private {
+ struct drm_device *dev;
+ struct pci_dev *pci_root;
+
+ /* IMG video context */
+ struct list_head ipvr_ctx_list;
+ spinlock_t ipvr_ctx_lock;
+ struct idr ipvr_ctx_idr;
+ struct ipvr_context default_ctx;
+
+ /* PM related */
+ atomic_t pending_events;
+
+ /* exec related */
+ struct ipvr_validate_context validate_ctx;
+
+ /* IMG MMU specific */
+ struct ipvr_mmu_driver *mmu;
+ /*struct ipvr_mmu_pd *pf_pd;*/
+ atomic_t ipvr_mmu_invaldc;
+
+ /* GEM mm related */
+ struct ipvr_gem_stat ipvr_stat;
+ struct kmem_cache *ipvr_bo_slab;
+ struct ipvr_address_space addr_space;
+
+ /* fence related */
+ u32 last_seq;
+ wait_queue_head_t fence_queue;
+ struct ipvr_fence_driver fence_drv;
+
+ /* MMIO window shared from parent device */
+ u8 __iomem* reg_base;
+
+ /*
+ * VED specific
+ */
+ struct ved_private *ved_private;
+}drm_ipvr_private_t;
+
+struct drm_ipvr_gem_object;
+
+/* VED private structure */
+struct ved_private {
+ struct drm_ipvr_private *dev_priv;
+
+ /* used to record seq got from irq fw-to-host msg */
+ u16 ved_cur_seq;
+
+ /*
+ * VED Rendec Memory
+ */
+ struct drm_ipvr_gem_object *ccb0;
+ u32 base_addr0;
+ struct drm_ipvr_gem_object *ccb1;
+ u32 base_addr1;
+ bool rendec_initialized;
+
+ /* VED firmware related */
+ struct drm_ipvr_gem_object *fw_bo;
+ u32 fw_offset;
+ u32 mtx_mem_size;
+ bool fw_loaded_to_bo;
+ bool ved_fw_loaded;
+ void *ved_fw_ptr;
+ size_t ved_fw_size;
+
+ /*
+ * ved command queue
+ */
+ spinlock_t ved_lock;
+ struct mutex ved_mutex;
+ struct list_head ved_queue;
+ /* busy means cmd submitted to fw, while irq hasn't been receieved */
+ bool ved_busy;
+ u32 ved_dash_access_ctrl;
+
+ /* error concealment related */
+ struct drm_file *tfile; /* protected by cmdbuf_mutex */
+ int num_cmd;
+
+ /* pm related */
+ int ved_needs_reset;
+
+ /* current ved decode context */
+ struct ipvr_context *ipvr_ctx;
+
+ struct page *mmu_recover_page;
+};
+
+struct drm_ipvr_file_private {
+ u32 ctx_id;
+};
+
+/* helpers for runtime pm */
+int ipvr_runtime_pm_get(struct drm_ipvr_private *dev_priv);
+int ipvr_runtime_pm_put(struct drm_ipvr_private *dev_priv, bool async);
+int ipvr_runtime_pm_put_all(struct drm_ipvr_private *dev_priv, bool async);
+
+#endif
diff --git a/drivers/gpu/drm/ipvr/ipvr_exec.c b/drivers/gpu/drm/ipvr/ipvr_exec.c
new file mode 100644
index 0000000..e84cf9c
--- /dev/null
+++ b/drivers/gpu/drm/ipvr/ipvr_exec.c
@@ -0,0 +1,623 @@
+/**************************************************************************
+ * ipvr_exec.c: IPVR command buffer execution
+ *
+ * Copyright (c) 2014 Intel Corporation, Hillsboro, OR, USA
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Authors:
+ * Fei Jiang <fei.jiang at intel.com>
+ * Yao Cheng <yao.cheng at intel.com>
+ *
+ **************************************************************************/
+
+#include "ipvr_exec.h"
+#include "ipvr_gem.h"
+#include "ipvr_mmu.h"
+#include "ipvr_bo.h"
+#include "ipvr_fence.h"
+#include "ipvr_trace.h"
+#include "ved_fw.h"
+#include "ved_msg.h"
+#include "ved_reg.h"
+#include "ved_pm.h"
+#include "ved_cmd.h"
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+
+static inline bool ipvr_bo_is_reserved(struct drm_ipvr_gem_object *obj)
+{
+ return atomic_read(&obj->reserved);
+}
+
+static int
+ipvr_bo_wait_unreserved(struct drm_ipvr_gem_object *obj, bool interruptible)
+{
+ if (interruptible) {
+ return wait_event_interruptible(obj->event_queue,
+ !ipvr_bo_is_reserved(obj));
+ } else {
+ wait_event(obj->event_queue, !ipvr_bo_is_reserved(obj));
+ return 0;
+ }
+}
+
+/**
+ * ipvr_bo_reserve - reserve the given bo
+ *
+ * @obj: The buffer object to reserve.
+ * @interruptible: whether the waiting is interruptible or not.
+ * @no_wait: flag to indicate returning immediately
+ *
+ * Returns: 0 if successful, error code otherwise
+ */
+int ipvr_bo_reserve(struct drm_ipvr_gem_object *obj,
+ bool interruptible, bool no_wait)
+{
+ int ret;
+
+ while (unlikely(atomic_xchg(&obj->reserved, 1) != 0)) {
+ if (no_wait)
+ return -EBUSY;
+ IPVR_DEBUG_GENERAL("wait bo unreserved, add to wait queue.\n");
+ ret = ipvr_bo_wait_unreserved(obj, interruptible);
+ if (unlikely(ret))
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * ipvr_bo_unreserve - unreserve the given bo
+ *
+ * @obj: The buffer object to reserve.
+ *
+ * No return value.
+ */
+void ipvr_bo_unreserve(struct drm_ipvr_gem_object *obj)
+{
+ atomic_set(&obj->reserved, 0);
+ wake_up_all(&obj->event_queue);
+}
+
+static void ipvr_backoff_reservation(struct list_head *list)
+{
+ struct ipvr_validate_buffer *entry;
+
+ list_for_each_entry(entry, list, head) {
+ struct drm_ipvr_gem_object *obj = entry->ipvr_gem_bo;
+ if (!atomic_read(&obj->reserved))
+ continue;
+ atomic_set(&obj->reserved, 0);
+ wake_up_all(&obj->event_queue);
+ }
+}
+
+/*
+ * ipvr_reserve_buffers - Reserve buffers for validation.
+ *
+ * @list: points to a bo list to be backoffed
+ *
+ * If a buffer in the list is marked for CPU access, we back off and
+ * wait for that buffer to become free for VPU access.
+ *
+ * If a buffer is reserved for another validation, the validator with
+ * the highest validation sequence backs off and waits for that buffer
+ * to become unreserved. This prevents deadlocks when validating multiple
+ * buffers in different orders.
+ *
+ * Returns:
+ * 0 on success, error code on failure.
+ */
+int ipvr_reserve_buffers(struct list_head *list)
+{
+ struct ipvr_validate_buffer *entry;
+ int ret;
+
+ if (list_empty(list))
+ return 0;
+
+ list_for_each_entry(entry, list, head) {
+ struct drm_ipvr_gem_object *bo = entry->ipvr_gem_bo;
+
+ ret = ipvr_bo_reserve(bo, true, true);
+ switch (ret) {
+ case 0:
+ break;
+ case -EBUSY:
+ ret = ipvr_bo_reserve(bo, true, false);
+ if (!ret)
+ break;
+ else
+ goto err;
+ default:
+ goto err;
+ }
+ }
+
+ return 0;
+err:
+ ipvr_backoff_reservation(list);
+ return ret;
+}
+
+/**
+ * ipvr_set_tile - global setting of tiling info
+ *
+ * @dev: the ipvr drm device
+ * @tiling_scheme: see ipvr_drm.h for details
+ * @tiling_stride: see ipvr_drm.h for details
+ *
+ * vxd392 hardware supports only one tile region so this configuration
+ * is global.
+ */
+void ipvr_set_tile(struct drm_ipvr_private *dev_priv,
+ u8 tiling_scheme, u8 tiling_stride)
+{
+ u32 cmd;
+ u32 start = IPVR_MEM_MMU_TILING_START;
+ u32 end = IPVR_MEM_MMU_TILING_END;
+
+ /* Enable memory tiling */
+ cmd = ((start >> 20) + (((end >> 20) - 1) << 12) +
+ ((0x8 | tiling_stride) << 24));
+ IPVR_DEBUG_GENERAL("VED: MMU Tiling register0 %08x.\n", cmd);
+ IPVR_DEBUG_GENERAL("Region 0x%08x-0x%08x.\n", start, end);
+ IPVR_REG_WRITE32(cmd, MSVDX_MMU_TILE_BASE0_OFFSET);
+
+ /* we need set tile format as 512x8 on Baytrail, which is shceme 1 */
+ IPVR_REG_WRITE32(tiling_scheme << 3, MSVDX_MMU_CONTROL2_OFFSET);
+}
+
+/**
+ * ipvr_find_ctx_with_fence - lookup the context with given fence seqno
+ *
+ * @dev_priv: the ipvr drm device
+ * @fence: fence seqno generated by the context
+ *
+ * Returns:
+ * context pointer if found.
+ * NULL if not found.
+ */
+struct ipvr_context*
+ipvr_find_ctx_with_fence(struct drm_ipvr_private *dev_priv, u16 fence)
+{
+ struct ipvr_context *pos = NULL, *n = NULL;
+
+ if (unlikely(dev_priv == NULL)) {
+ return NULL;
+ }
+
+ spin_lock(&dev_priv->ipvr_ctx_lock);
+ list_for_each_entry_safe(pos, n, &dev_priv->ipvr_ctx_list, head) {
+ if (pos->cur_seq == fence) {
+ spin_unlock(&dev_priv->ipvr_ctx_lock);
+ return pos;
+ }
+ }
+ spin_unlock(&dev_priv->ipvr_ctx_lock);
+
+ return NULL;
+}
+
+static void ipvr_unreference_buffers(struct ipvr_validate_context *context)
+{
+ struct ipvr_validate_buffer *entry, *next;
+ struct drm_ipvr_gem_object *obj;
+ struct list_head *list = &context->validate_list;
+
+ list_for_each_entry_safe(entry, next, list, head) {
+ obj = entry->ipvr_gem_bo;
+ list_del(&entry->head);
+ drm_gem_object_unreference_unlocked(&obj->base);
+ context->used_buffers--;
+ }
+}
+
+static int ipvr_update_buffers(struct drm_file *file_priv,
+ struct ipvr_validate_context *context,
+ u64 buffer_list,
+ int count)
+{
+ struct ipvr_validate_buffer *entry;
+ struct drm_ipvr_gem_exec_object __user *val_arg
+ = (struct drm_ipvr_gem_exec_object __user *)(uintptr_t)buffer_list;
+
+ if (list_empty(&context->validate_list))
+ return 0;
+
+ list_for_each_entry(entry, &context->validate_list, head) {
+ if (!val_arg) {
+ IPVR_DEBUG_WARN("unexpected end of val_arg list!!!\n");
+ return -EINVAL;
+ }
+ if (unlikely(copy_to_user(val_arg, &entry->val_req,
+ sizeof(entry->val_req)))) {
+ IPVR_ERROR("copy_to_user fault.\n");
+ return -EFAULT;
+ }
+ val_arg ++;
+ }
+ return 0;
+}
+
+static int ipvr_reference_buffers(struct drm_file *file_priv,
+ struct ipvr_validate_context *context,
+ u64 buffer_list,
+ int count)
+{
+ struct drm_device *dev = file_priv->minor->dev;
+ struct drm_ipvr_gem_exec_object __user *val_arg
+ = (struct drm_ipvr_gem_exec_object __user *)(uintptr_t)buffer_list;
+ struct ipvr_validate_buffer *item;
+ struct drm_ipvr_gem_object *obj;
+ int ret = 0;
+ int i = 0;
+
+ for (i = 0; i < count; ++i) {
+ if (unlikely(context->used_buffers >= IPVR_NUM_VALIDATE_BUFFERS)) {
+ IPVR_ERROR("Too many buffers on validate list.\n");
+ ret = -EINVAL;
+ goto out_err;
+ }
+ item = &context->buffers[context->used_buffers];
+ if (unlikely(copy_from_user(&item->val_req, val_arg,
+ sizeof(item->val_req)) != 0)) {
+ IPVR_ERROR("copy_from_user fault.\n");
+ ret = -EFAULT;
+ goto out_err;
+ }
+ INIT_LIST_HEAD(&item->head);
+ obj = to_ipvr_bo(drm_gem_object_lookup(dev, file_priv,
+ item->val_req.handle));
+ if (&obj->base == NULL) {
+ IPVR_ERROR("cannot find obj for handle %u at position %d.\n",
+ item->val_req.handle, i);
+ ret = -ENOENT;
+ goto out_err;
+ }
+ item->ipvr_gem_bo = obj;
+
+ list_add_tail(&item->head, &context->validate_list);
+ context->used_buffers++;
+
+ val_arg++;
+ }
+
+ return 0;
+
+out_err:
+ ipvr_unreference_buffers(context);
+ return ret;
+}
+
+static int ipvr_fixup_reloc_entries(struct drm_device *dev,
+ struct drm_file *filp,
+ struct ipvr_validate_buffer *val_obj)
+{
+ int i, ret;
+ u64 mmu_offset;
+ struct drm_ipvr_gem_object *obj, *target_obj;
+ struct drm_ipvr_gem_exec_object *exec_obj = &val_obj->val_req;
+ struct drm_ipvr_gem_relocation_entry __user *reloc_entries
+ = (struct drm_ipvr_gem_relocation_entry __user *)(uintptr_t)exec_obj->relocs_ptr;
+ struct drm_ipvr_gem_relocation_entry local_reloc_entry;
+
+ obj = val_obj->ipvr_gem_bo;
+ if (!obj)
+ return -ENOENT;
+
+ /* todo: check write access */
+
+ /* overwrite user content and update relocation entries */
+ mmu_offset = ipvr_gem_object_mmu_offset(obj);
+ if (mmu_offset != exec_obj->offset) {
+ exec_obj->offset = mmu_offset;
+ IPVR_DEBUG_GENERAL("Fixup BO %u offset to 0x%llx\n",
+ exec_obj->handle, exec_obj->offset);
+ }
+ for (i = 0; i < exec_obj->relocation_count; ++i) {
+ if (unlikely(copy_from_user(&local_reloc_entry, &reloc_entries[i],
+ sizeof(local_reloc_entry)) != 0)) {
+ IPVR_ERROR("copy_from_user fault.\n");
+ return -EFAULT;
+ }
+ target_obj = to_ipvr_bo(drm_gem_object_lookup(dev, filp,
+ local_reloc_entry.target_handle));
+ if (&target_obj->base == NULL) {
+ IPVR_ERROR("cannot find obj for handle %u at position %d.\n",
+ local_reloc_entry.target_handle, i);
+ return -ENOENT;
+ }
+ ret = ipvr_gem_object_apply_reloc(obj,
+ local_reloc_entry.offset,
+ local_reloc_entry.delta + ipvr_gem_object_mmu_offset(target_obj));
+ if (ret) {
+ IPVR_ERROR("Failed applying reloc: %d\n", ret);
+ drm_gem_object_unreference_unlocked(&target_obj->base);
+ return ret;
+ }
+ if (unlikely(copy_to_user(&reloc_entries[i], &local_reloc_entry,
+ sizeof(local_reloc_entry)) != 0)) {
+ IPVR_DEBUG_WARN("copy_to_user fault.\n");
+ }
+ IPVR_DEBUG_GENERAL("Fixup offset %llx in BO %u to 0x%lx\n",
+ local_reloc_entry.offset, exec_obj->handle,
+ local_reloc_entry.delta + ipvr_gem_object_mmu_offset(target_obj));
+ drm_gem_object_unreference_unlocked(&target_obj->base);
+ }
+ return 0;
+}
+
+static int ipvr_fixup_relocs(struct drm_device *dev,
+ struct drm_file *filp,
+ struct ipvr_validate_context *context)
+{
+ int ret;
+ struct ipvr_validate_buffer *entry;
+
+ if (list_empty(&context->validate_list)) {
+ IPVR_DEBUG_WARN("No relocs required in validate contex, skip\n");
+ return 0;
+ }
+
+ list_for_each_entry(entry, &context->validate_list, head) {
+ IPVR_DEBUG_GENERAL("Fixing up reloc for BO handle %u\n",
+ entry->val_req.handle);
+ ret = ipvr_fixup_reloc_entries(dev, filp, entry);
+ if (ret) {
+ IPVR_ERROR("Failed to fixup reloc for BO handle %u\n",
+ entry->val_req.handle);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static int ipvr_validate_buffer_list(struct drm_file *file_priv,
+ struct ipvr_validate_context *context,
+ bool *need_fixup_relocs,
+ struct drm_ipvr_gem_object **cmd_buffer)
+{
+ struct ipvr_validate_buffer *entry;
+ struct drm_ipvr_gem_object *obj;
+ struct list_head *list = &context->validate_list;
+ int ret = 0;
+ u64 real_mmu_offset;
+
+ list_for_each_entry(entry, list, head) {
+ obj = entry->ipvr_gem_bo;
+ /**
+ * need validate bo locate in the mmu space
+ * check if presumed offset is correct
+ * with ved_check_presumed, if presume is not correct,
+ * call fixup relocs with ved_fixup_relocs.
+ * current implementation doesn't support shrink/evict,
+ * so needn't validate mmu offset.
+ * need be implemented in the future if shrink/evict
+ * is supported.
+ */
+ real_mmu_offset = ipvr_gem_object_mmu_offset(obj);
+ if (IPVR_IS_ERR(real_mmu_offset))
+ return -ENOENT;
+ if (entry->val_req.offset != real_mmu_offset) {
+ IPVR_DEBUG_GENERAL("BO %u offset doesn't match MMU, need fixup reloc\n", entry->val_req.handle);
+ *need_fixup_relocs = true;
+ }
+ if (entry->val_req.flags & IPVR_EXEC_OBJECT_SUBMIT) {
+ if (*cmd_buffer != NULL) {
+ IPVR_ERROR("Only one BO can be submitted in one exec ioctl\n");
+ return -EINVAL;
+ }
+ *cmd_buffer = obj;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * ipvr_gem_do_execbuffer - lookup the context with given fence seqno
+ *
+ * @dev: the ipvr drm device
+ * @file_priv: the ipvr drm file pointer
+ * @args: input argument passed from userland
+ * @vm: ipvr address space for all the bo to bind to
+ *
+ * Returns:
+ * -ENOENT if context not found, or cmdbuf bo not found
+ * -EINVAL if referencing buffer fails, or executing cmdbuf fails
+ * -EINTR if fails to lock mutex
+ * -EBUSY if fails to get power well, or execution fails
+ * -ERESTARTSYS if reservating buffer fails
+ * -ENOMEM if execution fails
+ * -EFAULT if execution fails
+ * 0 if successful
+ */
+static int ipvr_gem_do_execbuffer(struct drm_device *dev,
+ struct drm_file *file_priv,
+ struct drm_ipvr_gem_execbuffer *args,
+ struct ipvr_address_space *vm)
+{
+ drm_ipvr_private_t *dev_priv = dev->dev_private;
+ struct ipvr_validate_context *context = &dev_priv->validate_ctx;
+ struct ved_private *ved_priv = dev_priv->ved_private;
+ struct drm_ipvr_gem_object *cmd_buffer = NULL;
+ struct ipvr_context *ipvr_ctx = NULL;
+ int ret, ctx_id;
+ bool need_fixup_relocs = false;
+
+ /* if not pass 0, use default context instead */
+ if (args->ctx_id == 0)
+ ctx_id = dev_priv->default_ctx.ctx_id;
+ else
+ ctx_id = args->ctx_id;
+
+ IPVR_DEBUG_GENERAL("try to find ctx according ctx_id %d.\n", ctx_id);
+ ipvr_ctx = (struct ipvr_context *)
+ idr_find(&dev_priv->ipvr_ctx_idr, ctx_id);
+ if (!ipvr_ctx) {
+ IPVR_DEBUG_WARN("video ctx is not found.\n");
+ return -ENOENT;
+ }
+
+ IPVR_DEBUG_GENERAL("reference all buffers passed through buffer_list.\n");
+ ret = ipvr_reference_buffers(file_priv, context,
+ args->buffers_ptr, args->buffer_count);
+ if (unlikely(ret)) {
+ IPVR_DEBUG_WARN("reference buffer failed: %d.\n", ret);
+ return ret;
+ }
+
+ IPVR_DEBUG_GENERAL("reserve all buffers to make them not accessed "
+ "by other threads.\n");
+ ret = ipvr_reserve_buffers(&context->validate_list);
+ if (unlikely(ret)) {
+ IPVR_ERROR("reserve buffers failed.\n");
+ /* -EBUSY or -ERESTARTSYS */
+ goto out_unref_buf;
+ }
+
+ IPVR_DEBUG_GENERAL("validate buffer list, mainly check "
+ "the bo mmu offset.\n");
+ ret = ipvr_validate_buffer_list(file_priv, context, &need_fixup_relocs, &cmd_buffer);
+ if (unlikely(ret)) {
+ IPVR_ERROR("validate buffers failed: %d.\n", ret);
+ goto out_backoff_reserv;
+ }
+
+ if (unlikely(cmd_buffer == NULL)) {
+ IPVR_ERROR("No cmd BO found.\n");
+ ret = -EINVAL;
+ goto out_backoff_reserv;
+ }
+
+ if (unlikely(need_fixup_relocs)) {
+ ret = ipvr_fixup_relocs(dev, file_priv, context);
+ if (ret) {
+ IPVR_ERROR("fixup relocs failed.\n");
+ goto out_backoff_reserv;
+ }
+ }
+
+#if 0
+ bo = idr_find(&file_priv->object_idr, args->cmdbuf_handle);
+ if (!bo) {
+ IPVR_DEBUG_WARN("Invalid cmd object handle 0x%x.\n",
+ args->cmdbuf_handle);
+ ret = -EINVAL;
+ goto out_backoff_reserv;
+ }
+
+ cmd_buffer = to_ipvr_bo(bo);
+#endif
+ /**
+ * check contex id and type
+ */
+ /*
+ * only VED is supported currently
+ */
+ if (ipvr_ctx->ctx_type == IPVR_CONTEXT_TYPE_VED)
+ {
+ /* fixme: should support non-zero start_offset */
+ if (unlikely(args->exec_start_offset != 0)) {
+ IPVR_ERROR("Unsupported exec_start_offset %u\n", args->exec_start_offset);
+ ret = -EINVAL;
+ goto out_backoff_reserv;
+ }
+
+ ret = mutex_lock_interruptible(&ved_priv->ved_mutex);
+ if (unlikely(ret)) {
+ IPVR_ERROR("Error get VED mutex: %d\n", ret);
+ /* -EINTR */
+ goto out_backoff_reserv;
+ }
+
+ IPVR_DEBUG_GENERAL("parse cmd buffer and send to VED.\n");
+ ret = ved_cmdbuf_video(ved_priv, cmd_buffer,
+ args->exec_len, ipvr_ctx );
+ if (unlikely(ret)) {
+ IPVR_ERROR("ved_cmdbuf_video returns %d.\n", ret);
+ /* -EINVAL, -ENOMEM, -EFAULT, -EBUSY */
+ mutex_unlock(&ved_priv->ved_mutex);
+ goto out_backoff_reserv;
+ }
+
+ mutex_unlock(&ved_priv->ved_mutex);
+ }
+
+ /**
+ * update mmu_offsets and fence fds to user
+ */
+ ret = ipvr_update_buffers(file_priv, context,
+ args->buffers_ptr, args->buffer_count);
+ if (unlikely(ret)) {
+ IPVR_DEBUG_WARN("ipvr_update_buffers returns error %d.\n", ret);
+ ret = 0;
+ }
+
+out_backoff_reserv:
+ IPVR_DEBUG_GENERAL("unreserve buffer list.\n");
+ ipvr_backoff_reservation(&context->validate_list);
+out_unref_buf:
+ IPVR_DEBUG_GENERAL("unref bufs which are refered during bo lookup.\n");
+ ipvr_unreference_buffers(context);
+ return ret;
+}
+
+/**
+ * ipvr_gem_do_execbuffer - lookup the context with given fence seqno
+ *
+ * ioctl entry for DRM_IPVR_GEM_EXECBUFFER
+ *
+ * Returns:
+ * -ENOENT if context not found, or cmdbuf bo not found
+ * -EINVAL if referencing buffer fails, or executing cmdbuf fails
+ * -EINTR if fails to lock mutex
+ * -EBUSY if fails to get power well, or execution fails
+ * -ERESTARTSYS if reservating buffer fails
+ * -ENOMEM if execution fails
+ * -EFAULT if execution fails
+ * 0 if successful
+ */
+int ipvr_gem_execbuffer_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_ipvr_private *dev_priv = dev->dev_private;
+ struct drm_ipvr_gem_execbuffer *args = data;
+ int ret;
+ struct ipvr_validate_context *context = &dev_priv->validate_ctx;
+
+ if (!context || !context->buffers) {
+ ret = -EINVAL;
+ return ret;
+ }
+
+ context->used_buffers = 0;
+
+ if (args->buffer_count < 1 ||
+ args->buffer_count >
+ (UINT_MAX / sizeof(struct ipvr_validate_buffer))) {
+ IPVR_ERROR("validate %d buffers.\n", args->buffer_count);
+ return -EINVAL;
+ }
+
+ trace_ipvr_execbuffer(args);
+ ret = ipvr_gem_do_execbuffer(dev, file_priv, args,
+ &dev_priv->addr_space);
+ return ret;
+}
diff --git a/drivers/gpu/drm/ipvr/ipvr_exec.h b/drivers/gpu/drm/ipvr/ipvr_exec.h
new file mode 100644
index 0000000..cd174a8
--- /dev/null
+++ b/drivers/gpu/drm/ipvr/ipvr_exec.h
@@ -0,0 +1,57 @@
+/**************************************************************************
+ * ipvr_exec.h: IPVR header file for command buffer execution
+ *
+ * Copyright (c) 2014 Intel Corporation, Hillsboro, OR, USA
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Authors:
+ * Fei Jiang <fei.jiang at intel.com>
+ * Yao Cheng <yao.cheng at intel.com>
+ *
+ **************************************************************************/
+
+#ifndef _IPVR_EXEC_H_
+#define _IPVR_EXEC_H_
+
+#include "ipvr_drv.h"
+#include "ipvr_drm.h"
+#include "ipvr_gem.h"
+#include "ipvr_fence.h"
+
+struct drm_ipvr_private;
+
+#define IPVR_NUM_VALIDATE_BUFFERS 2048
+#define IPVR_MAX_RELOC_PAGES 1024
+
+struct ipvr_validate_buffer {
+ struct drm_ipvr_gem_exec_object val_req;
+ struct list_head head;
+ struct drm_ipvr_gem_object *ipvr_gem_bo;
+ struct ipvr_fence *old_fence;
+};
+
+int ipvr_bo_reserve(struct drm_ipvr_gem_object *obj,
+ bool interruptible, bool no_wait);
+
+void ipvr_bo_unreserve(struct drm_ipvr_gem_object *obj);
+
+struct ipvr_context*
+ipvr_find_ctx_with_fence(struct drm_ipvr_private *dev_priv, u16 fence);
+
+void ipvr_set_tile(struct drm_ipvr_private *dev_priv,
+ u8 tiling_scheme, u8 tiling_stride);
+
+#endif
diff --git a/drivers/gpu/drm/ipvr/ipvr_fence.c b/drivers/gpu/drm/ipvr/ipvr_fence.c
new file mode 100644
index 0000000..d18a602
--- /dev/null
+++ b/drivers/gpu/drm/ipvr/ipvr_fence.c
@@ -0,0 +1,490 @@
+/**************************************************************************
+ * ipvr_fence.c: IPVR fence handling to track command exectuion status
+ *
+ * Copyright (c) 2014 Intel Corporation, Hillsboro, OR, USA
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Authors:
+ * Fei Jiang <fei.jiang at intel.com>
+ * Yao Cheng <yao.cheng at intel.com>
+ *
+ **************************************************************************/
+
+#include "ipvr_fence.h"
+#include "ipvr_exec.h"
+#include "ipvr_bo.h"
+#include "ipvr_trace.h"
+#include "ved_reg.h"
+#include "ved_fw.h"
+#include "ved_cmd.h"
+#include <linux/debugfs.h>
+#include <linux/export.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/anon_inodes.h>
+
+/**
+ * ipvr_fence_create - create and init a fence
+ *
+ * @dev_priv: drm_ipvr_private pointer
+ * @fence: ipvr fence object
+ * @fence_fd: file descriptor for exporting fence
+ *
+ * Create a fence, actually the fence is written to ipvr through msg.
+ * exporting a new file descriptor to userspace.
+ * Returns 0 on success, -ENOMEM on failure.
+ */
+int
+ipvr_fence_create(struct drm_ipvr_private *dev_priv,
+ struct ipvr_fence **fence)
+{
+ unsigned long irq_flags;
+ u16 old_seq;
+ struct ved_private *ved_priv;
+
+ ved_priv = dev_priv->ved_private;
+
+ *fence = kmalloc(sizeof(struct ipvr_fence), GFP_KERNEL);
+ if ((*fence) == NULL) {
+ return -ENOMEM;
+ }
+ if (IS_ERR((*fence)->file)) {
+ kfree(*fence);
+ IPVR_ERROR("ALARM!!! anon_inode_getfile call failed\n");
+ return -ENOMEM;
+ }
+
+ kref_init(&((*fence)->kref));
+ (*fence)->dev_priv = dev_priv;
+
+ spin_lock_irqsave(&dev_priv->fence_drv.fence_lock, irq_flags);
+ /* cmds in one batch use different fence value */
+ old_seq = dev_priv->fence_drv.sync_seq;
+ dev_priv->fence_drv.sync_seq = dev_priv->last_seq++;
+ dev_priv->fence_drv.sync_seq <<= 4;
+ dev_priv->fence_drv.sync_seq += ved_priv->num_cmd;
+ (*fence)->seq = dev_priv->fence_drv.sync_seq;
+
+ spin_unlock_irqrestore(&dev_priv->fence_drv.fence_lock, irq_flags);
+
+ kref_get(&(*fence)->kref);
+ IPVR_DEBUG_GENERAL("fence is created and its seq is %u (0x%04x).\n",
+ (*fence)->seq, (*fence)->seq);
+ return 0;
+}
+
+/**
+ * ipvr_fence_destroy - destroy a fence
+ *
+ * @kref: fence kref
+ *
+ * Frees the fence object (all asics).
+ */
+static void ipvr_fence_destroy(struct kref *kref)
+{
+ struct ipvr_fence *fence;
+
+ fence = container_of(kref, struct ipvr_fence, kref);
+ kfree(fence);
+}
+
+/**
+ * ipvr_fence_process - process a fence
+ *
+ * @dev_priv: drm_ipvr_private pointer
+ * @seq: indicate the fence seq has been signaled
+ * @err: indicate if err happened, for future use
+ *
+ * Checks the current fence value and wakes the fence queue
+ * if the sequence number has increased (all asics).
+ */
+void ipvr_fence_process(struct drm_ipvr_private *dev_priv, u16 seq, u8 err)
+{
+ int signaled_seq_int;
+ u16 signaled_seq;
+ u16 last_emitted;
+
+ signaled_seq_int = atomic_read(&dev_priv->fence_drv.signaled_seq);
+ signaled_seq = (u16)signaled_seq_int;
+ last_emitted = dev_priv->fence_drv.sync_seq;
+
+ if (ipvr_seq_after(seq, last_emitted)) {
+ IPVR_DEBUG_WARN("seq error, seq is %u, signaled_seq is %u, "
+ "last_emitted is %u.\n",
+ seq, signaled_seq, last_emitted);
+ return;
+ }
+ if (ipvr_seq_after(seq, signaled_seq)) {
+ atomic_xchg(&dev_priv->fence_drv.signaled_seq, seq);
+ dev_priv->fence_drv.last_activity = jiffies;
+ IPVR_DEBUG_GENERAL("last emitted seq %u is updated.\n", seq);
+ wake_up_all(&dev_priv->fence_queue);
+ }
+}
+
+/**
+ * ipvr_fence_signaled - check if a fence sequeuce number has signaled
+ *
+ * @dev_priv: ipvr device pointer
+ * @seq: sequence number
+ *
+ * Check if the last singled fence sequnce number is >= the requested
+ * sequence number (all asics).
+ * Returns true if the fence has signaled (current fence value
+ * is >= requested value) or false if it has not (current fence
+ * value is < the requested value.
+ */
+static bool ipvr_fence_signaled(struct drm_ipvr_private *dev_priv, u16 seq)
+{
+ u16 curr_seq, signaled_seq;
+ unsigned long irq_flags;
+ spin_lock_irqsave(&dev_priv->fence_drv.fence_lock, irq_flags);
+ curr_seq = dev_priv->ved_private->ved_cur_seq;
+ signaled_seq = atomic_read(&dev_priv->fence_drv.signaled_seq);
+
+ if (ipvr_seq_after(seq, signaled_seq)) {
+ /* poll new last sequence at least once */
+ ipvr_fence_process(dev_priv, curr_seq, IPVR_CMD_SUCCESS);
+ signaled_seq = atomic_read(&dev_priv->fence_drv.signaled_seq);
+ if (ipvr_seq_after(seq, signaled_seq)) {
+ spin_unlock_irqrestore(&dev_priv->fence_drv.fence_lock,
+ irq_flags);
+ return false;
+ }
+ }
+ spin_unlock_irqrestore(&dev_priv->fence_drv.fence_lock, irq_flags);
+ return true;
+}
+
+/**
+ * ipvr_fence_lockup - ipvr lockup is detected
+ *
+ * @dev_priv: ipvr device pointer
+ * @fence: lockup detected when wait the specific fence
+ *
+ * During the calling of ipvr_fence_wait, if wait to timeout,
+ * indicate lockup happened, need flush cmd queue and reset ved
+ * If ipvr_fence_wait_empty_locked encounter lockup, fence is NULL
+ */
+static void
+ipvr_fence_lockup(struct drm_ipvr_private *dev_priv, struct ipvr_fence *fence)
+{
+ unsigned long irq_flags;
+ struct ved_private *ved_priv = dev_priv->ved_private;
+
+ IPVR_DEBUG_WARN("timeout detected, flush queued cmd, maybe lockup.\n");
+ IPVR_DEBUG_WARN("MSVDX_COMMS_FW_STATUS reg is 0x%x.\n",
+ IPVR_REG_READ32(MSVDX_COMMS_FW_STATUS));
+
+ if (fence) {
+ spin_lock_irqsave(&dev_priv->fence_drv.fence_lock, irq_flags);
+ ipvr_fence_process(dev_priv, fence->seq, IPVR_CMD_LOCKUP);
+ spin_unlock_irqrestore(&dev_priv->fence_drv.fence_lock, irq_flags);
+ }
+
+ /* should behave according to ctx type in the future */
+ ved_flush_cmd_queue(dev_priv->ved_private);
+
+ ved_priv->ved_needs_reset = 1;
+}
+
+/**
+ * ipvr_fence_wait_seq - wait for a specific sequence number
+ *
+ * @dev_priv: ipvr device pointer
+ * @target_seq: sequence number we want to wait for
+ * @intr: use interruptable sleep
+ *
+ * Wait for the requested sequence number to be written.
+ * @intr selects whether to use interruptable (true) or non-interruptable
+ * (false) sleep when waiting for the sequence number.
+ * Returns 0 if the sequence number has passed, error for all other cases.
+ * -EDEADLK is returned when a VPU lockup has been detected.
+ */
+static int ipvr_fence_wait_seq(struct drm_ipvr_private *dev_priv,
+ u16 target_seq, bool intr)
+{
+ struct ipvr_fence_driver *fence_drv = &dev_priv->fence_drv;
+ unsigned long timeout, last_activity;
+ u16 signaled_seq;
+ int ret;
+ unsigned long irq_flags;
+ bool signaled;
+ spin_lock_irqsave(&dev_priv->fence_drv.fence_lock, irq_flags);
+
+ while (ipvr_seq_after(target_seq,
+ (u16)atomic_read(&fence_drv->signaled_seq))) {
+ /* seems the fence_drv->last_activity is useless? */
+ timeout = IPVR_FENCE_JIFFIES_TIMEOUT;
+ signaled_seq = atomic_read(&fence_drv->signaled_seq);
+ /* save last activity valuee, used to check for VPU lockups */
+ last_activity = fence_drv->last_activity;
+
+ spin_unlock_irqrestore(&dev_priv->fence_drv.fence_lock, irq_flags);
+ if (intr) {
+ ret = wait_event_interruptible_timeout(
+ dev_priv->fence_queue,
+ (signaled = ipvr_fence_signaled(dev_priv, target_seq)),
+ timeout);
+ } else {
+ ret = wait_event_timeout(
+ dev_priv->fence_queue,
+ (signaled = ipvr_fence_signaled(dev_priv, target_seq)),
+ timeout);
+ }
+ spin_lock_irqsave(&dev_priv->fence_drv.fence_lock, irq_flags);
+
+ if (unlikely(!signaled)) {
+ /* we were interrupted for some reason and fence
+ * isn't signaled yet, resume waiting until timeout */
+ if (unlikely(ret < 0)) {
+ /* should return -ERESTARTSYS,
+ * interrupted by a signal */
+ continue;
+ }
+
+ /* check if sequence value has changed since
+ * last_activity */
+ if (signaled_seq !=
+ atomic_read(&fence_drv->signaled_seq)) {
+ continue;
+ }
+
+ if (last_activity != fence_drv->last_activity) {
+ continue;
+ }
+
+ /* lockup happen, it is better have some reg to check */
+ IPVR_DEBUG_WARN("VPU lockup (waiting for 0x%0x last "
+ "signaled fence id 0x%x).\n",
+ target_seq, signaled_seq);
+
+ /* change last activity so nobody else
+ * think there is a lockup */
+ fence_drv->last_activity = jiffies;
+ spin_unlock_irqrestore(&dev_priv->fence_drv.fence_lock,
+ irq_flags);
+ return -EDEADLK;
+
+ }
+ }
+ spin_unlock_irqrestore(&dev_priv->fence_drv.fence_lock, irq_flags);
+ return 0;
+}
+
+/**
+ * ipvr_fence_wait - wait for a fence to signal
+ *
+ * @fence: ipvr fence object
+ * @intr: use interruptable sleep
+ * @no_wait: not signaled, if need add into wait queue
+ *
+ * Wait for the requested fence to signal (all asics).
+ * @intr selects whether to use interruptable (true) or non-interruptable
+ * (false) sleep when waiting for the fence.
+ * Returns 0 if the fence has passed, error for all other cases.
+ */
+int ipvr_fence_wait(struct ipvr_fence *fence, bool intr, bool no_wait)
+{
+ int ret;
+ struct drm_ipvr_private *dev_priv;
+
+ if (fence == NULL || fence->seq == IPVR_FENCE_SIGNALED_SEQ) {
+ IPVR_DEBUG_GENERAL("fence is NULL or has been singaled.\n");
+ return 0;
+ }
+ dev_priv = fence->dev_priv;
+
+ IPVR_DEBUG_GENERAL("wait fence seq %u, last signaled seq is %d, "
+ "last emitted seq is %u.\n", fence->seq,
+ atomic_read(&dev_priv->fence_drv.signaled_seq),
+ dev_priv->fence_drv.sync_seq);
+ if (!no_wait)
+ trace_ipvr_fence_wait_begin(fence,
+ atomic_read(&dev_priv->fence_drv.signaled_seq),
+ dev_priv->fence_drv.sync_seq);
+
+ if (ipvr_fence_signaled(dev_priv, fence->seq)) {
+ IPVR_DEBUG_GENERAL("fence has been signaled.\n");
+ /*
+ * compare with ttm_bo_wait, don't need create a tmp_obj
+ * it is better we also set bo->fence = NULL
+ */
+ if (!no_wait)
+ trace_ipvr_fence_wait_end(fence,
+ atomic_read(&dev_priv->fence_drv.signaled_seq),
+ dev_priv->fence_drv.sync_seq);
+ fence->seq = IPVR_FENCE_SIGNALED_SEQ;
+ ipvr_fence_unref(&fence);
+ return 0;
+ }
+
+ if (no_wait)
+ return -EBUSY;
+
+ ret = ipvr_fence_wait_seq(dev_priv, fence->seq, intr);
+ if (ret) {
+ if (ret == -EDEADLK) {
+ trace_ipvr_fence_wait_lockup(fence,
+ atomic_read(&dev_priv->fence_drv.signaled_seq),
+ dev_priv->fence_drv.sync_seq);
+ ipvr_fence_lockup(dev_priv, fence);
+ }
+ return ret;
+ }
+ trace_ipvr_fence_wait_end(fence,
+ atomic_read(&dev_priv->fence_drv.signaled_seq),
+ dev_priv->fence_drv.sync_seq);
+ fence->seq = IPVR_FENCE_SIGNALED_SEQ;
+
+ return 0;
+}
+
+/**
+ * ipvr_fence_driver_init - init the fence driver
+ *
+ * @dev_priv: ipvr device pointer
+ *
+ * Init the fence driver, will not fail
+ */
+void ipvr_fence_driver_init(struct drm_ipvr_private *dev_priv)
+{
+ spin_lock_init(&dev_priv->fence_drv.fence_lock);
+ init_waitqueue_head(&dev_priv->fence_queue);
+ dev_priv->fence_drv.sync_seq = 0;
+ atomic_set(&dev_priv->fence_drv.signaled_seq, 0);
+ dev_priv->fence_drv.last_activity = jiffies;
+ dev_priv->fence_drv.initialized = false;
+}
+
+/**
+ * ipvr_fence_wait_empty_locked - wait for all fences to signal
+ *
+ * @dev_priv: ipvr device pointer
+ *
+ * Wait for all fences to be signalled.
+ */
+void ipvr_fence_wait_empty_locked(struct drm_ipvr_private *dev_priv)
+{
+ u16 seq;
+
+ seq = dev_priv->fence_drv.sync_seq;
+
+ while(1) {
+ int ret;
+ ret = ipvr_fence_wait_seq(dev_priv, seq, false);
+ if (ret == 0) {
+ return;
+ } else if (ret == -EDEADLK) {
+ ipvr_fence_lockup(dev_priv, NULL);
+ IPVR_DEBUG_WARN("Lockup found waiting for seq %d.\n",
+ seq);
+ return;
+ } else {
+ continue;
+ }
+ }
+}
+
+/**
+ * ipvr_fence_driver_fini - tear down the fence driver
+ * for all possible rings.
+ *
+ * @dev_priv: ipvr device pointer
+ *
+ * Tear down the fence driver for all possible rings (all asics).
+ */
+void ipvr_fence_driver_fini(struct drm_ipvr_private *dev_priv)
+{
+ if (!dev_priv->fence_drv.initialized)
+ return;
+ ipvr_fence_wait_empty_locked(dev_priv);
+ wake_up_all(&dev_priv->fence_queue);
+ dev_priv->fence_drv.initialized = false;
+}
+
+/**
+ * ipvr_fence_ref - take a ref on a fence
+ *
+ * @fence: fence object
+ *
+ * Take a reference on a fence (all asics).
+ * Returns the fence.
+ */
+struct ipvr_fence *ipvr_fence_ref(struct ipvr_fence *fence)
+{
+ kref_get(&fence->kref);
+ return fence;
+}
+
+/**
+ * ipvr_fence_unref - remove a ref on a fence
+ *
+ * @fence: ipvr fence object
+ *
+ * Remove a reference on a fence, if ref == 0, destory the fence.
+ */
+void ipvr_fence_unref(struct ipvr_fence **fence)
+{
+ struct ipvr_fence *tmp = *fence;
+
+ *fence = NULL;
+ if (tmp) {
+ kref_put(&tmp->kref, &ipvr_fence_destroy);
+ }
+}
+
+/**
+ * ipvr_fence_buffer_objects - bind fence to buffer list
+ *
+ * @list: validation buffer list
+ * @fence: ipvr fence object
+ *
+ * bind a fence to all obj in the validation list
+ */
+void
+ipvr_fence_buffer_objects(struct list_head *list, struct ipvr_fence *fence)
+{
+ struct ipvr_validate_buffer *entry;
+ struct drm_ipvr_gem_object *obj;
+
+ if (list_empty(list))
+ return;
+
+ list_for_each_entry(entry, list, head) {
+ obj = entry->ipvr_gem_bo;
+ /**
+ * do not update fence if val_args specifies so
+ */
+ if (entry->val_req.flags & IPVR_EXEC_OBJECT_NEED_FENCE) {
+ entry->old_fence = obj->fence;
+ obj->fence = ipvr_fence_ref(fence);
+ if (entry->old_fence)
+ ipvr_fence_unref(&entry->old_fence);
+ }
+ else {
+ IPVR_DEBUG_GENERAL("obj 0x%lx marked as non-fence\n",
+ ipvr_gem_object_mmu_offset(obj));
+ }
+ ipvr_bo_unreserve(obj);
+ }
+}
diff --git a/drivers/gpu/drm/ipvr/ipvr_fence.h b/drivers/gpu/drm/ipvr/ipvr_fence.h
new file mode 100644
index 0000000..501d6b55
--- /dev/null
+++ b/drivers/gpu/drm/ipvr/ipvr_fence.h
@@ -0,0 +1,74 @@
+/**************************************************************************
+ * ipvr_fence.h: IPVR header file for fence handling
+ *
+ * Copyright (c) 2014 Intel Corporation, Hillsboro, OR, USA
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Authors:
+ * Fei Jiang <fei.jiang at intel.com>
+ * Yao Cheng <yao.cheng at intel.com>
+ *
+ **************************************************************************/
+
+#ifndef _IPVR_FENCE_H_
+#define _IPVR_FENCE_H_
+
+#include "ipvr_drv.h"
+
+/* seq_after(a,b) returns true if the seq a is after seq b.*/
+#define ipvr_seq_after(a,b) \
+ (typecheck(u16, a) && \
+ typecheck(u16, b) && \
+ ((s16)(a - b) > 0))
+
+enum ipvr_cmd_status {
+ IPVR_CMD_SUCCESS,
+ IPVR_CMD_FAILED,
+ IPVR_CMD_LOCKUP,
+ IPVR_CMD_SKIP
+};
+
+#define IPVR_FENCE_JIFFIES_TIMEOUT (HZ / 2)
+/* fence seq are set to this number when signaled */
+#define IPVR_FENCE_SIGNALED_SEQ 0LL
+
+struct ipvr_fence {
+ struct drm_ipvr_private *dev_priv;
+ struct kref kref;
+ /* protected by dev_priv->fence_drv.fence_lock */
+ u16 seq;
+ /* fields for usrfence */
+ struct file *file;
+ char name[32];
+};
+
+int ipvr_fence_wait(struct ipvr_fence *fence, bool intr, bool no_wait);
+
+void ipvr_fence_process(struct drm_ipvr_private *dev_priv, u16 seq, u8 err);
+
+void ipvr_fence_driver_init(struct drm_ipvr_private *dev_priv);
+
+void ipvr_fence_driver_fini(struct drm_ipvr_private *dev_priv);
+
+int ipvr_fence_create(struct drm_ipvr_private *dev_priv, struct ipvr_fence **fence);
+
+void ipvr_fence_buffer_objects(struct list_head *list, struct ipvr_fence *fence);
+
+void ipvr_fence_unref(struct ipvr_fence **fence);
+
+void ipvr_fence_wait_empty_locked(struct drm_ipvr_private *dev_priv);
+
+#endif
diff --git a/drivers/gpu/drm/ipvr/ipvr_gem.c b/drivers/gpu/drm/ipvr/ipvr_gem.c
new file mode 100644
index 0000000..4536dd7
--- /dev/null
+++ b/drivers/gpu/drm/ipvr/ipvr_gem.c
@@ -0,0 +1,341 @@
+/**************************************************************************
+ * ipvr_gem.c: IPVR hook file for gem ioctls
+ *
+ * Copyright (c) 2014 Intel Corporation, Hillsboro, OR, USA
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Authors:
+ * Fei Jiang <fei.jiang at intel.com>
+ * Yao Cheng <yao.cheng at intel.com>
+ *
+ **************************************************************************/
+
+#include "ipvr_gem.h"
+#include "ipvr_bo.h"
+#include "ipvr_fence.h"
+#include "ipvr_exec.h"
+#include "ipvr_trace.h"
+#include <drm_gem.h>
+#include <linux/slab.h>
+#include <linux/swap.h>
+#include <linux/pci.h>
+#include <linux/dma-buf.h>
+
+#define VLV_IPVR_DEV_ID (0xf31)
+
+int
+ipvr_context_create_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_ipvr_context_create *args = data;
+ struct drm_ipvr_private *dev_priv = dev->dev_private;
+ struct ipvr_context *ipvr_ctx = NULL;
+ unsigned long irq_flags;
+ int ctx_id, ret = 0;
+
+ IPVR_DEBUG_ENTRY("enter\n");
+ /* add video decode context */
+ ipvr_ctx = kzalloc(sizeof(struct ipvr_context), GFP_KERNEL);
+ if (ipvr_ctx == NULL)
+ return -ENOMEM;
+
+ ctx_id = idr_alloc(&dev_priv->ipvr_ctx_idr, ipvr_ctx ,
+ IPVR_MIN_CONTEXT_ID, IPVR_MAX_CONTEXT_ID,
+ GFP_KERNEL);
+ if (ctx_id < 0)
+ return -ENOMEM;
+ ipvr_ctx->ctx_id = ctx_id;
+
+ INIT_LIST_HEAD(&ipvr_ctx->head);
+ ipvr_ctx->ctx_type = args->ctx_type;
+ ipvr_ctx->ipvr_fpriv = file_priv->driver_priv;
+ spin_lock_irqsave(&dev_priv->ipvr_ctx_lock, irq_flags);
+ list_add(&ipvr_ctx ->head, &dev_priv->ipvr_ctx_list);
+ spin_unlock_irqrestore(&dev_priv->ipvr_ctx_lock, irq_flags);
+ args->ctx_id = ctx_id;
+ IPVR_DEBUG_INIT("add ctx type 0x%x, ctx_id is %d.\n",
+ ipvr_ctx->ctx_type, ctx_id);
+ /*
+ * todo: only one tiling region is supported now,
+ * maybe we need create additional tiling region for rotation case,
+ * which has different tiling stride
+ */
+ if ((args->tiling_scheme == 0 && args->tiling_stride <= 3) ||
+ (args->tiling_scheme == 1 && args->tiling_stride <= 2)) {
+ ipvr_ctx->tiling_scheme = args->tiling_scheme;
+ ipvr_ctx->tiling_stride = args->tiling_stride;
+ } else {
+ IPVR_DEBUG_WARN("unsupported tiling scheme %d and stide %d.\n",
+ args->tiling_scheme, args->tiling_stride);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+int
+ipvr_context_destroy_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_ipvr_context_destroy *args = data;
+ struct drm_ipvr_private *dev_priv = dev->dev_private;
+ struct drm_ipvr_file_private *fpriv = file_priv->driver_priv;
+ struct ved_private *ved_priv = dev_priv->ved_private;
+ struct ipvr_context *ipvr_ctx = NULL;
+ unsigned long irq_flags;
+
+ IPVR_DEBUG_ENTRY("enter\n");
+ ipvr_ctx = (struct ipvr_context *)
+ idr_find(&dev_priv->ipvr_ctx_idr, args->ctx_id);
+ if (!ipvr_ctx || (ipvr_ctx->ipvr_fpriv != file_priv->driver_priv)) {
+ return -ENOENT;
+ }
+
+ /**
+ * fixme: remove this work-around (WA the issue that calling
+ * ctx_destroy_ioctl with queued cmd might cause state machine issue).
+ * we should wait for only the cmds sent from this context instead of
+ * all cmds
+ */
+ ipvr_fence_wait_empty_locked(dev_priv);
+
+ IPVR_DEBUG_PM("Video:remove context type 0x%x\n", ipvr_ctx->ctx_type);
+ mutex_lock(&ved_priv->ved_mutex);
+ if (ved_priv->ipvr_ctx == ipvr_ctx )
+ ved_priv->ipvr_ctx = NULL;
+ mutex_unlock(&ved_priv->ved_mutex);
+
+ spin_lock_irqsave(&dev_priv->ipvr_ctx_lock, irq_flags);
+ list_del(&ipvr_ctx->head);
+ fpriv->ctx_id = IPVR_CONTEXT_INVALID_ID;
+ spin_unlock_irqrestore(&dev_priv->ipvr_ctx_lock, irq_flags);
+
+ idr_remove(&dev_priv->ipvr_ctx_idr, ipvr_ctx->ctx_id);
+
+ kfree(ipvr_ctx);
+ return 0;
+}
+
+int
+ipvr_misc_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_ipvr_private *dev_priv = dev->dev_private;
+ struct drm_ipvr_misc *args = data;
+ int ret = 0;
+ u64 value;
+
+ IPVR_DEBUG_ENTRY("enter\n");
+ if (!dev_priv) {
+ IPVR_DEBUG_WARN("called with no initialization.\n");
+ return -EINVAL;
+ }
+ switch (args->key) {
+ case IPVR_DEVICE_INFO: {
+ /* only vlv supported now
+ */
+ u32 device_info = VLV_IPVR_DEV_ID << 16;
+ ret = copy_to_user((void __user *)((unsigned long)args->value),
+ &device_info, sizeof(device_info));
+ break;
+ }
+ case IPVR_UPDATE_TILING: {
+ struct drm_ipvr_update_tiling tiling;
+ struct ipvr_context *ipvr_ctx = NULL;
+ ret = copy_from_user(&tiling,
+ (void __user *)((unsigned long)args->arg),
+ sizeof(tiling));
+ if (ret)
+ break;
+
+ ipvr_ctx = (struct ipvr_context *)
+ idr_find(&dev_priv->ipvr_ctx_idr, tiling.ctx_id);
+ if (!ipvr_ctx ||
+ (ipvr_ctx->ipvr_fpriv != file_priv->driver_priv)) {
+ IPVR_DEBUG_WARN("fail to find ctx %d", tiling.ctx_id);
+ return -ENOENT;
+ }
+ IPVR_DEBUG_GENERAL("Video: update video tiling for ctx %d, "
+ "old tiling scheme is %d, old tiling stride is %d, "
+ "new tiling scheme is %d, new tiling stride is %d.\n",
+ tiling.ctx_id,
+ ipvr_ctx ->tiling_scheme, ipvr_ctx ->tiling_stride,
+ tiling.tiling_scheme, tiling.tiling_stride);
+ if ((tiling.tiling_scheme == 0 && tiling.tiling_stride <= 3) ||
+ (tiling.tiling_scheme == 1 && tiling.tiling_stride <= 2)) {
+ ipvr_ctx ->tiling_scheme = tiling.tiling_scheme;
+ ipvr_ctx ->tiling_stride = tiling.tiling_stride;
+ } else {
+ IPVR_ERROR("unsupported tile scheme: %d, stide: %d.\n",
+ tiling.tiling_scheme, tiling.tiling_stride);
+ ret = -EINVAL;
+ }
+ break;
+ }
+ default:
+ if (copy_from_user(&value,
+ (void __user *)((unsigned long)args->value),
+ sizeof(value))) {
+ IPVR_DEBUG_WARN("copy_from_user failed\n");
+ return -EFAULT;
+ }
+ if (copy_to_user((void __user *)((unsigned long)args->value),
+ &value, sizeof(value))) {
+ IPVR_DEBUG_WARN("copy_to_user failed\n");
+ return -EFAULT;
+ }
+ ret = -EFAULT;
+ break;
+ }
+ return ret;
+}
+
+int ipvr_gem_create_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ int ret;
+ struct drm_ipvr_gem_create *args = data;
+ struct drm_ipvr_gem_object *obj;
+ struct drm_ipvr_private *dev_priv = dev->dev_private;
+ if (args->cache_level >= IPVR_CACHE_MAX)
+ return -EINVAL;
+ if (args->size == 0)
+ return -EINVAL;
+ args->rounded_size = roundup(args->size, PAGE_SIZE);
+ obj = ipvr_gem_create(dev_priv, args->rounded_size, args->tiling,
+ args->cache_level);
+ if (IS_ERR(obj)) {
+ ret = PTR_ERR(obj);
+ goto out;
+ }
+ args->mmu_offset = ipvr_gem_object_mmu_offset(obj);
+ /* create handle */
+ ret = drm_gem_handle_create(file_priv, &obj->base, &args->handle);
+ if (ret) {
+ IPVR_ERROR("could not allocate mmap offset: %d\n", ret);
+ goto out_free;
+ }
+ /* drop reference from allocate - handle holds it now */
+ drm_gem_object_unreference_unlocked(&obj->base);
+ /* create map offset */
+ ret = drm_gem_create_mmap_offset(&obj->base);
+ if (ret) {
+ IPVR_ERROR("could not allocate mmap offset: %d\n", ret);
+ goto out_free;
+ }
+ args->map_offset = drm_vma_node_offset_addr(&obj->base.vma_node);
+ IPVR_DEBUG_GENERAL("bo create done, handle: %u, vpu offset: 0x%llx.\n",
+ args->handle, args->mmu_offset);
+ return 0;
+out_free:
+ ipvr_gem_free_object(&obj->base);
+out:
+ return ret;
+}
+
+int ipvr_gem_busy_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_ipvr_gem_busy *args = data;
+ struct drm_ipvr_gem_object *obj;
+ int ret = 0;
+
+ obj = to_ipvr_bo(drm_gem_object_lookup(dev, file_priv, args->handle));
+ if (!obj || &obj->base == NULL) {
+ return -ENOENT;
+ }
+ IPVR_DEBUG_GENERAL("Checking bo %p (fence %p seq %u) busy status\n",
+ obj, obj->fence, ((obj->fence)? obj->fence->seq: 0));
+
+ ret = ipvr_bo_reserve(obj, true, false);
+ if (unlikely(ret != 0))
+ goto out;
+ ret = ipvr_fence_wait(obj->fence, true, true);
+ ipvr_bo_unreserve(obj);
+
+ args->busy = ret? 1: 0;
+out:
+ drm_gem_object_unreference_unlocked(&obj->base);
+ return ret;
+}
+
+/**
+ * ipvr_gem_wait_ioctl - implements DRM_IOCTL_IPVR_GEM_WAIT
+ * @DRM_IOCTL_ARGS: standard ioctl arguments
+ *
+ * Returns 0 if successful, else an error is returned with the remaining time in
+ * the timeout parameter.
+ * -ETIME: object is still busy after timeout
+ * -ERESTARTSYS: signal interrupted the wait
+ * -ENONENT: object doesn't exist
+ * Also possible, but rare:
+ * -EAGAIN: VPU wedged
+ * -ENOMEM: damn
+ * -ENODEV: Internal IRQ fail
+ * -E?: The add request failed
+ *
+ * The wait ioctl with a timeout of 0 reimplements the busy ioctl. With any
+ * non-zero timeout parameter the wait ioctl will wait for the given number of
+ * nanoseconds on an object becoming unbusy. Since the wait itself does so
+ * without holding struct_mutex the object may become re-busied before this
+ * function completes. A similar but shorter * race condition exists in the busy
+ * ioctl
+ */
+int ipvr_gem_wait_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_ipvr_gem_wait *args = data;
+ struct drm_ipvr_gem_object *obj;
+ int ret = 0;
+
+ IPVR_DEBUG_ENTRY("wait %d buffer to finish execution.\n", args->handle);
+ obj = to_ipvr_bo(drm_gem_object_lookup(dev, file_priv, args->handle));
+ if (&obj->base == NULL) {
+ return -ENOENT;
+ }
+
+ ret = ipvr_bo_reserve(obj, true, false);
+ if (unlikely(ret != 0))
+ goto out;
+
+ ret = ipvr_fence_wait(obj->fence, true, false);
+
+ ipvr_bo_unreserve(obj);
+
+out:
+ drm_gem_object_unreference_unlocked(&obj->base);
+ return ret;
+}
+
+int ipvr_gem_mmap_offset_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ int ret;
+ struct drm_ipvr_gem_mmap_offset *args = data;
+ struct drm_ipvr_gem_object *obj;
+
+ IPVR_DEBUG_ENTRY("getting mmap offset for BO %u.\n", args->handle);
+ obj = to_ipvr_bo(drm_gem_object_lookup(dev, file_priv, args->handle));
+
+ /* create map offset */
+ ret = drm_gem_create_mmap_offset(&obj->base);
+ if (ret) {
+ IPVR_ERROR("could not allocate mmap offset: %d\n", ret);
+ return ret;
+ }
+ args->offset = drm_vma_node_offset_addr(&obj->base.vma_node);
+ return 0;
+}
diff --git a/drivers/gpu/drm/ipvr/ipvr_gem.h b/drivers/gpu/drm/ipvr/ipvr_gem.h
new file mode 100644
index 0000000..71ca172
--- /dev/null
+++ b/drivers/gpu/drm/ipvr/ipvr_gem.h
@@ -0,0 +1,48 @@
+/**************************************************************************
+ * ipvr_gem.h: IPVR header file for GEM ioctls
+ *
+ * Copyright (c) 2014 Intel Corporation, Hillsboro, OR, USA
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Authors:
+ * Fei Jiang <fei.jiang at intel.com>
+ * Yao Cheng <yao.cheng at intel.com>
+ *
+ **************************************************************************/
+
+#ifndef _IPVR_GEM_H_
+#define _IPVR_GEM_H_
+
+#include "ipvr_drv.h"
+
+int ipvr_context_create_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+int ipvr_context_destroy_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+int ipvr_misc_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+int ipvr_gem_execbuffer_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+int ipvr_gem_busy_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+int ipvr_gem_create_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+int ipvr_gem_wait_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+int ipvr_gem_mmap_offset_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+
+#endif
diff --git a/drivers/gpu/drm/ipvr/ipvr_mmu.c b/drivers/gpu/drm/ipvr/ipvr_mmu.c
new file mode 100644
index 0000000..0f8f364
--- /dev/null
+++ b/drivers/gpu/drm/ipvr/ipvr_mmu.c
@@ -0,0 +1,752 @@
+/**************************************************************************
+ * ipvr_mmu.c: IPVR MMU handling to support VED, VEC, VSP buffer access
+ *
+ * Copyright (c) 2014 Intel Corporation, Hillsboro, OR, USA
+ * Copyright (c) Imagination Technologies Limited, UK
+ * Copyright (c) 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Authors:
+ * Fei Jiang <fei.jiang at intel.com>
+ * Yao Cheng <yao.cheng at intel.com>
+ *
+ **************************************************************************/
+
+#include "ipvr_mmu.h"
+#include "ipvr_debug.h"
+
+/*
+ * Code for the VED MMU
+ * Assumes system page size is same with VED (4KiB).
+ * Doesn't work for the case of page size mismatch.
+ */
+
+/*
+ * clflush on one processor only:
+ * clflush should apparently flush the cache line on all processors in an
+ * SMP system.
+ */
+
+/*
+ * kmap atomic:
+ * Usage of the slots must be completely encapsulated within a spinlock, and
+ * no other functions that may be using the locks for other purposed may be
+ * called from within the locked region.
+ * Since the slots are per processor, this will guarantee that we are the only
+ * user.
+ */
+
+/*
+ *PTE's and PDE's
+ */
+#define IPVR_PDE_MASK 0x003FFFFF
+#define IPVR_PDE_SHIFT 22
+#define IPVR_PTE_SHIFT 12
+#define IPVR_PTE_VALID 0x0001 /* PTE / PDE valid */
+#define IPVR_PTE_WO 0x0002 /* Write only */
+#define IPVR_PTE_RO 0x0004 /* Read only */
+#define IPVR_PTE_CACHED 0x0008 /* CPU cache coherent */
+
+struct ipvr_mmu_pt {
+ struct ipvr_mmu_pd *pd;
+ u32 index;
+ u32 count;
+ struct page *p;
+ u32 *v;
+};
+
+struct ipvr_mmu_pd {
+ struct ipvr_mmu_driver *driver;
+ u32 hw_context;
+ struct ipvr_mmu_pt **tables;
+ struct page *p;
+ struct page *dummy_pt;
+ struct page *dummy_page;
+ u32 pd_mask;
+ u32 invalid_pde;
+ u32 invalid_pte;
+};
+
+static inline u32 ipvr_mmu_pt_index(u32 offset)
+{
+ return (offset >> IPVR_PTE_SHIFT) & 0x3FF;
+}
+
+static inline u32 ipvr_mmu_pd_index(u32 offset)
+{
+ return offset >> IPVR_PDE_SHIFT;
+}
+
+#if defined(CONFIG_X86)
+static inline void ipvr_clflush(void *addr)
+{
+ __asm__ __volatile__("clflush (%0)\n" : : "r"(addr) : "memory");
+}
+
+static inline void
+ipvr_mmu_clflush(struct ipvr_mmu_driver *driver, void *addr)
+{
+ if (!driver->has_clflush)
+ return;
+
+ mb();
+ ipvr_clflush(addr);
+ mb();
+}
+
+static void
+ipvr_mmu_page_clflush(struct ipvr_mmu_driver *driver, struct page* page)
+{
+ u32 clflush_add = driver->clflush_add >> PAGE_SHIFT;
+ u32 clflush_count = PAGE_SIZE / clflush_add;
+ int i;
+ u8 *clf;
+
+ clf = kmap_atomic(page);
+
+ mb();
+ for (i = 0; i < clflush_count; ++i) {
+ ipvr_clflush(clf);
+ clf += clflush_add;
+ }
+ mb();
+
+ kunmap_atomic(clf);
+}
+
+static void ipvr_mmu_pages_clflush(struct ipvr_mmu_driver *driver,
+ struct page *page[], int num_pages)
+{
+ int i;
+
+ if (!driver->has_clflush)
+ return ;
+
+ for (i = 0; i < num_pages; i++)
+ ipvr_mmu_page_clflush(driver, *page++);
+}
+#else
+
+static inline void
+ipvr_mmu_clflush(struct ipvr_mmu_driver *driver, void *addr)
+{
+ ;
+}
+
+static void ipvr_mmu_pages_clflush(struct ipvr_mmu_driver *driver,
+ struct page *page[], int num_pages)
+{
+ IPVR_DEBUG_GENERAL("Dumy ipvr_mmu_pages_clflush\n");
+}
+
+#endif
+
+static void
+ipvr_mmu_flush_pd_locked(struct ipvr_mmu_driver *driver, bool force)
+{
+ if (atomic_read(&driver->needs_tlbflush) || force) {
+ if (!driver->dev_priv)
+ goto out;
+
+ atomic_set(&driver->dev_priv->ipvr_mmu_invaldc, 1);
+ }
+out:
+ atomic_set(&driver->needs_tlbflush, 0);
+}
+
+static void ipvr_mmu_flush(struct ipvr_mmu_driver *driver, bool rc_prot)
+{
+ if (rc_prot)
+ down_write(&driver->sem);
+
+ if (!driver->dev_priv)
+ goto out;
+
+ atomic_set(&driver->dev_priv->ipvr_mmu_invaldc, 1);
+
+out:
+ if (rc_prot)
+ up_write(&driver->sem);
+}
+
+void ipvr_mmu_set_pd_context(struct ipvr_mmu_pd *pd, u32 hw_context)
+{
+ ipvr_mmu_pages_clflush(pd->driver, &pd->p, 1);
+ down_write(&pd->driver->sem);
+ wmb();
+ ipvr_mmu_flush_pd_locked(pd->driver, 1);
+ pd->hw_context = hw_context;
+ up_write(&pd->driver->sem);
+}
+
+static inline unsigned long
+ipvr_pd_addr_end(unsigned long addr, unsigned long end)
+{
+
+ addr = (addr + IPVR_PDE_MASK + 1) & ~IPVR_PDE_MASK;
+ return (addr < end) ? addr : end;
+}
+
+static inline u32 ipvr_mmu_mask_pte(u32 pfn, u32 type)
+{
+ u32 mask = IPVR_PTE_VALID;
+
+ if (type & IPVR_MMU_CACHED_MEMORY)
+ mask |= IPVR_PTE_CACHED;
+ if (type & IPVR_MMU_RO_MEMORY)
+ mask |= IPVR_PTE_RO;
+ if (type & IPVR_MMU_WO_MEMORY)
+ mask |= IPVR_PTE_WO;
+
+ return (pfn << PAGE_SHIFT) | mask;
+}
+
+static struct ipvr_mmu_pd* __must_check
+ipvr_mmu_alloc_pd(struct ipvr_mmu_driver *driver, u32 invalid_type)
+{
+ struct ipvr_mmu_pd *pd = kmalloc(sizeof(*pd), GFP_KERNEL);
+ u32 *v;
+ int i;
+
+ if (!pd)
+ return NULL;
+
+ pd->p = alloc_page(GFP_DMA32);
+ if (!pd->p)
+ goto out_err1;
+ pd->dummy_pt = alloc_page(GFP_DMA32);
+ if (!pd->dummy_pt)
+ goto out_err2;
+ pd->dummy_page = alloc_page(GFP_DMA32);
+ if (!pd->dummy_page)
+ goto out_err3;
+
+ pd->invalid_pde =
+ ipvr_mmu_mask_pte(page_to_pfn(pd->dummy_pt), invalid_type);
+ pd->invalid_pte =
+ ipvr_mmu_mask_pte(page_to_pfn(pd->dummy_page), invalid_type);
+
+ v = kmap(pd->dummy_pt);
+ if (!v)
+ goto out_err4;
+ for (i = 0; i < (PAGE_SIZE / sizeof(u32)); ++i)
+ v[i] = pd->invalid_pte;
+
+ kunmap(pd->dummy_pt);
+
+ v = kmap(pd->p);
+ if (!v)
+ goto out_err4;
+ for (i = 0; i < (PAGE_SIZE / sizeof(u32)); ++i)
+ v[i] = pd->invalid_pde;
+
+ kunmap(pd->p);
+
+ v = kmap(pd->dummy_page);
+ if (!v)
+ goto out_err4;
+ clear_page(v);
+ kunmap(pd->dummy_page);
+
+ pd->tables = vmalloc_user(sizeof(struct ipvr_mmu_pt *) * 1024);
+ if (!pd->tables)
+ goto out_err4;
+
+ pd->hw_context = -1;
+ pd->pd_mask = IPVR_PTE_VALID;
+ pd->driver = driver;
+
+ return pd;
+
+out_err4:
+ __free_page(pd->dummy_page);
+out_err3:
+ __free_page(pd->dummy_pt);
+out_err2:
+ __free_page(pd->p);
+out_err1:
+ kfree(pd);
+ return NULL;
+}
+
+static void ipvr_mmu_free_pt(struct ipvr_mmu_pt *pt)
+{
+ __free_page(pt->p);
+ kfree(pt);
+}
+
+static void ipvr_mmu_free_pagedir(struct ipvr_mmu_pd *pd)
+{
+ struct ipvr_mmu_driver *driver = pd->driver;
+ struct ipvr_mmu_pt *pt;
+ int i;
+
+ down_write(&driver->sem);
+ if (pd->hw_context != -1)
+ ipvr_mmu_flush_pd_locked(driver, 1);
+
+ /* Should take the spinlock here, but we don't need to do that
+ since we have the semaphore in write mode. */
+
+ for (i = 0; i < 1024; ++i) {
+ pt = pd->tables[i];
+ if (pt)
+ ipvr_mmu_free_pt(pt);
+ }
+
+ vfree(pd->tables);
+ __free_page(pd->dummy_page);
+ __free_page(pd->dummy_pt);
+ __free_page(pd->p);
+ kfree(pd);
+ up_write(&driver->sem);
+}
+
+static struct ipvr_mmu_pt *ipvr_mmu_alloc_pt(struct ipvr_mmu_pd *pd)
+{
+ struct ipvr_mmu_pt *pt = kmalloc(sizeof(*pt), GFP_KERNEL);
+ void *v;
+ u32 clflush_add = pd->driver->clflush_add >> PAGE_SHIFT;
+ u32 clflush_count = PAGE_SIZE / clflush_add;
+ spinlock_t *lock = &pd->driver->lock;
+ u8 *clf;
+ u32 *ptes;
+ int i;
+
+ if (!pt)
+ return NULL;
+
+ pt->p = alloc_page(GFP_DMA32);
+ if (!pt->p) {
+ kfree(pt);
+ return NULL;
+ }
+
+ spin_lock(lock);
+
+ v = kmap_atomic(pt->p);
+
+ clf = (u8 *) v;
+ ptes = (u32 *) v;
+ for (i = 0; i < (PAGE_SIZE / sizeof(u32)); ++i)
+ *ptes++ = pd->invalid_pte;
+
+
+#if defined(CONFIG_X86)
+ if (pd->driver->has_clflush && pd->hw_context != -1) {
+ mb();
+ for (i = 0; i < clflush_count; ++i) {
+ ipvr_clflush(clf);
+ clf += clflush_add;
+ }
+ mb();
+ }
+#endif
+ kunmap_atomic(v);
+
+ spin_unlock(lock);
+
+ pt->count = 0;
+ pt->pd = pd;
+ pt->index = 0;
+
+ return pt;
+}
+
+static struct ipvr_mmu_pt *
+ipvr_mmu_pt_alloc_map_lock(struct ipvr_mmu_pd *pd, unsigned long addr)
+{
+ u32 index = ipvr_mmu_pd_index(addr);
+ struct ipvr_mmu_pt *pt;
+ u32 *v;
+ spinlock_t *lock = &pd->driver->lock;
+
+ spin_lock(lock);
+ pt = pd->tables[index];
+ while (!pt) {
+ spin_unlock(lock);
+ pt = ipvr_mmu_alloc_pt(pd);
+ if (!pt)
+ return NULL;
+ spin_lock(lock);
+
+ if (pd->tables[index]) {
+ spin_unlock(lock);
+ ipvr_mmu_free_pt(pt);
+ spin_lock(lock);
+ pt = pd->tables[index];
+ continue;
+ }
+
+ v = kmap_atomic(pd->p);
+
+ pd->tables[index] = pt;
+ v[index] = (page_to_pfn(pt->p) << 12) |
+ pd->pd_mask;
+
+
+ pt->index = index;
+
+ kunmap_atomic((void *) v);
+
+ if (pd->hw_context != -1) {
+ ipvr_mmu_clflush(pd->driver, (void *) &v[index]);
+ atomic_set(&pd->driver->needs_tlbflush, 1);
+ }
+ }
+
+ pt->v = kmap_atomic(pt->p);
+
+ return pt;
+}
+
+static struct ipvr_mmu_pt *
+ipvr_mmu_pt_map_lock(struct ipvr_mmu_pd *pd, unsigned long addr)
+{
+ u32 index = ipvr_mmu_pd_index(addr);
+ struct ipvr_mmu_pt *pt;
+ spinlock_t *lock = &pd->driver->lock;
+
+ spin_lock(lock);
+ pt = pd->tables[index];
+ if (!pt) {
+ spin_unlock(lock);
+ return NULL;
+ }
+
+ pt->v = kmap_atomic(pt->p);
+
+ return pt;
+}
+
+static void ipvr_mmu_pt_unmap_unlock(struct ipvr_mmu_pt *pt)
+{
+ struct ipvr_mmu_pd *pd = pt->pd;
+ u32 *v;
+
+ kunmap_atomic(pt->v);
+
+ if (pt->count == 0) {
+ v = kmap_atomic(pd->p);
+
+ v[pt->index] = pd->invalid_pde;
+ pd->tables[pt->index] = NULL;
+
+ if (pd->hw_context != -1) {
+ ipvr_mmu_clflush(pd->driver,
+ (void *) &v[pt->index]);
+ atomic_set(&pd->driver->needs_tlbflush, 1);
+ }
+
+ kunmap_atomic(pt->v);
+
+ spin_unlock(&pd->driver->lock);
+ ipvr_mmu_free_pt(pt);
+ return;
+ }
+ spin_unlock(&pd->driver->lock);
+}
+
+static inline void
+ipvr_mmu_set_pte(struct ipvr_mmu_pt *pt, unsigned long addr, u32 pte)
+{
+ pt->v[ipvr_mmu_pt_index(addr)] = pte;
+}
+
+static inline void
+ipvr_mmu_invalidate_pte(struct ipvr_mmu_pt *pt, unsigned long addr)
+{
+ pt->v[ipvr_mmu_pt_index(addr)] = pt->pd->invalid_pte;
+}
+
+struct ipvr_mmu_pd *ipvr_mmu_get_default_pd(struct ipvr_mmu_driver *driver)
+{
+ struct ipvr_mmu_pd *pd;
+
+ /* down_read(&driver->sem); */
+ pd = driver->default_pd;
+ /* up_read(&driver->sem); */
+
+ return pd;
+}
+
+/* Returns the physical address of the PD shared by sgx/msvdx */
+u32 __must_check ipvr_get_default_pd_addr32(struct ipvr_mmu_driver *driver)
+{
+ struct ipvr_mmu_pd *pd;
+ unsigned long pfn;
+ pd = ipvr_mmu_get_default_pd(driver);
+ pfn = page_to_pfn(pd->p);
+ if (pfn >= 0x00100000UL)
+ return 0;
+ return pfn << PAGE_SHIFT;
+}
+
+void ipvr_mmu_driver_takedown(struct ipvr_mmu_driver *driver)
+{
+ ipvr_mmu_free_pagedir(driver->default_pd);
+ kfree(driver);
+}
+
+struct ipvr_mmu_driver * __must_check
+ipvr_mmu_driver_init(u8 __iomem * registers, u32 invalid_type,
+ struct drm_ipvr_private *dev_priv)
+{
+ struct ipvr_mmu_driver *driver;
+
+ driver = kmalloc(sizeof(*driver), GFP_KERNEL);
+ if (!driver)
+ return NULL;
+
+ driver->dev_priv = dev_priv;
+
+ driver->default_pd =
+ ipvr_mmu_alloc_pd(driver, invalid_type);
+ if (!driver->default_pd)
+ goto out_err1;
+
+ spin_lock_init(&driver->lock);
+ init_rwsem(&driver->sem);
+ down_write(&driver->sem);
+ driver->register_map = registers;
+ atomic_set(&driver->needs_tlbflush, 1);
+
+ driver->has_clflush = false;
+
+#if defined(CONFIG_X86)
+ if (cpu_has_clflush) {
+ u32 tfms, misc, cap0, cap4, clflush_size;
+
+ /*
+ * clflush size is determined at kernel setup for x86_64
+ * but not for i386. We have to do it here.
+ */
+
+ cpuid(0x00000001, &tfms, &misc, &cap0, &cap4);
+ clflush_size = ((misc >> 8) & 0xff) * 8;
+ driver->has_clflush = true;
+ driver->clflush_add =
+ PAGE_SIZE * clflush_size / sizeof(u32);
+ driver->clflush_mask = driver->clflush_add - 1;
+ driver->clflush_mask = ~driver->clflush_mask;
+ }
+#endif
+
+ up_write(&driver->sem);
+ return driver;
+
+out_err1:
+ kfree(driver);
+ return NULL;
+}
+
+#if defined(CONFIG_X86)
+static void ipvr_mmu_flush_ptes(struct ipvr_mmu_pd *pd,
+ unsigned long address,
+ int num_pages,
+ u32 desired_tile_stride,
+ u32 hw_tile_stride)
+{
+ struct ipvr_mmu_pt *pt;
+ int rows = 1;
+ int i;
+ unsigned long addr;
+ unsigned long end;
+ unsigned long next;
+ unsigned long add;
+ unsigned long row_add;
+ unsigned long clflush_add = pd->driver->clflush_add;
+ unsigned long clflush_mask = pd->driver->clflush_mask;
+ IPVR_DEBUG_GENERAL("call x86 ipvr_mmu_flush_ptes, address is 0x%lx, "
+ "num pages is %d.\n", address, num_pages);
+ if (!pd->driver->has_clflush) {
+ IPVR_DEBUG_GENERAL("call ipvr_mmu_pages_clflush.\n");
+ ipvr_mmu_pages_clflush(pd->driver, &pd->p, num_pages);
+ return;
+ }
+
+ if (hw_tile_stride)
+ rows = num_pages / desired_tile_stride;
+ else
+ desired_tile_stride = num_pages;
+
+ add = desired_tile_stride << PAGE_SHIFT;
+ row_add = hw_tile_stride << PAGE_SHIFT;
+ mb();
+ for (i = 0; i < rows; ++i) {
+ addr = address;
+ end = addr + add;
+
+ do {
+ next = ipvr_pd_addr_end(addr, end);
+ pt = ipvr_mmu_pt_map_lock(pd, addr);
+ if (!pt)
+ continue;
+ do {
+ ipvr_clflush(&pt->v[ipvr_mmu_pt_index(addr)]);
+ } while (addr +=
+ clflush_add,
+ (addr & clflush_mask) < next);
+
+ ipvr_mmu_pt_unmap_unlock(pt);
+ } while (addr = next, next != end);
+ address += row_add;
+ }
+ mb();
+}
+#else
+
+static void ipvr_mmu_flush_ptes(struct ipvr_mmu_pd *pd,
+ unsigned long address,
+ int num_pages,
+ u32 desired_tile_stride,
+ u32 hw_tile_stride)
+{
+ IPVR_DEBUG_GENERAL("call non-x86 ipvr_mmu_flush_ptes.\n");
+}
+#endif
+
+void ipvr_mmu_remove_pages(struct ipvr_mmu_pd *pd, unsigned long address,
+ int num_pages, u32 desired_tile_stride,
+ u32 hw_tile_stride)
+{
+ struct ipvr_mmu_pt *pt;
+ int rows = 1;
+ int i;
+ unsigned long addr;
+ unsigned long end;
+ unsigned long next;
+ unsigned long add;
+ unsigned long row_add;
+ unsigned long f_address = address;
+
+ if (hw_tile_stride)
+ rows = num_pages / desired_tile_stride;
+ else
+ desired_tile_stride = num_pages;
+
+ add = desired_tile_stride << PAGE_SHIFT;
+ row_add = hw_tile_stride << PAGE_SHIFT;
+
+ /* down_read(&pd->driver->sem); */
+
+ /* Make sure we only need to flush this processor's cache */
+
+ for (i = 0; i < rows; ++i) {
+
+ addr = address;
+ end = addr + add;
+
+ do {
+ next = ipvr_pd_addr_end(addr, end);
+ pt = ipvr_mmu_pt_map_lock(pd, addr);
+ if (!pt)
+ continue;
+ do {
+ ipvr_mmu_invalidate_pte(pt, addr);
+ --pt->count;
+
+ } while (addr += PAGE_SIZE, addr < next);
+ ipvr_mmu_pt_unmap_unlock(pt);
+
+ } while (addr = next, next != end);
+ address += row_add;
+ }
+ if (pd->hw_context != -1)
+ ipvr_mmu_flush_ptes(pd, f_address, num_pages,
+ desired_tile_stride, hw_tile_stride);
+
+ /* up_read(&pd->driver->sem); */
+
+ if (pd->hw_context != -1)
+ ipvr_mmu_flush(pd->driver, 0);
+ ipvr_stat_remove_mmu_bind(pd->driver->dev_priv, num_pages << PAGE_SHIFT);
+}
+
+int ipvr_mmu_insert_pages(struct ipvr_mmu_pd *pd, struct page **pages,
+ unsigned long address, int num_pages,
+ u32 desired_tile_stride,
+ u32 hw_tile_stride, u32 type)
+{
+ struct ipvr_mmu_pt *pt;
+ int rows = 1;
+ int i;
+ u32 pte;
+ unsigned long addr;
+ unsigned long end;
+ unsigned long next;
+ unsigned long add;
+ unsigned long row_add;
+ unsigned long f_address = address;
+ unsigned long pfn;
+ int ret = 0;
+
+ if (hw_tile_stride) {
+ if (num_pages % desired_tile_stride != 0)
+ return -EINVAL;
+ rows = num_pages / desired_tile_stride;
+ } else {
+ desired_tile_stride = num_pages;
+ }
+
+ add = desired_tile_stride << PAGE_SHIFT;
+ row_add = hw_tile_stride << PAGE_SHIFT;
+
+ down_read(&pd->driver->sem);
+
+ for (i = 0; i < rows; ++i) {
+
+ addr = address;
+ end = addr + add;
+
+ do {
+ next = ipvr_pd_addr_end(addr, end);
+ pt = ipvr_mmu_pt_alloc_map_lock(pd, addr);
+ if (!pt) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ do {
+ pfn = page_to_pfn(*pages++);
+ /* should be under 4GiB */
+ if (pfn >= 0x00100000UL) {
+ IPVR_ERROR("cannot support pfn 0x%lx\n", pfn);
+ ret = -EINVAL;
+ goto out;
+ }
+ pte = ipvr_mmu_mask_pte(pfn, type);
+ ipvr_mmu_set_pte(pt, addr, pte);
+ pt->count++;
+ } while (addr += PAGE_SIZE, addr < next);
+ ipvr_mmu_pt_unmap_unlock(pt);
+
+ } while (addr = next, next != end);
+
+ address += row_add;
+ }
+out:
+ if (pd->hw_context != -1)
+ ipvr_mmu_flush_ptes(pd, f_address, num_pages,
+ desired_tile_stride, hw_tile_stride);
+
+ up_read(&pd->driver->sem);
+
+ if (pd->hw_context != -1)
+ ipvr_mmu_flush(pd->driver, 1);
+
+ ipvr_stat_add_mmu_bind(pd->driver->dev_priv, num_pages << PAGE_SHIFT);
+ return ret;
+}
diff --git a/drivers/gpu/drm/ipvr/ipvr_mmu.h b/drivers/gpu/drm/ipvr/ipvr_mmu.h
new file mode 100644
index 0000000..1f524d4
--- /dev/null
+++ b/drivers/gpu/drm/ipvr/ipvr_mmu.h
@@ -0,0 +1,111 @@
+/**************************************************************************
+ * ipvr_mmu.h: IPVR header file for VED/VEC/VSP MMU handling
+ *
+ * Copyright (c) 2014 Intel Corporation, Hillsboro, OR, USA
+ * Copyright (c) Imagination Technologies Limited, UK
+ * Copyright (c) 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Authors:
+ * Eric Anholt <eric at anholt.net>
+ * Fei Jiang <fei.jiang at intel.com>
+ * Yao Cheng <yao.cheng at intel.com>
+ *
+ **************************************************************************/
+
+#ifndef _IPVR_MMU_H_
+#define _IPVR_MMU_H_
+
+#include "ipvr_drv.h"
+
+static inline bool __must_check IPVR_IS_ERR(__force const unsigned long offset)
+{
+ return unlikely((offset) >= (unsigned long)-MAX_ERRNO);
+}
+
+static inline long __must_check IPVR_OFFSET_ERR(__force const unsigned long offset)
+{
+ return (long)offset;
+}
+
+static inline unsigned long __must_check IPVR_ERR_OFFSET(__force const long err)
+{
+ return (unsigned long)err;
+}
+
+/**
+ * memory access control for VPU
+ */
+#define IPVR_MMU_CACHED_MEMORY (1 << 0) /* Bind to MMU only */
+#define IPVR_MMU_RO_MEMORY (1 << 1) /* MMU RO memory */
+#define IPVR_MMU_WO_MEMORY (1 << 2) /* MMU WO memory */
+
+/*
+ * linear MMU size is 512M : 0 - 512M
+ * tiling MMU size is 512M : 512M - 1024M
+ */
+#define IPVR_MEM_MMU_LINEAR_START 0x00000000
+#define IPVR_MEM_MMU_LINEAR_END 0x20000000
+#define IPVR_MEM_MMU_TILING_START 0x20000000
+#define IPVR_MEM_MMU_TILING_END 0x40000000
+
+struct ipvr_mmu_pd;
+struct ipvr_mmu_pt;
+
+struct ipvr_mmu_driver {
+ /* protects driver- and pd structures. Always take in read mode
+ * before taking the page table spinlock.
+ */
+ struct rw_semaphore sem;
+
+ /* protects page tables, directory tables and pt tables.
+ * and pt structures.
+ */
+ spinlock_t lock;
+
+ atomic_t needs_tlbflush;
+
+ u8 __iomem *register_map;
+ struct ipvr_mmu_pd *default_pd;
+
+ bool has_clflush;
+ u32 clflush_add;
+ unsigned long clflush_mask;
+
+ struct drm_ipvr_private *dev_priv;
+};
+
+struct ipvr_mmu_driver *__must_check ipvr_mmu_driver_init(u8 __iomem *registers,
+ u32 invalid_type, struct drm_ipvr_private *dev_priv);
+
+void ipvr_mmu_driver_takedown(struct ipvr_mmu_driver *driver);
+
+struct ipvr_mmu_pd *
+ipvr_mmu_get_default_pd(struct ipvr_mmu_driver *driver);
+
+void ipvr_mmu_set_pd_context(struct ipvr_mmu_pd *pd, u32 hw_context);
+
+u32 __must_check ipvr_get_default_pd_addr32(struct ipvr_mmu_driver *driver);
+
+int ipvr_mmu_insert_pages(struct ipvr_mmu_pd *pd, struct page **pages,
+ unsigned long address, int num_pages,
+ u32 desired_tile_stride, u32 hw_tile_stride, u32 type);
+
+void ipvr_mmu_remove_pages(struct ipvr_mmu_pd *pd,
+ unsigned long address, int num_pages,
+ u32 desired_tile_stride, u32 hw_tile_stride);
+
+#endif
diff --git a/drivers/gpu/drm/ipvr/ipvr_trace.c b/drivers/gpu/drm/ipvr/ipvr_trace.c
new file mode 100644
index 0000000..91c0bda
--- /dev/null
+++ b/drivers/gpu/drm/ipvr/ipvr_trace.c
@@ -0,0 +1,11 @@
+/*
+ * Copyright © 2014 Intel Corporation
+ *
+ * Authors:
+ * Yao Cheng <yao.cheng at intel.com>>
+ */
+
+#ifndef __CHECKER__
+#define CREATE_TRACE_POINTS
+#include "ipvr_trace.h"
+#endif
diff --git a/drivers/gpu/drm/ipvr/ipvr_trace.h b/drivers/gpu/drm/ipvr/ipvr_trace.h
new file mode 100644
index 0000000..db08c82
--- /dev/null
+++ b/drivers/gpu/drm/ipvr/ipvr_trace.h
@@ -0,0 +1,326 @@
+/**************************************************************************
+ * ipvr_trace.h: IPVR header file for trace support
+ *
+ * Copyright (c) 2014 Intel Corporation, Hillsboro, OR, USA
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Authors:
+ * Fei Jiang <fei.jiang at intel.com>
+ * Yao Cheng <yao.cheng at intel.com>
+ *
+ **************************************************************************/
+
+#if !defined(_IPVR_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ)
+#define _IPVR_TRACE_H_
+
+#include "ipvr_bo.h"
+#include "ipvr_fence.h"
+#include "ved_msg.h"
+#include <drm/drmP.h>
+#include <linux/stringify.h>
+#include <linux/types.h>
+#include <linux/tracepoint.h>
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM ipvr
+#define TRACE_SYSTEM_STRING __stringify(TRACE_SYSTEM)
+#define TRACE_INCLUDE_FILE ipvr_trace
+
+/* object tracking */
+
+TRACE_EVENT(ipvr_create_object,
+ TP_PROTO(struct drm_ipvr_gem_object *obj, u64 mmu_offset),
+ TP_ARGS(obj, mmu_offset),
+ TP_STRUCT__entry(
+ __field(struct drm_ipvr_gem_object *, obj)
+ __field(u32, size)
+ __field(bool, tiling)
+ __field(u32, cache_level)
+ __field(u64, mmu_offset)
+ ),
+ TP_fast_assign(
+ __entry->obj = obj;
+ __entry->size = obj->base.size;
+ __entry->tiling = obj->tiling;
+ __entry->cache_level = obj->cache_level;
+ __entry->mmu_offset = mmu_offset;
+ ),
+ TP_printk("obj=0x%p, size=%u, tiling=%u, cache=%u, mmu_offset=0x%llx",
+ __entry->obj, __entry->size, __entry->tiling,
+ __entry->cache_level, __entry->mmu_offset)
+);
+
+TRACE_EVENT(ipvr_free_object,
+ TP_PROTO(struct drm_ipvr_gem_object *obj),
+ TP_ARGS(obj),
+ TP_STRUCT__entry(
+ __field(struct drm_ipvr_gem_object *, obj)
+ ),
+ TP_fast_assign(
+ __entry->obj = obj;
+ ),
+ TP_printk("obj=0x%p", __entry->obj)
+);
+
+TRACE_EVENT(ipvr_fence_wait_begin,
+ TP_PROTO(struct ipvr_fence *fence,
+ u32 signaled_seq,
+ u16 sync_seq),
+ TP_ARGS(fence, signaled_seq, sync_seq),
+ TP_STRUCT__entry(
+ __field(struct ipvr_fence *, fence)
+ __field(u16, fence_seq)
+ __field(u32, signaled_seq)
+ __field(u16, sync_seq)
+ ),
+ TP_fast_assign(
+ __entry->fence = fence;
+ __entry->fence_seq = fence->seq;
+ __entry->signaled_seq = signaled_seq;
+ __entry->sync_seq = sync_seq;
+ ),
+ TP_printk("fence=%p, fence_seq=%d, signaled_seq=%d, sync_seq=%d",
+ __entry->fence, __entry->fence_seq,
+ __entry->signaled_seq, __entry->sync_seq)
+);
+
+TRACE_EVENT(ipvr_fence_wait_end,
+ TP_PROTO(struct ipvr_fence *fence,
+ u32 signaled_seq,
+ u16 sync_seq),
+ TP_ARGS(fence, signaled_seq, sync_seq),
+ TP_STRUCT__entry(
+ __field(struct ipvr_fence *, fence)
+ __field(u16, fence_seq)
+ __field(u32, signaled_seq)
+ __field(u16, sync_seq)
+ ),
+ TP_fast_assign(
+ __entry->fence = fence;
+ __entry->fence_seq = fence->seq;
+ __entry->signaled_seq = signaled_seq;
+ __entry->sync_seq = sync_seq;
+ ),
+ TP_printk("fence=%p, fence_seq=%d, signaled_seq=%d, sync_seq=%d",
+ __entry->fence, __entry->fence_seq,
+ __entry->signaled_seq, __entry->sync_seq)
+);
+
+
+TRACE_EVENT(ipvr_fence_wait_lockup,
+ TP_PROTO(struct ipvr_fence *fence,
+ u32 signaled_seq,
+ u16 sync_seq),
+ TP_ARGS(fence, signaled_seq, sync_seq),
+ TP_STRUCT__entry(
+ __field(struct ipvr_fence *, fence)
+ __field(u16, fence_seq)
+ __field(u32, signaled_seq)
+ __field(u16, sync_seq)
+ ),
+ TP_fast_assign(
+ __entry->fence = fence;
+ __entry->fence_seq = fence->seq;
+ __entry->signaled_seq = signaled_seq;
+ __entry->sync_seq = sync_seq;
+ ),
+ TP_printk("fence=%p, fence_seq=%d, signaled_seq=%d, sync_seq=%d",
+ __entry->fence, __entry->fence_seq,
+ __entry->signaled_seq, __entry->sync_seq)
+);
+
+TRACE_EVENT(ipvr_execbuffer,
+ TP_PROTO(struct drm_ipvr_gem_execbuffer *exec),
+ TP_ARGS(exec),
+ TP_STRUCT__entry(
+ __field(u64, buffers_ptr)
+ __field(u32, buffer_count)
+ __field(u32, exec_start_offset)
+ __field(u32, exec_len)
+ __field(u32, ctx_id)
+ ),
+ TP_fast_assign(
+ __entry->buffers_ptr = exec->buffers_ptr;
+ __entry->buffer_count = exec->buffer_count;
+ __entry->exec_start_offset = exec->exec_start_offset;
+ __entry->exec_len = exec->exec_len;
+ __entry->ctx_id = exec->ctx_id;
+ ),
+ TP_printk("buffers_ptr=0x%llx, buffer_count=0x%d, "
+ "exec_start_offset=0x%x, exec_len=%u, ctx_id=%d",
+ __entry->buffers_ptr, __entry->buffer_count,
+ __entry->exec_start_offset, __entry->exec_len,
+ __entry->ctx_id)
+);
+
+TRACE_EVENT(ved_cmd_send,
+ TP_PROTO(u32 cmd_id, u32 seq),
+ TP_ARGS(cmd_id, seq),
+ TP_STRUCT__entry(
+ __field(u32, cmd_id)
+ __field(u32, seq)
+ ),
+ TP_fast_assign(
+ __entry->cmd_id = cmd_id;
+ __entry->seq = seq;
+ ),
+ TP_printk("cmd_id=0x%08x, seq=0x%08x",
+ __entry->cmd_id, __entry->seq)
+);
+
+TRACE_EVENT(ved_cmd_copy,
+ TP_PROTO(u32 cmd_id, u32 seq),
+ TP_ARGS(cmd_id, seq),
+ TP_STRUCT__entry(
+ __field(u32, cmd_id)
+ __field(u32, seq)
+ ),
+ TP_fast_assign(
+ __entry->cmd_id = cmd_id;
+ __entry->seq = seq;
+ ),
+ TP_printk("cmd_id=0x%08x, seq=0x%08x",
+ __entry->cmd_id, __entry->seq)
+);
+
+TRACE_EVENT(ipvr_get_power,
+ TP_PROTO(int usage, int pending),
+ TP_ARGS(usage, pending),
+ TP_STRUCT__entry(
+ __field(int, usage)
+ __field(int, pending)
+ ),
+ TP_fast_assign(
+ __entry->usage = usage;
+ __entry->pending = pending;
+ ),
+ TP_printk("power usage %d, pending events %d",
+ __entry->usage,
+ __entry->pending)
+);
+
+TRACE_EVENT(ipvr_put_power,
+ TP_PROTO(int usage, int pending),
+ TP_ARGS(usage, pending),
+ TP_STRUCT__entry(
+ __field(int, usage)
+ __field(int, pending)
+ ),
+ TP_fast_assign(
+ __entry->usage = usage;
+ __entry->pending = pending;
+ ),
+ TP_printk("power usage %d, pending events %d",
+ __entry->usage,
+ __entry->pending)
+);
+
+TRACE_EVENT(ved_power_on,
+ TP_PROTO(int freq),
+ TP_ARGS(freq),
+ TP_STRUCT__entry(
+ __field(int, freq)
+ ),
+ TP_fast_assign(
+ __entry->freq = freq;
+ ),
+ TP_printk("frequency %d MHz", __entry->freq)
+);
+
+TRACE_EVENT(ved_power_off,
+ TP_PROTO(int freq),
+ TP_ARGS(freq),
+ TP_STRUCT__entry(
+ __field(int, freq)
+ ),
+ TP_fast_assign(
+ __entry->freq = freq;
+ ),
+ TP_printk("frequency %d MHz", __entry->freq)
+);
+
+TRACE_EVENT(ved_irq_completed,
+ TP_PROTO(struct fw_completed_msg *completed_msg),
+ TP_ARGS(completed_msg),
+ TP_STRUCT__entry(
+ __field(u16, seqno)
+ __field(u32, flags)
+ __field(u32, vdebcr)
+ __field(u16, start_mb)
+ __field(u16, last_mb)
+ ),
+ TP_fast_assign(
+ __entry->seqno = completed_msg->header.bits.msg_fence;
+ __entry->flags = completed_msg->flags;
+ __entry->vdebcr = completed_msg->vdebcr;
+ __entry->start_mb = completed_msg->mb.bits.start_mb;
+ __entry->last_mb = completed_msg->mb.bits.last_mb;
+ ),
+ TP_printk("seq=0x%04x, flags=0x%08x, vdebcr=0x%08x, mb=[%u, %u]",
+ __entry->seqno,
+ __entry->flags,
+ __entry->vdebcr,
+ __entry->start_mb,
+ __entry->last_mb)
+);
+
+TRACE_EVENT(ved_irq_panic,
+ TP_PROTO(struct fw_panic_msg *panic_msg, u32 err_trig,
+ u32 irq_status, u32 mmu_status, u32 dmac_status),
+ TP_ARGS(panic_msg, err_trig, irq_status, mmu_status, dmac_status),
+ TP_STRUCT__entry(
+ __field(u16, seqno)
+ __field(u32, fe_status)
+ __field(u32, be_status)
+ __field(u16, rsvd)
+ __field(u16, last_mb)
+ __field(u32, err_trig)
+ __field(u32, irq_status)
+ __field(u32, mmu_status)
+ __field(u32, dmac_status)
+
+ ),
+ TP_fast_assign(
+ __entry->seqno = panic_msg->header.bits.msg_fence;
+ __entry->fe_status = panic_msg->fe_status;
+ __entry->be_status = panic_msg->be_status;
+ __entry->rsvd = panic_msg->mb.bits.reserved2;
+ __entry->last_mb = panic_msg->mb.bits.last_mb;
+ __entry->err_trig = err_trig;
+ __entry->irq_status = irq_status;
+ __entry->mmu_status = mmu_status;
+ __entry->dmac_status = dmac_status;
+ ),
+ TP_printk("seq=0x%04x, status=[fe 0x%08x be 0x%08x], rsvd=0x%04x, "
+ "last_mb=%u, err_trig=0x%08x, irq_status=0x%08x, "
+ "mmu_status=0x%08x, dmac_status=0x%08x",
+ __entry->seqno,
+ __entry->fe_status,
+ __entry->be_status,
+ __entry->rsvd,
+ __entry->last_mb,
+ __entry->err_trig,
+ __entry->irq_status,
+ __entry->mmu_status,
+ __entry->dmac_status)
+);
+
+#endif /* _IPVR_TRACE_H_ */
+
+ /* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#include <trace/define_trace.h>
diff --git a/drivers/gpu/drm/ipvr/ved_cmd.c b/drivers/gpu/drm/ipvr/ved_cmd.c
new file mode 100644
index 0000000..78c1f1e
--- /dev/null
+++ b/drivers/gpu/drm/ipvr/ved_cmd.c
@@ -0,0 +1,882 @@
+/**************************************************************************
+ * ved_cmd.c: VED command handling between host driver and VED firmware
+ *
+ * Copyright (c) 2014 Intel Corporation, Hillsboro, OR, USA
+ * Copyright (c) Imagination Technologies Limited, UK
+ * Copyright (c) 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Authors:
+ * Fei Jiang <fei.jiang at intel.com>
+ * Yao Cheng <yao.cheng at intel.com>
+ *
+ **************************************************************************/
+
+#include "ipvr_gem.h"
+#include "ipvr_mmu.h"
+#include "ipvr_bo.h"
+#include "ipvr_trace.h"
+#include "ipvr_fence.h"
+#include "ved_cmd.h"
+#include "ved_fw.h"
+#include "ved_msg.h"
+#include "ved_reg.h"
+#include "ved_pm.h"
+#include <linux/pm_runtime.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+
+#ifndef list_first_entry
+#define list_first_entry(ptr, type, member) \
+ list_entry((ptr)->next, type, member)
+#endif
+
+int ved_mtx_send(struct ved_private *ved_priv, const void *msg)
+{
+ static struct fw_padding_msg pad_msg;
+ const u32 *p_msg = (u32 *)msg;
+ struct drm_ipvr_private *dev_priv = ved_priv->dev_priv;
+ u32 msg_num, words_free, ridx, widx, buf_size, buf_offset;
+ int ret = 0;
+ int i;
+ union msg_header *header;
+ header = (union msg_header *)msg;
+
+ IPVR_DEBUG_ENTRY("enter.\n");
+
+ /* we need clocks enabled before we touch VEC local ram,
+ * but fw will take care of the clock after fw is loaded
+ */
+
+ msg_num = (header->bits.msg_size + 3) / 4;
+
+ /* debug code for msg dump */
+ IPVR_DEBUG_VED("MSVDX: ved_mtx_send is %dDW\n", msg_num);
+
+ for (i = 0; i < msg_num; i++)
+ IPVR_DEBUG_VED(" 0x%08x\n", p_msg[i]);
+
+ buf_size = IPVR_REG_READ32(MSVDX_COMMS_TO_MTX_BUF_SIZE) &
+ ((1 << 16) - 1);
+
+ if (msg_num > buf_size) {
+ ret = -EINVAL;
+ IPVR_ERROR("VED: message exceed maximum, ret:%d\n", ret);
+ goto out;
+ }
+
+ ridx = IPVR_REG_READ32(MSVDX_COMMS_TO_MTX_RD_INDEX);
+ widx = IPVR_REG_READ32(MSVDX_COMMS_TO_MTX_WRT_INDEX);
+
+
+ buf_size = IPVR_REG_READ32(MSVDX_COMMS_TO_MTX_BUF_SIZE) &
+ ((1 << 16) - 1);
+ /*0x2000 is VEC Local Ram offset*/
+ buf_offset =
+ (IPVR_REG_READ32(MSVDX_COMMS_TO_MTX_BUF_SIZE) >> 16) + 0x2000;
+
+ /* message would wrap, need to send a pad message */
+ if (widx + msg_num > buf_size) {
+ /* Shouldn't happen for a PAD message itself */
+ if (header->bits.msg_type == MTX_MSGID_PADDING)
+ IPVR_DEBUG_WARN("VED: should not wrap pad msg, "
+ "buf_size is %d, widx is %d, msg_num is %d.\n",
+ buf_size, widx, msg_num);
+
+ /* if the read pointer is at zero then we must wait for it to
+ * change otherwise the write pointer will equal the read
+ * pointer,which should only happen when the buffer is empty
+ *
+ * This will only happens if we try to overfill the queue,
+ * queue management should make
+ * sure this never happens in the first place.
+ */
+ if (0 == ridx) {
+ ret = -EINVAL;
+ IPVR_ERROR("MSVDX: RIndex=0, ret:%d\n", ret);
+ goto out;
+ }
+
+ /* Send a pad message */
+ pad_msg.header.bits.msg_size = (buf_size - widx) << 2;
+ pad_msg.header.bits.msg_type = MTX_MSGID_PADDING;
+ ved_mtx_send(ved_priv, (void *)&pad_msg);
+ widx = IPVR_REG_READ32(MSVDX_COMMS_TO_MTX_WRT_INDEX);
+ }
+
+ if (widx >= ridx)
+ words_free = buf_size - (widx - ridx) - 1;
+ else
+ words_free = ridx - widx - 1;
+
+ if (msg_num > words_free) {
+ ret = -EINVAL;
+ IPVR_ERROR("MSVDX: msg_num > words_free, ret:%d\n", ret);
+ goto out;
+ }
+ while (msg_num > 0) {
+ IPVR_REG_WRITE32(*p_msg++, buf_offset + (widx << 2));
+ msg_num--;
+ widx++;
+ if (buf_size == widx)
+ widx = 0;
+ }
+
+ IPVR_REG_WRITE32(widx, MSVDX_COMMS_TO_MTX_WRT_INDEX);
+
+ /* Make sure clocks are enabled before we kick
+ * but fw will take care of the clock after fw is loaded
+ */
+
+ /* signal an interrupt to let the mtx know there is a new message */
+ IPVR_REG_WRITE32(1, MTX_KICK_INPUT_OFFSET);
+
+ /* Read MSVDX Register several times in case Idle signal assert */
+ IPVR_REG_READ32(MSVDX_INTERRUPT_STATUS_OFFSET);
+ IPVR_REG_READ32(MSVDX_INTERRUPT_STATUS_OFFSET);
+ IPVR_REG_READ32(MSVDX_INTERRUPT_STATUS_OFFSET);
+ IPVR_REG_READ32(MSVDX_INTERRUPT_STATUS_OFFSET);
+
+out:
+ return ret;
+}
+
+static int ved_cmd_send(struct ved_private *ved_priv, void *cmd,
+ u32 cmd_size, struct ipvr_context *ipvr_ctx)
+{
+ int ret = 0;
+ union msg_header *header;
+ u32 cur_seq = 0xffffffff;
+
+ while (cmd_size > 0) {
+ u32 cur_cmd_size, cur_cmd_id;
+ header = (union msg_header *)cmd;
+ cur_cmd_size = header->bits.msg_size;
+ cur_cmd_id = header->bits.msg_type;
+
+ cur_seq = ((struct fw_msg_header *)cmd)->header.bits.msg_fence;
+
+ if (cur_seq != 0xffffffff) {
+ ipvr_ctx->cur_seq = cur_seq;
+ }
+
+ if (cur_cmd_size > cmd_size) {
+ ret = -EINVAL;
+ IPVR_ERROR("VED: cmd_size %u cur_cmd_size %u.\n",
+ cmd_size, cur_cmd_size);
+ goto out;
+ }
+
+ /* Send the message to h/w */
+ trace_ved_cmd_send(cur_cmd_id, cur_seq);
+ ret = ved_mtx_send(ved_priv, cmd);
+ if (ret) {
+ IPVR_DEBUG_WARN("VED: ret:%d\n", ret);
+ goto out;
+ }
+ cmd += cur_cmd_size;
+ cmd_size -= cur_cmd_size;
+ if (cur_cmd_id == MTX_MSGID_HOST_BE_OPP ||
+ cur_cmd_id == MTX_MSGID_DEBLOCK ||
+ cur_cmd_id == MTX_MSGID_INTRA_OOLD) {
+ cmd += (sizeof(struct fw_deblock_msg) - cur_cmd_size);
+ cmd_size -=
+ (sizeof(struct fw_deblock_msg) - cur_cmd_size);
+ }
+ }
+out:
+ IPVR_DEBUG_VED("VED: ret:%d\n", ret);
+ return ret;
+}
+
+int ved_cmd_dequeue_send(struct ved_private *ved_priv)
+{
+ struct ved_cmd_queue *ved_cmd = NULL;
+ int ret = 0;
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&ved_priv->ved_lock, irq_flags);
+ if (list_empty(&ved_priv->ved_queue)) {
+ IPVR_DEBUG_VED("VED: ved cmd queue empty.\n");
+ ved_priv->ved_busy = false;
+ spin_unlock_irqrestore(&ved_priv->ved_lock, irq_flags);
+ return -EINVAL;
+ }
+
+ ved_cmd = list_first_entry(&ved_priv->ved_queue,
+ struct ved_cmd_queue, head);
+ list_del(&ved_cmd->head);
+ spin_unlock_irqrestore(&ved_priv->ved_lock, irq_flags);
+
+ IPVR_DEBUG_VED("VED: cmd queue seq is %08x.\n", ved_cmd->cmd_seq);
+
+ ipvr_set_tile(ved_priv->dev_priv, ved_cmd->tiling_scheme,
+ ved_cmd->tiling_stride);
+
+ ret = ved_cmd_send(ved_priv, ved_cmd->cmd,
+ ved_cmd->cmd_size, ved_cmd->ipvr_ctx);
+ if (ret) {
+ IPVR_ERROR("VED: ved_cmd_send failed.\n");
+ ret = -EINVAL;
+ }
+
+ kfree(ved_cmd->cmd);
+ kfree(ved_cmd);
+
+ return ret;
+}
+
+void ved_flush_cmd_queue(struct ved_private *ved_priv)
+{
+ struct ved_cmd_queue *ved_cmd;
+ struct list_head *list, *next;
+ unsigned long irq_flags;
+ spin_lock_irqsave(&ved_priv->ved_lock, irq_flags);
+ /* Flush the VED cmd queue and signal all fences in the queue */
+ list_for_each_safe(list, next, &ved_priv->ved_queue) {
+ ved_cmd = list_entry(list, struct ved_cmd_queue, head);
+ list_del(list);
+ IPVR_DEBUG_VED("VED: flushing sequence:0x%08x.\n",
+ ved_cmd->cmd_seq);
+ ved_priv->ved_cur_seq = ved_cmd->cmd_seq;
+
+ ipvr_fence_process(ved_priv->dev_priv, ved_cmd->cmd_seq, IPVR_CMD_SKIP);
+ kfree(ved_cmd->cmd);
+ kfree(ved_cmd);
+ }
+ ved_priv->ved_busy = false;
+ spin_unlock_irqrestore(&ved_priv->ved_lock, irq_flags);
+}
+
+static int
+ved_map_command(struct ved_private *ved_priv,
+ struct drm_ipvr_gem_object *cmd_buffer,
+ u32 cmd_size, void **ved_cmd,
+ u16 sequence, s32 copy_cmd,
+ struct ipvr_context *ipvr_ctx)
+{
+ int ret = 0;
+ u32 cmd_size_remain;
+ void *cmd, *cmd_copy, *cmd_start;
+ union msg_header *header;
+ struct ipvr_fence *fence = NULL;
+
+ /* command buffers may not exceed page boundary */
+ if (cmd_size > PAGE_SIZE)
+ return -EINVAL;
+
+ cmd_start = kmap(sg_page(cmd_buffer->sg_table->sgl));
+ if (!cmd_start) {
+ IPVR_ERROR("VED: kmap failed.\n");
+ return -EFAULT;
+ }
+
+ cmd = cmd_start;
+ cmd_size_remain = cmd_size;
+
+ while (cmd_size_remain > 0) {
+ u32 cur_cmd_size, cur_cmd_id, mmu_ptd, msvdx_mmu_invalid;
+ if (cmd_size_remain < MTX_GENMSG_HEADER_SIZE) {
+ ret = -EINVAL;
+ goto out;
+ }
+ header = (union msg_header *)cmd;
+ cur_cmd_size = header->bits.msg_size;
+ cur_cmd_id = header->bits.msg_type;
+ mmu_ptd = 0;
+ msvdx_mmu_invalid = 0;
+
+ IPVR_DEBUG_VED("cmd start at %lx cur_cmd_size = %d"
+ " cur_cmd_id = %02x fence = %08x\n",
+ (unsigned long)cmd, cur_cmd_size,
+ cur_cmd_id, sequence);
+ if ((cur_cmd_size % sizeof(u32))
+ || (cur_cmd_size > cmd_size_remain)) {
+ ret = -EINVAL;
+ IPVR_ERROR("VED: cmd size err, ret:%d.\n", ret);
+ goto out;
+ }
+
+ switch (cur_cmd_id) {
+ case MTX_MSGID_DECODE_FE: {
+ struct fw_decode_msg *decode_msg;
+ if (sizeof(struct fw_decode_msg) > cmd_size_remain) {
+ /* Msg size is not correct */
+ ret = -EINVAL;
+ IPVR_DEBUG_WARN("MSVDX: wrong msg size.\n");
+ goto out;
+ }
+ decode_msg = (struct fw_decode_msg *)cmd;
+ decode_msg->header.bits.msg_fence = sequence;
+
+ mmu_ptd = ipvr_get_default_pd_addr32(ved_priv->dev_priv->mmu);
+ if (mmu_ptd == 0) {
+ ret = -EINVAL;
+ IPVR_DEBUG_WARN("MSVDX: invalid PD addr32.\n");
+ goto out;
+ }
+ msvdx_mmu_invalid = atomic_cmpxchg(&ved_priv->dev_priv->ipvr_mmu_invaldc, 1, 0);
+ if (msvdx_mmu_invalid == 1) {
+ decode_msg->flag_size.bits.flags |= FW_INVALIDATE_MMU;
+ IPVR_DEBUG_VED("VED: Set MMU invalidate\n");
+ }
+ /* if ctx_id is not passed, use default id */
+ if (decode_msg->mmu_context.bits.context == 0)
+ decode_msg->mmu_context.bits.context =
+ ved_priv->dev_priv->default_ctx.ctx_id;
+
+ decode_msg->mmu_context.bits.mmu_ptd = mmu_ptd >> 8;
+ IPVR_DEBUG_VED("VED: MSGID_DECODE_FE:"
+ " - fence: %08x"
+ " - flags: %08x - buffer_size: %08x"
+ " - crtl_alloc_addr: %08x"
+ " - context: %08x - mmu_ptd: %08x"
+ " - operating_mode: %08x.\n",
+ decode_msg->header.bits.msg_fence,
+ decode_msg->flag_size.bits.flags,
+ decode_msg->flag_size.bits.buffer_size,
+ decode_msg->crtl_alloc_addr,
+ decode_msg->mmu_context.bits.context,
+ decode_msg->mmu_context.bits.mmu_ptd,
+ decode_msg->operating_mode);
+ break;
+ }
+ default:
+ /* Msg not supported */
+ ret = -EINVAL;
+ IPVR_DEBUG_WARN("VED: msg not supported.\n");
+ goto out;
+ }
+
+ cmd += cur_cmd_size;
+ cmd_size_remain -= cur_cmd_size;
+ if (((sequence++) & 0xf) == 0xf) {
+ ret = -EINVAL;
+ IPVR_DEBUG_WARN("VED: too many cmds, abort.\n");
+ goto out;
+ }
+ }
+
+ ved_priv->num_cmd = ((--sequence) & 0xf);
+
+ ipvr_fence_create(ved_priv->dev_priv, &fence);
+
+ ipvr_fence_buffer_objects(&ved_priv->dev_priv->validate_ctx.validate_list,
+ fence);
+
+ if (copy_cmd) {
+ IPVR_DEBUG_VED("VED: copying command.\n");
+
+ cmd_copy = kzalloc(cmd_size, GFP_KERNEL);
+ if (cmd_copy == NULL) {
+ ret = -ENOMEM;
+ IPVR_ERROR("VED: fail to callc, ret=:%d\n", ret);
+ goto out;
+ }
+ memcpy(cmd_copy, cmd_start, cmd_size);
+ *ved_cmd = cmd_copy;
+ } else {
+ IPVR_DEBUG_VED("VED: did NOT copy command.\n");
+ ipvr_set_tile(ved_priv->dev_priv, ved_priv->ipvr_ctx->tiling_scheme,
+ ved_priv->ipvr_ctx->tiling_stride);
+
+ ret = ved_cmd_send(ved_priv, cmd_start, cmd_size, ipvr_ctx);
+ if (ret) {
+ IPVR_ERROR("VED: ved_cmd_send failed\n");
+ ret = -EINVAL;
+ }
+ }
+
+out:
+ kunmap(sg_page(cmd_buffer->sg_table->sgl));
+
+ return ret;
+}
+
+static int
+ved_submit_cmdbuf_copy(struct ved_private *ved_priv,
+ struct drm_ipvr_gem_object *cmd_buffer,
+ u32 cmd_size,
+ struct ipvr_context *ipvr_ctx,
+ u32 fence_flag)
+{
+ struct ved_cmd_queue *ved_cmd;
+ u16 sequence = (ved_priv->dev_priv->last_seq << 4);
+ unsigned long irq_flags;
+ void *cmd = NULL;
+ int ret;
+ union msg_header *header;
+
+ /* queue the command to be sent when the h/w is ready */
+ IPVR_DEBUG_VED("VED: queueing sequence:%08x.\n",
+ sequence);
+ ved_cmd = kzalloc(sizeof(struct ved_cmd_queue),
+ GFP_KERNEL);
+ if (ved_cmd == NULL) {
+ IPVR_ERROR("MSVDXQUE: Out of memory...\n");
+ return -ENOMEM;
+ }
+
+ ret = ved_map_command(ved_priv, cmd_buffer, cmd_size,
+ &cmd, sequence, 1, ipvr_ctx);
+ if (ret) {
+ IPVR_ERROR("VED: Failed to extract cmd\n");
+ kfree(ved_cmd);
+ /* -EINVAL or -EFAULT or -ENOMEM */
+ return ret;
+ }
+ header = (union msg_header *)cmd;
+ ved_cmd->cmd = cmd;
+ ved_cmd->cmd_size = cmd_size;
+ ved_cmd->cmd_seq = sequence;
+
+ ved_cmd->tiling_scheme = ved_priv->ipvr_ctx->tiling_scheme;
+ ved_cmd->tiling_stride = ved_priv->ipvr_ctx->tiling_stride;
+ ved_cmd->tfile =
+ ved_priv->tfile;
+ ved_cmd->ipvr_ctx = ipvr_ctx;
+ spin_lock_irqsave(&ved_priv->ved_lock, irq_flags);
+ list_add_tail(&ved_cmd->head, &ved_priv->ved_queue);
+ spin_unlock_irqrestore(&ved_priv->ved_lock, irq_flags);
+ if (!ved_priv->ved_busy) {
+ ved_priv->ved_busy = true;
+ IPVR_DEBUG_VED("VED: Need immediate dequeue.\n");
+ ved_cmd_dequeue_send(ved_priv);
+ }
+ trace_ved_cmd_copy(header->bits.msg_type, sequence);
+
+ return ret;
+}
+
+int
+
+ved_submit_video_cmdbuf(struct ved_private *ved_priv,
+ struct drm_ipvr_gem_object *cmd_buffer,
+ u32 cmd_size,
+ struct ipvr_context *ipvr_ctx,
+ u32 fence_flag)
+{
+ unsigned long irq_flags;
+ struct drm_ipvr_private *dev_priv = ved_priv->dev_priv;
+ u16 sequence = (dev_priv->last_seq << 4) & 0xffff;
+ int ret = 0;
+
+ if (sequence == IPVR_FENCE_SIGNALED_SEQ) {
+ sequence = (++ved_priv->dev_priv->last_seq << 4) & 0xffff;
+ }
+
+ if (!ipvr_ctx) {
+ IPVR_ERROR("VED: null ctx\n");
+ return -ENOENT;
+ }
+
+ spin_lock_irqsave(&ved_priv->ved_lock, irq_flags);
+
+ ved_priv->ipvr_ctx = ipvr_ctx;
+
+ IPVR_DEBUG_VED("sequence is 0x%x, needs_reset is 0x%x.\n",
+ sequence, ved_priv->ved_needs_reset);
+
+ if (WARN_ON(ipvr_runtime_pm_get(ved_priv->dev_priv) < 0)) {
+ IPVR_ERROR("Failed to get ipvr power\n");
+ spin_unlock_irqrestore(&ved_priv->ved_lock, irq_flags);
+ return -EBUSY;
+ }
+
+ if (ved_priv->ved_busy) {
+ spin_unlock_irqrestore(&ved_priv->ved_lock, irq_flags);
+ ret = ved_submit_cmdbuf_copy(ved_priv, cmd_buffer,
+ cmd_size, ipvr_ctx, fence_flag);
+
+ return ret;
+ }
+
+ if (ved_priv->ved_needs_reset) {
+ spin_unlock_irqrestore(&ved_priv->ved_lock, irq_flags);
+ IPVR_DEBUG_VED("VED: will reset msvdx.\n");
+
+ if (ved_core_reset(ved_priv)) {
+ ret = -EBUSY;
+ IPVR_ERROR("VED: Reset failed.\n");
+ goto out_power_put;
+ }
+
+ ved_priv->ved_needs_reset = 0;
+ ved_priv->ved_busy = false;
+
+ if (ved_core_init(ved_priv->dev_priv)) {
+ ret = -EBUSY;
+ IPVR_DEBUG_WARN("VED: ved_core_init fail.\n");
+ goto out_power_put;
+ }
+
+ spin_lock_irqsave(&ved_priv->ved_lock, irq_flags);
+ }
+
+ if (!ved_priv->ved_fw_loaded) {
+ spin_unlock_irqrestore(&ved_priv->ved_lock, irq_flags);
+ IPVR_DEBUG_VED("VED: reload FW to MTX\n");
+ ret = ved_setup_fw(ved_priv);
+ if (ret) {
+ IPVR_ERROR("VED: fail to load FW\n");
+ /* FIXME: find a proper return value */
+ ret = -EFAULT;
+ goto out_power_put;
+ }
+ ved_priv->ved_fw_loaded = true;
+
+ IPVR_DEBUG_VED("VED: load firmware successfully\n");
+ spin_lock_irqsave(&ved_priv->ved_lock, irq_flags);
+ }
+
+ ved_priv->ved_busy = true;
+ spin_unlock_irqrestore(&ved_priv->ved_lock, irq_flags);
+ IPVR_DEBUG_VED("VED: commit command to HW,seq=0x%08x\n",
+ sequence);
+ ret = ved_map_command(ved_priv, cmd_buffer, cmd_size,
+ NULL, sequence, 0, ipvr_ctx);
+ if (ret) {
+ IPVR_ERROR("VED: Failed to extract cmd.\n");
+ goto out_power_put;
+ }
+
+ return 0;
+out_power_put:
+ if (WARN_ON(ipvr_runtime_pm_put(ved_priv->dev_priv, false) < 0))
+ IPVR_ERROR("Failed to put ipvr power\n");
+ return ret;
+}
+
+int ved_cmdbuf_video(struct ved_private *ved_priv,
+ struct drm_ipvr_gem_object *cmd_buffer,
+ u32 cmdbuf_size,
+ struct ipvr_context *ipvr_ctx)
+{
+ return ved_submit_video_cmdbuf(ved_priv, cmd_buffer, cmdbuf_size, ipvr_ctx, 0);
+}
+
+static int ved_handle_panic_msg(struct ved_private *ved_priv,
+ struct fw_panic_msg *panic_msg)
+{
+ /* For VXD385 firmware, fence value is not validate here */
+ u32 diff = 0;
+ u16 fence;
+ u32 err_trig, irq_sts, mmu_sts, dmac_sts;
+ struct drm_ipvr_private *dev_priv = ved_priv->dev_priv;
+ IPVR_DEBUG_WARN("MSVDX: MSGID_CMD_HW_PANIC:"
+ "Fault detected"
+ " - Fence: %08x"
+ " - fe_status mb: %08x"
+ " - be_status mb: %08x"
+ " - reserved2: %08x"
+ " - last mb: %08x"
+ " - resetting and ignoring error\n",
+ panic_msg->header.bits.msg_fence,
+ panic_msg->fe_status,
+ panic_msg->be_status,
+ panic_msg->mb.bits.reserved2,
+ panic_msg->mb.bits.last_mb);
+ /*
+ * If bit 8 of MSVDX_INTERRUPT_STATUS is set the fault
+ * was caused in the DMAC. In this case you should
+ * check bits 20:22 of MSVDX_INTERRUPT_STATUS.
+ * If bit 20 is set there was a problem DMAing the buffer
+ * back to host. If bit 22 is set you'll need to get the
+ * value of MSVDX_DMAC_STREAM_STATUS (0x648).
+ * If bit 1 is set then there was an issue DMAing
+ * the bitstream or termination code for parsing.
+ */
+ err_trig = IPVR_REG_READ32(MSVDX_COMMS_ERROR_TRIG);
+ irq_sts = IPVR_REG_READ32(MSVDX_INTERRUPT_STATUS_OFFSET);
+ mmu_sts = IPVR_REG_READ32(MSVDX_MMU_STATUS_OFFSET);
+ dmac_sts = IPVR_REG_READ32(MSVDX_DMAC_STREAM_STATUS_OFFSET);
+ IPVR_DEBUG_VED("MSVDX: MSVDX_COMMS_ERROR_TRIG is 0x%x,"
+ "MSVDX_INTERRUPT_STATUS is 0x%x,"
+ "MSVDX_MMU_STATUS is 0x%x,"
+ "MSVDX_DMAC_STREAM_STATUS is 0x%x.\n",
+ err_trig, irq_sts, mmu_sts, dmac_sts);
+
+ trace_ved_irq_panic(panic_msg, err_trig, irq_sts, mmu_sts, dmac_sts);
+
+ fence = panic_msg->header.bits.msg_fence;
+
+ ved_priv->ved_needs_reset = 1;
+
+ diff = ved_priv->ved_cur_seq - dev_priv->last_seq;
+ if (diff > 0x0FFFFFFF)
+ ved_priv->ved_cur_seq++;
+
+ IPVR_DEBUG_WARN("VED: Fence ID missing, assuming %08x\n",
+ ved_priv->ved_cur_seq);
+
+ ipvr_fence_process(dev_priv, ved_priv->ved_cur_seq, IPVR_CMD_FAILED);
+
+ /* Flush the command queue */
+ ved_flush_cmd_queue(ved_priv);
+ return 0;
+}
+
+static int
+ved_handle_completed_msg(struct ved_private *ved_priv,
+ struct fw_completed_msg *completed_msg)
+{
+ struct drm_ipvr_private *dev_priv = ved_priv->dev_priv;
+ u16 fence, flags;
+ struct ipvr_context *ipvr_ctx;
+
+ IPVR_DEBUG_VED("VED: MSGID_CMD_COMPLETED:"
+ " - Fence: %08x - flags: %08x - vdebcr: %08x"
+ " - first_mb : %d - last_mb: %d\n",
+ completed_msg->header.bits.msg_fence,
+ completed_msg->flags, completed_msg->vdebcr,
+ completed_msg->mb.bits.start_mb,
+ completed_msg->mb.bits.last_mb);
+
+ trace_ved_irq_completed(completed_msg);
+
+ flags = completed_msg->flags;
+ fence = completed_msg->header.bits.msg_fence;
+
+ ved_priv->ved_cur_seq = fence;
+
+ ipvr_fence_process(dev_priv, fence, IPVR_CMD_SUCCESS);
+
+ ipvr_ctx = ipvr_find_ctx_with_fence(dev_priv, fence);
+ if (unlikely(ipvr_ctx == NULL)) {
+ IPVR_DEBUG_WARN("abnormal complete msg.\n");
+ return -EINVAL;
+ }
+
+ if (flags & FW_VA_RENDER_HOST_INT) {
+ /* Now send the next command from the msvdx cmd queue */
+ ved_cmd_dequeue_send(ved_priv);
+ }
+ return 0;
+}
+
+/*
+ * MSVDX MTX interrupt
+ */
+static void ved_mtx_interrupt(struct ved_private *ved_priv)
+{
+ static u32 buf[128]; /* message buffer */
+ u32 ridx, widx, buf_size, buf_offset;
+ u32 num, ofs; /* message num and offset */
+ union msg_header *header;
+ int cmd_complete = 0;
+ struct drm_ipvr_private *dev_priv = ved_priv->dev_priv;
+ IPVR_DEBUG_VED("VED: Got a VED MTX interrupt.\n");
+
+ /* we need clocks enabled before we touch VEC local ram,
+ * but fw will take care of the clock after fw is loaded
+ */
+
+loop: /* just for coding style check */
+ ridx = IPVR_REG_READ32(MSVDX_COMMS_TO_HOST_RD_INDEX);
+ widx = IPVR_REG_READ32(MSVDX_COMMS_TO_HOST_WRT_INDEX);
+
+ /* Get out of here if nothing */
+ if (ridx == widx)
+ goto done;
+
+ buf_size = IPVR_REG_READ32(MSVDX_COMMS_TO_HOST_BUF_SIZE) &
+ ((1 << 16) - 1);
+ /*0x2000 is VEC Local Ram offset*/
+ buf_offset = (IPVR_REG_READ32(MSVDX_COMMS_TO_HOST_BUF_SIZE) >> 16)
+ + 0x2000;
+
+ ofs = 0;
+ buf[ofs] = IPVR_REG_READ32(buf_offset + (ridx << 2));
+ header = (union msg_header *)buf;
+
+ /* round to nearest word */
+ num = (header->bits.msg_size + 3) / 4;
+
+ /* ASSERT(num <= sizeof(buf) / sizeof(u32)); */
+
+ if (++ridx >= buf_size)
+ ridx = 0;
+
+ for (ofs++; ofs < num; ofs++) {
+ buf[ofs] = IPVR_REG_READ32(buf_offset + (ridx << 2));
+
+ if (++ridx >= buf_size)
+ ridx = 0;
+ }
+
+ /* Update the Read index */
+ IPVR_REG_WRITE32(ridx, MSVDX_COMMS_TO_HOST_RD_INDEX);
+
+ if (ved_priv->ved_needs_reset)
+ goto loop;
+
+ switch (header->bits.msg_type) {
+ case MTX_MSGID_HW_PANIC: {
+ struct fw_panic_msg *panic_msg = (struct fw_panic_msg *)buf;
+ cmd_complete = 1;
+ ved_handle_panic_msg(ved_priv, panic_msg);
+ /**
+ * panic msg clears all pending cmds and breaks the cmd<->irq pairing
+ */
+ if (WARN_ON(ipvr_runtime_pm_put_all(ved_priv->dev_priv, true) < 0)) {
+ IPVR_ERROR("Error clearing pending events and put power\n");
+ }
+ goto done;
+ }
+
+ case MTX_MSGID_COMPLETED: {
+ struct fw_completed_msg *completed_msg =
+ (struct fw_completed_msg *)buf;
+ cmd_complete = 1;
+ if (ved_handle_completed_msg(ved_priv, completed_msg))
+ cmd_complete = 0;
+ /**
+ * for VP8, cmd and COMPLETED msg are paired. we can safely call
+ * get in execbuf_ioctl and call put here
+ */
+ if (WARN_ON(ipvr_runtime_pm_put(ved_priv->dev_priv, true) < 0)) {
+ IPVR_ERROR("Error put power\n");
+ }
+ break;
+ }
+
+ default:
+ IPVR_ERROR("VED: unknown message from MTX, ID:0x%08x.\n",
+ header->bits.msg_type);
+ goto done;
+ }
+
+done:
+ IPVR_DEBUG_VED("VED Interrupt: finish process a message.\n");
+ if (ridx != widx) {
+ IPVR_DEBUG_VED("VED: there are more message to be read.\n");
+ goto loop;
+ }
+
+ mb(); /* TBD check this... */
+}
+
+/*
+ * MSVDX interrupt.
+ */
+int ved_irq_handler(struct ved_private *ved_priv)
+{
+ u32 msvdx_stat;
+ struct drm_ipvr_private *dev_priv = ved_priv->dev_priv;
+ msvdx_stat = IPVR_REG_READ32(MSVDX_INTERRUPT_STATUS_OFFSET);
+
+ /* driver only needs to handle mtx irq
+ * For MMU fault irq, there's always a HW PANIC generated
+ * if HW/FW is totally hang, the lockup function will handle
+ * the reseting
+ */
+ if (msvdx_stat & MSVDX_INTERRUPT_STATUS_MMU_FAULT_IRQ_MASK) {
+ /*Ideally we should we should never get to this */
+ IPVR_DEBUG_IRQ("VED: MMU Fault:0x%x\n", msvdx_stat);
+
+ /* Pause MMU */
+ IPVR_REG_WRITE32(MSVDX_MMU_CONTROL0_MMU_PAUSE_MASK,
+ MSVDX_MMU_CONTROL0_OFFSET);
+ wmb();
+
+ /* Clear this interupt bit only */
+ IPVR_REG_WRITE32(MSVDX_INTERRUPT_STATUS_MMU_FAULT_IRQ_MASK,
+ MSVDX_INTERRUPT_CLEAR_OFFSET);
+ IPVR_REG_READ32(MSVDX_INTERRUPT_CLEAR_OFFSET);
+ rmb();
+
+ ved_priv->ved_needs_reset = 1;
+ } else if (msvdx_stat & MSVDX_INTERRUPT_STATUS_MTX_IRQ_MASK) {
+ IPVR_DEBUG_IRQ("VED: msvdx_stat: 0x%x(MTX)\n", msvdx_stat);
+
+ /* Clear all interupt bits */
+ IPVR_REG_WRITE32(0xffff, MSVDX_INTERRUPT_CLEAR_OFFSET);
+
+ IPVR_REG_READ32(MSVDX_INTERRUPT_CLEAR_OFFSET);
+ rmb();
+
+ ved_mtx_interrupt(ved_priv);
+ }
+
+ return 0;
+}
+
+int ved_check_idle(struct ved_private *ved_priv)
+{
+ int loop, ret;
+ struct drm_ipvr_private *dev_priv;
+ if (!ved_priv)
+ return 0;
+
+ dev_priv = ved_priv->dev_priv;
+ if (!ved_priv->ved_fw_loaded)
+ return 0;
+
+ if (ved_priv->ved_busy) {
+ IPVR_DEBUG_PM("VED: ved_busy was set, return busy.\n");
+ return -EBUSY;
+ }
+
+ /* on some cores below 50502, there is one instance that
+ * read requests may not go to zero is in the case of a page fault,
+ * check core revision by reg MSVDX_CORE_REV, 385 core is 0x20001
+ * check if mmu page fault happend by reg MSVDX_INTERRUPT_STATUS,
+ * check was it a page table rather than a protection fault
+ * by reg MSVDX_MMU_STATUS, for such case,
+ * need call ved_core_reset as the work around */
+ if ((IPVR_REG_READ32(MSVDX_CORE_REV_OFFSET) < 0x00050502) &&
+ (IPVR_REG_READ32(MSVDX_INTERRUPT_STATUS_OFFSET)
+ & MSVDX_INTERRUPT_STATUS_MMU_FAULT_IRQ_MASK) &&
+ (IPVR_REG_READ32(MSVDX_MMU_STATUS_OFFSET) & 1)) {
+ IPVR_DEBUG_WARN("mmu page fault, recover by core_reset.\n");
+ return 0;
+ }
+
+ /* check MSVDX_MMU_MEM_REQ to confirm there's no memory requests */
+ for (loop = 0; loop < 10; loop++)
+ ret = ved_wait_for_register(ved_priv,
+ MSVDX_MMU_MEM_REQ_OFFSET,
+ 0, 0xff, 100, 1);
+ if (ret) {
+ IPVR_DEBUG_WARN("MSVDX: MSVDX_MMU_MEM_REQ reg is 0x%x,\n"
+ "indicate mem busy, prevent power off ved,"
+ "MSVDX_COMMS_FW_STATUS reg is 0x%x,"
+ "MSVDX_COMMS_ERROR_TRIG reg is 0x%x,",
+ IPVR_REG_READ32(MSVDX_MMU_MEM_REQ_OFFSET),
+ IPVR_REG_READ32(MSVDX_COMMS_FW_STATUS),
+ IPVR_REG_READ32(MSVDX_COMMS_ERROR_TRIG));
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+void ved_check_reset_fw(struct ved_private *ved_priv)
+{
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&ved_priv->ved_lock, irq_flags);
+
+ /* handling fw upload here if required */
+ /* power off first, then hw_begin will power up/upload FW correctly */
+ if (ved_priv->ved_needs_reset & MSVDX_RESET_NEEDS_REUPLOAD_FW) {
+ ved_priv->ved_needs_reset &= ~MSVDX_RESET_NEEDS_REUPLOAD_FW;
+ spin_unlock_irqrestore(&ved_priv->ved_lock, irq_flags);
+ IPVR_DEBUG_VED("VED: force power off VED due to decode err\n");
+ spin_lock_irqsave(&ved_priv->ved_lock, irq_flags);
+ }
+ spin_unlock_irqrestore(&ved_priv->ved_lock, irq_flags);
+}
diff --git a/drivers/gpu/drm/ipvr/ved_cmd.h b/drivers/gpu/drm/ipvr/ved_cmd.h
new file mode 100644
index 0000000..cf932ed
--- /dev/null
+++ b/drivers/gpu/drm/ipvr/ved_cmd.h
@@ -0,0 +1,71 @@
+/**************************************************************************
+ * ved_cmd.h: VED header file to support command buffer handling
+ *
+ * Copyright (c) 2014 Intel Corporation, Hillsboro, OR, USA
+ * Copyright (c) Imagination Technologies Limited, UK
+ * Copyright (c) 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Authors:
+ * Fei Jiang <fei.jiang at intel.com>
+ * Yao Cheng <yao.cheng at intel.com>
+ *
+ **************************************************************************/
+
+#ifndef _VED_CMD_H_
+#define _VED_CMD_H_
+
+#include "ipvr_drv.h"
+#include "ipvr_drm.h"
+#include "ipvr_gem.h"
+#include "ipvr_fence.h"
+#include "ipvr_exec.h"
+#include "ved_reg.h"
+#include "ved_pm.h"
+
+struct ved_cmd_queue {
+ struct list_head head;
+ void *cmd;
+ u32 cmd_size;
+ u16 cmd_seq;
+ u32 fence_flag;
+ u8 tiling_scheme;
+ u8 tiling_stride;
+ struct drm_file *tfile;
+ struct ipvr_context *ipvr_ctx;
+};
+
+int ved_irq_handler(struct ved_private *ved_priv);
+
+int ved_mtx_send(struct ved_private *ved_priv, const void *msg);
+
+int ved_check_idle(struct ved_private *ved_priv);
+
+void ved_check_reset_fw(struct ved_private *ved_priv);
+
+void ved_flush_cmd_queue(struct ved_private *ved_priv);
+
+int ved_cmdbuf_video(struct ved_private *ved_priv,
+ struct drm_ipvr_gem_object *cmd_buffer,
+ u32 cmdbuf_size, struct ipvr_context *ipvr_ctx);
+
+int ved_submit_video_cmdbuf(struct ved_private *ved_priv,
+ struct drm_ipvr_gem_object *cmd_buffer, u32 cmd_size,
+ struct ipvr_context *ipvr_ctx, u32 fence_flag);
+
+int ved_cmd_dequeue_send(struct ved_private *ved_priv);
+
+#endif
diff --git a/drivers/gpu/drm/ipvr/ved_fw.c b/drivers/gpu/drm/ipvr/ved_fw.c
new file mode 100644
index 0000000..43682da
--- /dev/null
+++ b/drivers/gpu/drm/ipvr/ved_fw.c
@@ -0,0 +1,1199 @@
+/**************************************************************************
+ * ved_fw.c: VED initialization and mtx-firmware upload
+ *
+ * Copyright (c) 2014 Intel Corporation, Hillsboro, OR, USA
+ * Copyright (c) Imagination Technologies Limited, UK
+ * Copyright (c) 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Authors:
+ * Fei Jiang <fei.jiang at intel.com>
+ * Yao Cheng <yao.cheng at intel.com>
+ *
+ **************************************************************************/
+
+#include "ipvr_bo.h"
+#include "ipvr_mmu.h"
+#include "ipvr_gem.h"
+#include "ved_fw.h"
+#include "ved_cmd.h"
+#include "ved_msg.h"
+#include "ved_reg.h"
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <asm/cacheflush.h>
+
+#define STACKGUARDWORD 0x10101010
+#define MSVDX_MTX_DATA_LOCATION 0x82880000
+#define UNINITILISE_MEM 0xcdcdcdcd
+#define FIRMWARE_NAME "msvdx_fw_mfld_DE2.0.bin"
+
+/* VED FW header */
+struct ved_fw {
+ u32 ver;
+ u32 text_size;
+ u32 data_size;
+ u32 data_location;
+};
+
+
+void ved_clear_irq(struct ved_private *ved_priv)
+{
+ u32 mtx_int = 0;
+ struct drm_ipvr_private *dev_priv = ved_priv->dev_priv;
+ /* Clear MTX interrupt */
+ REGIO_WRITE_FIELD_LITE(mtx_int, MSVDX_INTERRUPT_STATUS, MTX_IRQ, 1);
+ IPVR_REG_WRITE32(mtx_int, MSVDX_INTERRUPT_CLEAR_OFFSET);
+}
+
+/* following two functions also works for CLV and MFLD */
+/* IPVR_INT_ENABLE_R is set in ipvr_irq_(un)install_islands */
+void ved_disable_irq(struct ved_private *ved_priv)
+{
+ u32 enables = 0;
+ struct drm_ipvr_private *dev_priv = ved_priv->dev_priv;
+ REGIO_WRITE_FIELD_LITE(enables, MSVDX_INTERRUPT_STATUS, MTX_IRQ, 0);
+ IPVR_REG_WRITE32(enables, MSVDX_HOST_INTERRUPT_ENABLE_OFFSET);
+}
+
+void ved_enable_irq(struct ved_private *ved_priv)
+{
+ u32 enables = 0;
+ struct drm_ipvr_private *dev_priv = ved_priv->dev_priv;
+ /* Only enable the master core IRQ*/
+ REGIO_WRITE_FIELD_LITE(enables, MSVDX_INTERRUPT_STATUS, MTX_IRQ,
+ 1);
+ IPVR_REG_WRITE32(enables, MSVDX_HOST_INTERRUPT_ENABLE_OFFSET);
+}
+
+/*
+ * the original 1000 of udelay is derive from reference driver
+ * From Liu, Haiyang, changed the originial udelay value from 1000 to 5
+ * can save 3% C0 residence
+ */
+int
+ved_wait_for_register(struct ved_private *ved_priv,
+ u32 offset, u32 value, u32 enable,
+ u32 poll_cnt, u32 timeout)
+{
+ u32 reg_value = 0;
+ struct drm_ipvr_private *dev_priv = ved_priv->dev_priv;
+ while (poll_cnt) {
+ reg_value = IPVR_REG_READ32(offset);
+ if (value == (reg_value & enable))
+ return 0;
+
+ /* Wait a bit */
+ IPVR_UDELAY(timeout);
+ poll_cnt--;
+ }
+ IPVR_DEBUG_REG("MSVDX: Timeout while waiting for register %08x:"
+ " expecting %08x (mask %08x), got %08x\n",
+ offset, value, enable, reg_value);
+
+ return -EFAULT;
+}
+
+void
+ved_set_clocks(struct ved_private *ved_priv, u32 clock_state)
+{
+ u32 old_clock_state = 0;
+ struct drm_ipvr_private *dev_priv = ved_priv->dev_priv;
+ /* IPVR_DEBUG_VED("SetClocks to %x.\n", clock_state); */
+ old_clock_state = IPVR_REG_READ32(MSVDX_MAN_CLK_ENABLE_OFFSET);
+ if (old_clock_state == clock_state)
+ return;
+
+ if (clock_state == 0) {
+ /* Turn off clocks procedure */
+ if (old_clock_state) {
+ /* Turn off all the clocks except core */
+ IPVR_REG_WRITE32(
+ MSVDX_MAN_CLK_ENABLE_CORE_MAN_CLK_ENABLE_MASK,
+ MSVDX_MAN_CLK_ENABLE_OFFSET);
+
+ /* Make sure all the clocks are off except core */
+ ved_wait_for_register(ved_priv,
+ MSVDX_MAN_CLK_ENABLE_OFFSET,
+ MSVDX_MAN_CLK_ENABLE_CORE_MAN_CLK_ENABLE_MASK,
+ 0xffffffff, 2000000, 5);
+
+ /* Turn off core clock */
+ IPVR_REG_WRITE32(0, MSVDX_MAN_CLK_ENABLE_OFFSET);
+ }
+ } else {
+ u32 clocks_en = clock_state;
+
+ /*Make sure that core clock is not accidentally turned off */
+ clocks_en |= MSVDX_MAN_CLK_ENABLE_CORE_MAN_CLK_ENABLE_MASK;
+
+ /* If all clocks were disable do the bring up procedure */
+ if (old_clock_state == 0) {
+ /* turn on core clock */
+ IPVR_REG_WRITE32(
+ MSVDX_MAN_CLK_ENABLE_CORE_MAN_CLK_ENABLE_MASK,
+ MSVDX_MAN_CLK_ENABLE_OFFSET);
+
+ /* Make sure core clock is on */
+ ved_wait_for_register(ved_priv,
+ MSVDX_MAN_CLK_ENABLE_OFFSET,
+ MSVDX_MAN_CLK_ENABLE_CORE_MAN_CLK_ENABLE_MASK,
+ 0xffffffff, 2000000, 5);
+
+ /* turn on the other clocks as well */
+ IPVR_REG_WRITE32(clocks_en, MSVDX_MAN_CLK_ENABLE_OFFSET);
+
+ /* Make sure that all they are on */
+ ved_wait_for_register(ved_priv,
+ MSVDX_MAN_CLK_ENABLE_OFFSET,
+ clocks_en, 0xffffffff, 2000000, 5);
+ } else {
+ IPVR_REG_WRITE32(clocks_en, MSVDX_MAN_CLK_ENABLE_OFFSET);
+
+ /* Make sure that they are on */
+ ved_wait_for_register(ved_priv,
+ MSVDX_MAN_CLK_ENABLE_OFFSET,
+ clocks_en, 0xffffffff, 2000000, 5);
+ }
+ }
+}
+
+int ved_core_reset(struct ved_private *ved_priv)
+{
+ int ret = 0;
+ int loop;
+ u32 cmd;
+ struct drm_ipvr_private *dev_priv = ved_priv->dev_priv;
+ /* Enable Clocks */
+ IPVR_DEBUG_GENERAL("Enabling clocks.\n");
+ ved_set_clocks(ved_priv, clk_enable_all);
+
+ /* Always pause the MMU as the core may be still active
+ * when resetting. It is very bad to have memory
+ * activity at the same time as a reset - Very Very bad
+ */
+ IPVR_REG_WRITE32(2, MSVDX_MMU_CONTROL0_OFFSET);
+
+ /* BRN26106, BRN23944, BRN33671 */
+ /* This is neccessary for all cores up to Tourmaline */
+ if ((IPVR_REG_READ32(MSVDX_CORE_REV_OFFSET) < 0x00050502) &&
+ (IPVR_REG_READ32(MSVDX_INTERRUPT_STATUS_OFFSET)
+ & MSVDX_INTERRUPT_STATUS_MMU_FAULT_IRQ_MASK) &&
+ (IPVR_REG_READ32(MSVDX_MMU_STATUS_OFFSET) & 1)) {
+ u32 *pptd;
+ int loop;
+ unsigned long ptd_addr;
+
+ /* do work around */
+ ptd_addr = page_to_pfn(ved_priv->mmu_recover_page) << PAGE_SHIFT;
+ /* fixme: check ptd_addr bit length */
+ pptd = kmap(ved_priv->mmu_recover_page);
+ if (!pptd) {
+ IPVR_ERROR("failed to kmap mmu recover page.\n");
+ return -ENOMEM;
+ }
+ for (loop = 0; loop < 1024; loop++)
+ pptd[loop] = ptd_addr | 0x00000003;
+ IPVR_REG_WRITE32(ptd_addr, MSVDX_MMU_DIR_LIST_BASE_OFFSET + 0);
+ IPVR_REG_WRITE32(ptd_addr, MSVDX_MMU_DIR_LIST_BASE_OFFSET + 4);
+ IPVR_REG_WRITE32(ptd_addr, MSVDX_MMU_DIR_LIST_BASE_OFFSET + 8);
+ IPVR_REG_WRITE32(ptd_addr, MSVDX_MMU_DIR_LIST_BASE_OFFSET + 12);
+
+ IPVR_REG_WRITE32(6, MSVDX_MMU_CONTROL0_OFFSET);
+ IPVR_REG_WRITE32(MSVDX_INTERRUPT_STATUS_MMU_FAULT_IRQ_MASK,
+ MSVDX_INTERRUPT_STATUS_OFFSET);
+ kunmap(ved_priv->mmu_recover_page);
+ }
+
+ /* make sure *ALL* outstanding reads have gone away */
+ for (loop = 0; loop < 10; loop++)
+ ret = ved_wait_for_register(ved_priv, MSVDX_MMU_MEM_REQ_OFFSET,
+ 0, 0xff, 100, 1);
+ if (ret) {
+ IPVR_DEBUG_WARN("MSVDX_MMU_MEM_REQ is %d,\n"
+ "indicate outstanding read request 0.\n",
+ IPVR_REG_READ32(MSVDX_MMU_MEM_REQ_OFFSET));
+ ret = -1;
+ return ret;
+ }
+ /* disconnect RENDEC decoders from memory */
+ cmd = IPVR_REG_READ32(MSVDX_RENDEC_CONTROL1_OFFSET);
+ REGIO_WRITE_FIELD(cmd, MSVDX_RENDEC_CONTROL1, RENDEC_DEC_DISABLE, 1);
+ IPVR_REG_WRITE32(cmd, MSVDX_RENDEC_CONTROL1_OFFSET);
+
+ /* Issue software reset for all but core */
+ IPVR_REG_WRITE32((unsigned int)~MSVDX_CONTROL_MSVDX_SOFT_RESET_MASK,
+ MSVDX_CONTROL_OFFSET);
+ IPVR_REG_READ32(MSVDX_CONTROL_OFFSET);
+ /* bit format is set as little endian */
+ IPVR_REG_WRITE32(0, MSVDX_CONTROL_OFFSET);
+ /* make sure read requests are zero */
+ ret = ved_wait_for_register(ved_priv, MSVDX_MMU_MEM_REQ_OFFSET,
+ 0, 0xff, 100, 100);
+ if (!ret) {
+ /* Issue software reset */
+ IPVR_REG_WRITE32(MSVDX_CONTROL_MSVDX_SOFT_RESET_MASK,
+ MSVDX_CONTROL_OFFSET);
+
+ ret = ved_wait_for_register(ved_priv, MSVDX_CONTROL_OFFSET, 0,
+ MSVDX_CONTROL_MSVDX_SOFT_RESET_MASK,
+ 2000000, 5);
+ if (!ret) {
+ /* Clear interrupt enabled flag */
+ IPVR_REG_WRITE32(0, MSVDX_HOST_INTERRUPT_ENABLE_OFFSET);
+
+ /* Clear any pending interrupt flags */
+ IPVR_REG_WRITE32(0xFFFFFFFF, MSVDX_INTERRUPT_CLEAR_OFFSET);
+ } else {
+ IPVR_DEBUG_WARN("MSVDX_CONTROL_OFFSET is %d,\n"
+ "indicate software reset failed.\n",
+ IPVR_REG_READ32(MSVDX_CONTROL_OFFSET));
+ }
+ } else {
+ IPVR_DEBUG_WARN("MSVDX_MMU_MEM_REQ is %d,\n"
+ "indicate outstanding read request 1.\n",
+ IPVR_REG_READ32(MSVDX_MMU_MEM_REQ_OFFSET));
+ }
+ return ret;
+}
+
+/*
+ * Reset chip and disable interrupts.
+ * Return 0 success, 1 failure
+ * use ved_core_reset instead of ved_reset
+ */
+int ved_reset(struct ved_private *ved_priv)
+{
+ int ret = 0;
+ struct drm_ipvr_private *dev_priv = ved_priv->dev_priv;
+ /* Issue software reset */
+ /* IPVR_REG_WRITE32(msvdx_sw_reset_all, MSVDX_CONTROL); */
+ IPVR_REG_WRITE32(MSVDX_CONTROL_MSVDX_SOFT_RESET_MASK,
+ MSVDX_CONTROL_OFFSET);
+
+ ret = ved_wait_for_register(ved_priv, MSVDX_CONTROL_OFFSET, 0,
+ MSVDX_CONTROL_MSVDX_SOFT_RESET_MASK, 2000000, 5);
+ if (!ret) {
+ /* Clear interrupt enabled flag */
+ IPVR_REG_WRITE32(0, MSVDX_HOST_INTERRUPT_ENABLE_OFFSET);
+
+ /* Clear any pending interrupt flags */
+ IPVR_REG_WRITE32(0xFFFFFFFF, MSVDX_INTERRUPT_CLEAR_OFFSET);
+ } else {
+ IPVR_DEBUG_WARN("MSVDX_CONTROL_OFFSET is %d,\n"
+ "indicate software reset failed.\n",
+ IPVR_REG_READ32(MSVDX_CONTROL_OFFSET));
+ }
+
+ return ret;
+}
+
+static int ved_alloc_ccb_for_rendec(struct ved_private *ved_priv,
+ int32_t ccb0_size,
+ int32_t ccb1_size)
+{
+ int ret;
+ size_t size;
+ u8 *ccb0_addr = NULL;
+ u8 *ccb1_addr = NULL;
+
+ IPVR_DEBUG_INIT("VED: setting up RENDEC, allocate CCB 0/1\n");
+
+ /*handling for ccb0*/
+ if (ved_priv->ccb0 == NULL) {
+ size = roundup(ccb0_size, PAGE_SIZE);
+ if (size == 0)
+ return -EINVAL;
+
+ /* Allocate the new object */
+ ved_priv->ccb0 = ipvr_gem_create(ved_priv->dev_priv, size, 0, 0);
+ if (IS_ERR(ved_priv->ccb0)) {
+ ret = PTR_ERR(ved_priv->ccb0);
+ IPVR_ERROR("VED: failed to allocate ccb0 buffer: %d.\n", ret);
+ ved_priv->ccb0 = NULL;
+ return -ENOMEM;
+ }
+
+ ved_priv->base_addr0 = ipvr_gem_object_mmu_offset(ved_priv->ccb0);
+
+ ccb0_addr = ipvr_gem_object_vmap(ved_priv->ccb0);
+ if (!ccb0_addr) {
+ ret = -ENOMEM;
+ IPVR_ERROR("VED: kmap failed for ccb0 buffer: %d.\n", ret);
+ goto err_free_ccb0;
+ }
+
+ memset(ccb0_addr, 0, size);
+ vunmap(ccb0_addr);
+ }
+
+ /*handling for ccb1*/
+ if (ved_priv->ccb1 == NULL) {
+ size = roundup(ccb1_size, PAGE_SIZE);
+ if (size == 0) {
+ return -EINVAL;
+ goto err_free_ccb0;
+ }
+
+ /* Allocate the new object */
+ ved_priv->ccb1 = ipvr_gem_create(ved_priv->dev_priv, size, 0, 0);
+ if (IS_ERR(ved_priv->ccb1)) {
+ ret = PTR_ERR(ved_priv->ccb1);
+ IPVR_ERROR("VED: failed to allocate ccb1 buffer: %d.\n", ret);
+ goto err_free_ccb0;
+ }
+
+ ved_priv->base_addr1 = ipvr_gem_object_mmu_offset(ved_priv->ccb1);
+
+ ccb1_addr = ipvr_gem_object_vmap(ved_priv->ccb1);
+ if (!ccb1_addr) {
+ ret = -ENOMEM;
+ IPVR_ERROR("VED: kmap failed for ccb1 buffer: %d.\n", ret);
+ goto err_free_ccb1;
+ }
+
+ memset(ccb1_addr, 0, size);
+ vunmap(ccb1_addr);
+ }
+
+ IPVR_DEBUG_INIT("VED: RENDEC A: %08x RENDEC B: %08x\n",
+ ved_priv->base_addr0, ved_priv->base_addr1);
+
+ return 0;
+err_free_ccb1:
+ drm_gem_object_unreference_unlocked(&ved_priv->ccb1->base);
+ ved_priv->ccb1 = NULL;
+err_free_ccb0:
+ drm_gem_object_unreference_unlocked(&ved_priv->ccb0->base);
+ ved_priv->ccb0 = NULL;
+ return ret;
+}
+
+static void ved_free_ccb(struct ved_private *ved_priv)
+{
+ if (ved_priv->ccb0) {
+ drm_gem_object_unreference_unlocked(&ved_priv->ccb0->base);
+ ved_priv->ccb0 = NULL;
+ }
+ if (ved_priv->ccb1) {
+ drm_gem_object_unreference_unlocked(&ved_priv->ccb1->base);
+ ved_priv->ccb1 = NULL;
+ }
+}
+
+static void ved_rendec_init_by_reg(struct ved_private *ved_priv)
+{
+ u32 cmd;
+ struct drm_ipvr_private *dev_priv = ved_priv->dev_priv;
+
+ IPVR_REG_WRITE32(ved_priv->base_addr0, MSVDX_RENDEC_BASE_ADDR0_OFFSET);
+ IPVR_REG_WRITE32(ved_priv->base_addr1, MSVDX_RENDEC_BASE_ADDR1_OFFSET);
+
+ cmd = 0;
+ REGIO_WRITE_FIELD(cmd, MSVDX_RENDEC_BUFFER_SIZE,
+ RENDEC_BUFFER_SIZE0, RENDEC_A_SIZE / 4096);
+ REGIO_WRITE_FIELD(cmd, MSVDX_RENDEC_BUFFER_SIZE,
+ RENDEC_BUFFER_SIZE1, RENDEC_B_SIZE / 4096);
+ IPVR_REG_WRITE32(cmd, MSVDX_RENDEC_BUFFER_SIZE_OFFSET);
+
+ cmd = 0;
+ REGIO_WRITE_FIELD(cmd, MSVDX_RENDEC_CONTROL1,
+ RENDEC_DECODE_START_SIZE, 0);
+ REGIO_WRITE_FIELD(cmd, MSVDX_RENDEC_CONTROL1,
+ RENDEC_BURST_SIZE_W, 1);
+ REGIO_WRITE_FIELD(cmd, MSVDX_RENDEC_CONTROL1,
+ RENDEC_BURST_SIZE_R, 1);
+ REGIO_WRITE_FIELD(cmd, MSVDX_RENDEC_CONTROL1,
+ RENDEC_EXTERNAL_MEMORY, 1);
+ IPVR_REG_WRITE32(cmd, MSVDX_RENDEC_CONTROL1_OFFSET);
+
+ cmd = 0x00101010;
+ IPVR_REG_WRITE32(cmd, MSVDX_RENDEC_CONTEXT0_OFFSET);
+ IPVR_REG_WRITE32(cmd, MSVDX_RENDEC_CONTEXT1_OFFSET);
+ IPVR_REG_WRITE32(cmd, MSVDX_RENDEC_CONTEXT2_OFFSET);
+ IPVR_REG_WRITE32(cmd, MSVDX_RENDEC_CONTEXT3_OFFSET);
+ IPVR_REG_WRITE32(cmd, MSVDX_RENDEC_CONTEXT4_OFFSET);
+ IPVR_REG_WRITE32(cmd, MSVDX_RENDEC_CONTEXT5_OFFSET);
+
+ cmd = 0;
+ REGIO_WRITE_FIELD(cmd, MSVDX_RENDEC_CONTROL0, RENDEC_INITIALISE, 1);
+ IPVR_REG_WRITE32(cmd, MSVDX_RENDEC_CONTROL0_OFFSET);
+}
+
+int ved_rendec_init_by_msg(struct ved_private *ved_priv)
+{
+ /* at this stage, FW is uplaoded successfully,
+ * can send RENDEC init message */
+ struct fw_init_msg init_msg;
+ init_msg.header.bits.msg_size = sizeof(struct fw_init_msg);
+ init_msg.header.bits.msg_type = MTX_MSGID_INIT;
+ init_msg.rendec_addr0 = ved_priv->base_addr0;
+ init_msg.rendec_addr1 = ved_priv->base_addr1;
+ init_msg.rendec_size.bits.rendec_size0 = RENDEC_A_SIZE / (4 * 1024);
+ init_msg.rendec_size.bits.rendec_size1 = RENDEC_B_SIZE / (4 * 1024);
+ return ved_mtx_send(ved_priv, (void *)&init_msg);
+}
+
+static void ved_get_mtx_control_from_dash(struct ved_private *ved_priv)
+{
+ int count = 0;
+ u32 reg_val = 0;
+ struct drm_ipvr_private *dev_priv = ved_priv->dev_priv;
+
+ REGIO_WRITE_FIELD(reg_val, MSVDX_MTX_DEBUG, MTX_DBG_IS_SLAVE, 1);
+ REGIO_WRITE_FIELD(reg_val, MSVDX_MTX_DEBUG, MTX_DBG_GPIO_IN, 0x02);
+ IPVR_REG_WRITE32(reg_val, MSVDX_MTX_DEBUG_OFFSET);
+
+ do {
+ reg_val = IPVR_REG_READ32(MSVDX_MTX_DEBUG_OFFSET);
+ count++;
+ } while (((reg_val & 0x18) != 0) && count < 50000);
+
+ if (count >= 50000)
+ IPVR_DEBUG_VED("VED: timeout in get_mtx_control_from_dash.\n");
+
+ /* Save the access control register...*/
+ ved_priv->ved_dash_access_ctrl = IPVR_REG_READ32(MTX_RAM_ACCESS_CONTROL_OFFSET);
+}
+
+static void
+ved_release_mtx_control_from_dash(struct ved_private *ved_priv)
+{
+ struct drm_ipvr_private *dev_priv = ved_priv->dev_priv;
+ /* restore access control */
+ IPVR_REG_WRITE32(ved_priv->ved_dash_access_ctrl, MTX_RAM_ACCESS_CONTROL_OFFSET);
+ /* release bus */
+ IPVR_REG_WRITE32(0x4, MSVDX_MTX_DEBUG_OFFSET);
+}
+
+/* for future debug info of msvdx related registers */
+static void
+ved_setup_fw_dump(struct ved_private *ved_priv, u32 dma_channel)
+{
+ struct drm_ipvr_private *dev_priv = ved_priv->dev_priv;
+ IPVR_DEBUG_REG("dump registers during fw upload for debug:\n");
+ /* for DMAC REGISTER */
+ IPVR_DEBUG_REG("MTX_SYSC_CDMAA is 0x%x\n",
+ IPVR_REG_READ32(MTX_SYSC_CDMAA_OFFSET));
+ IPVR_DEBUG_REG("MTX_SYSC_CDMAC value is 0x%x\n",
+ IPVR_REG_READ32(MTX_SYSC_CDMAC_OFFSET));
+ IPVR_DEBUG_REG("DMAC_SETUP value is 0x%x\n",
+ IPVR_REG_READ32(DMAC_DMAC_SETUP_OFFSET + dma_channel));
+ IPVR_DEBUG_REG("DMAC_DMAC_COUNT value is 0x%x\n",
+ IPVR_REG_READ32(DMAC_DMAC_COUNT_OFFSET + dma_channel));
+ IPVR_DEBUG_REG("DMAC_DMAC_PERIPH_OFFSET value is 0x%x\n",
+ IPVR_REG_READ32(DMAC_DMAC_PERIPH_OFFSET + dma_channel));
+ IPVR_DEBUG_REG("DMAC_DMAC_PERIPHERAL_ADDR value is 0x%x\n",
+ IPVR_REG_READ32(DMAC_DMAC_PERIPHERAL_ADDR_OFFSET +
+ dma_channel));
+ IPVR_DEBUG_REG("MSVDX_CONTROL value is 0x%x\n",
+ IPVR_REG_READ32(MSVDX_CONTROL_OFFSET));
+ IPVR_DEBUG_REG("DMAC_DMAC_IRQ_STAT value is 0x%x\n",
+ IPVR_REG_READ32(DMAC_DMAC_IRQ_STAT_OFFSET));
+ IPVR_DEBUG_REG("MSVDX_MMU_CONTROL0 value is 0x%x\n",
+ IPVR_REG_READ32(MSVDX_MMU_CONTROL0_OFFSET));
+ IPVR_DEBUG_REG("DMAC_DMAC_COUNT 2222 value is 0x%x\n",
+ IPVR_REG_READ32(DMAC_DMAC_COUNT_OFFSET + dma_channel));
+
+ /* for MTX REGISTER */
+ IPVR_DEBUG_REG("MTX_ENABLE_OFFSET is 0x%x\n",
+ IPVR_REG_READ32(MTX_ENABLE_OFFSET));
+ IPVR_DEBUG_REG("MTX_KICK_INPUT_OFFSET value is 0x%x\n",
+ IPVR_REG_READ32(MTX_KICK_INPUT_OFFSET));
+ IPVR_DEBUG_REG("MTX_REG_READ_WRITE_REQUEST_OFFSET value is 0x%x\n",
+ IPVR_REG_READ32(MTX_REGISTER_READ_WRITE_REQUEST_OFFSET));
+ IPVR_DEBUG_REG("MTX_RAM_ACCESS_CONTROL_OFFSET value is 0x%x\n",
+ IPVR_REG_READ32(MTX_RAM_ACCESS_CONTROL_OFFSET));
+ IPVR_DEBUG_REG("MTX_RAM_ACCESS_STATUS_OFFSET value is 0x%x\n",
+ IPVR_REG_READ32(MTX_RAM_ACCESS_STATUS_OFFSET));
+ IPVR_DEBUG_REG("MTX_SYSC_TIMERDIV_OFFSET value is 0x%x\n",
+ IPVR_REG_READ32(MTX_SYSC_TIMERDIV_OFFSET));
+ IPVR_DEBUG_REG("MTX_SYSC_CDMAC_OFFSET value is 0x%x\n",
+ IPVR_REG_READ32(MTX_SYSC_CDMAC_OFFSET));
+ IPVR_DEBUG_REG("MTX_SYSC_CDMAA_OFFSET value is 0x%x\n",
+ IPVR_REG_READ32(MTX_SYSC_CDMAA_OFFSET));
+ IPVR_DEBUG_REG("MTX_SYSC_CDMAS0_OFFSET value is 0x%x\n",
+ IPVR_REG_READ32(MTX_SYSC_CDMAS0_OFFSET));
+ IPVR_DEBUG_REG("MTX_SYSC_CDMAT_OFFSET value is 0x%x\n",
+ IPVR_REG_READ32(MTX_SYSC_CDMAT_OFFSET));
+
+ /* for MSVDX CORE REGISTER */
+ IPVR_DEBUG_REG("MSVDX_CONTROL_OFFSET is 0x%x\n",
+ IPVR_REG_READ32(MSVDX_CONTROL_OFFSET));
+ IPVR_DEBUG_REG("MSVDX_INTERRUPT_CLEAR_OFFSET value is 0x%x\n",
+ IPVR_REG_READ32(MSVDX_INTERRUPT_CLEAR_OFFSET));
+ IPVR_DEBUG_REG("MSVDX_INTERRUPT_STATUS_OFFSET value is 0x%x\n",
+ IPVR_REG_READ32(MSVDX_INTERRUPT_STATUS_OFFSET));
+ IPVR_DEBUG_REG("MMSVDX_HOST_INTERRUPT_ENABLE_OFFSET value is 0x%x\n",
+ IPVR_REG_READ32(MSVDX_HOST_INTERRUPT_ENABLE_OFFSET));
+ IPVR_DEBUG_REG("MSVDX_MAN_CLK_ENABLE_OFFSET value is 0x%x\n",
+ IPVR_REG_READ32(MSVDX_MAN_CLK_ENABLE_OFFSET));
+ IPVR_DEBUG_REG("MSVDX_CORE_ID_OFFSET value is 0x%x\n",
+ IPVR_REG_READ32(MSVDX_CORE_ID_OFFSET));
+ IPVR_DEBUG_REG("MSVDX_MMU_STATUS_OFFSET value is 0x%x\n",
+ IPVR_REG_READ32(MSVDX_MMU_STATUS_OFFSET));
+ IPVR_DEBUG_REG("FE_MSVDX_WDT_CONTROL_OFFSET value is 0x%x\n",
+ IPVR_REG_READ32(FE_MSVDX_WDT_CONTROL_OFFSET));
+ IPVR_DEBUG_REG("FE_MSVDX_WDTIMER_OFFSET value is 0x%x\n",
+ IPVR_REG_READ32(FE_MSVDX_WDTIMER_OFFSET));
+ IPVR_DEBUG_REG("BE_MSVDX_WDT_CONTROL_OFFSET value is 0x%x\n",
+ IPVR_REG_READ32(BE_MSVDX_WDT_CONTROL_OFFSET));
+ IPVR_DEBUG_REG("BE_MSVDX_WDTIMER_OFFSET value is 0x%x\n",
+ IPVR_REG_READ32(BE_MSVDX_WDTIMER_OFFSET));
+
+ /* for MSVDX RENDEC REGISTER */
+ IPVR_DEBUG_REG("VEC_SHIFTREG_CONTROL_OFFSET is 0x%x\n",
+ IPVR_REG_READ32(VEC_SHIFTREG_CONTROL_OFFSET));
+ IPVR_DEBUG_REG("MSVDX_RENDEC_CONTROL0_OFFSET value is 0x%x\n",
+ IPVR_REG_READ32(MSVDX_RENDEC_CONTROL0_OFFSET));
+ IPVR_DEBUG_REG("MSVDX_RENDEC_BUFFER_SIZE_OFFSET value is 0x%x\n",
+ IPVR_REG_READ32(MSVDX_RENDEC_BUFFER_SIZE_OFFSET));
+ IPVR_DEBUG_REG("MSVDX_RENDEC_BASE_ADDR0_OFFSET value is 0x%x\n",
+ IPVR_REG_READ32(MSVDX_RENDEC_BASE_ADDR0_OFFSET));
+ IPVR_DEBUG_REG("MMSVDX_RENDEC_BASE_ADDR1_OFFSET value is 0x%x\n",
+ IPVR_REG_READ32(MSVDX_RENDEC_BASE_ADDR1_OFFSET));
+ IPVR_DEBUG_REG("MSVDX_RENDEC_READ_DATA_OFFSET value is 0x%x\n",
+ IPVR_REG_READ32(MSVDX_RENDEC_READ_DATA_OFFSET));
+ IPVR_DEBUG_REG("MSVDX_RENDEC_CONTEXT0_OFFSET value is 0x%x\n",
+ IPVR_REG_READ32(MSVDX_RENDEC_CONTEXT0_OFFSET));
+ IPVR_DEBUG_REG("MSVDX_RENDEC_CONTEXT1_OFFSET value is 0x%x\n",
+ IPVR_REG_READ32(MSVDX_RENDEC_CONTEXT1_OFFSET));
+ IPVR_DEBUG_REG("MSVDX_CMDS_END_SLICE_PICTURE_OFFSET value is 0x%x\n",
+ IPVR_REG_READ32(MSVDX_CMDS_END_SLICE_PICTURE_OFFSET));
+
+ IPVR_DEBUG_REG("MSVDX_MMU_MEM_REQ value is 0x%x\n",
+ IPVR_REG_READ32(MSVDX_MMU_MEM_REQ_OFFSET));
+ IPVR_DEBUG_REG("MSVDX_SYS_MEMORY_DEBUG2 value is 0x%x\n",
+ IPVR_REG_READ32(0x6fc));
+}
+
+static void ved_upload_fw(struct ved_private *ved_priv,
+ u32 address, const u32 words)
+{
+ u32 reg_val = 0;
+ u32 cmd;
+ u32 uCountReg, offset, mmu_ptd;
+ u32 size = words * 4; /* byte count */
+ u32 dma_channel = 0; /* Setup a Simple DMA for Ch0 */
+ struct drm_ipvr_private *dev_priv = ved_priv->dev_priv;
+
+ IPVR_DEBUG_VED("VED: Upload firmware by DMA.\n");
+ ved_get_mtx_control_from_dash(ved_priv);
+
+ /*
+ * dma transfers to/from the mtx have to be 32-bit aligned and
+ * in multiples of 32 bits
+ */
+ IPVR_REG_WRITE32(address, MTX_SYSC_CDMAA_OFFSET);
+
+ /* burst size in multiples of 64 bits (allowed values are 2 or 4) */
+ REGIO_WRITE_FIELD_LITE(reg_val, MTX_SYSC_CDMAC, BURSTSIZE, 4);
+ /* false means write to mtx mem, true means read from mtx mem */
+ REGIO_WRITE_FIELD_LITE(reg_val, MTX_SYSC_CDMAC, RNW, 0);
+ /* begin transfer */
+ REGIO_WRITE_FIELD_LITE(reg_val, MTX_SYSC_CDMAC, ENABLE, 1);
+ /* specifies transfer size of the DMA operation by 32-bit words */
+ REGIO_WRITE_FIELD_LITE(reg_val, MTX_SYSC_CDMAC, LENGTH, words);
+ IPVR_REG_WRITE32(reg_val, MTX_SYSC_CDMAC_OFFSET);
+
+ /* toggle channel 0 usage between mtx and other msvdx peripherals */
+ {
+ reg_val = IPVR_REG_READ32(MSVDX_CONTROL_OFFSET);
+ REGIO_WRITE_FIELD(reg_val, MSVDX_CONTROL, DMAC_CH0_SELECT, 0);
+ IPVR_REG_WRITE32(reg_val, MSVDX_CONTROL_OFFSET);
+ }
+
+ /* Clear the DMAC Stats */
+ IPVR_REG_WRITE32(0 , DMAC_DMAC_IRQ_STAT_OFFSET + dma_channel);
+
+ offset = ved_priv->fw_offset;
+ IPVR_DEBUG_VED("fw mmu offset is 0x%x.\n", offset);
+
+ /* use bank 0 */
+ cmd = 0;
+ IPVR_REG_WRITE32(cmd, MSVDX_MMU_BANK_INDEX_OFFSET);
+
+ /* Write PTD to mmu base 0*/
+ mmu_ptd = ipvr_get_default_pd_addr32(ved_priv->dev_priv->mmu);
+ BUG_ON(mmu_ptd == 0);
+ IPVR_REG_WRITE32(mmu_ptd, MSVDX_MMU_DIR_LIST_BASE_OFFSET + 0);
+ IPVR_DEBUG_VED("mmu_ptd is %d.\n", mmu_ptd);
+
+ /* Invalidate */
+ reg_val = IPVR_REG_READ32(MSVDX_MMU_CONTROL0_OFFSET);
+ reg_val &= ~0xf;
+ REGIO_WRITE_FIELD(reg_val, MSVDX_MMU_CONTROL0, MMU_INVALDC, 1);
+ IPVR_REG_WRITE32(reg_val, MSVDX_MMU_CONTROL0_OFFSET);
+
+ IPVR_REG_WRITE32(offset, DMAC_DMAC_SETUP_OFFSET + dma_channel);
+
+ /* Only use a single dma - assert that this is valid */
+ if ((size >> 2) >= (1 << 15)) {
+ IPVR_ERROR("DMA size beyond limit, abort firmware upload.\n");
+ return;
+ }
+
+ uCountReg = IPVR_DMAC_VALUE_COUNT(IPVR_DMAC_BSWAP_NO_SWAP, 0,
+ IPVR_DMAC_DIR_MEM_TO_PERIPH, 0, (size >> 2));
+ /* Set the number of bytes to dma*/
+ IPVR_REG_WRITE32(uCountReg, DMAC_DMAC_COUNT_OFFSET + dma_channel);
+
+ cmd = IPVR_DMAC_VALUE_PERIPH_PARAM(IPVR_DMAC_ACC_DEL_0,
+ IPVR_DMAC_INCR_OFF,
+ IPVR_DMAC_BURST_2);
+ IPVR_REG_WRITE32(cmd, DMAC_DMAC_PERIPH_OFFSET + dma_channel);
+
+ /* Set destination port for dma */
+ cmd = 0;
+ REGIO_WRITE_FIELD(cmd, DMAC_DMAC_PERIPHERAL_ADDR, ADDR,
+ MTX_SYSC_CDMAT_OFFSET);
+ IPVR_REG_WRITE32(cmd, DMAC_DMAC_PERIPHERAL_ADDR_OFFSET + dma_channel);
+
+
+ /* Finally, rewrite the count register with the enable bit set */
+ IPVR_REG_WRITE32(uCountReg | DMAC_DMAC_COUNT_EN_MASK,
+ DMAC_DMAC_COUNT_OFFSET + dma_channel);
+
+ /* Wait for all to be done */
+ if (ved_wait_for_register(ved_priv,
+ DMAC_DMAC_IRQ_STAT_OFFSET + dma_channel,
+ DMAC_DMAC_IRQ_STAT_TRANSFER_FIN_MASK,
+ DMAC_DMAC_IRQ_STAT_TRANSFER_FIN_MASK,
+ 2000000, 5)) {
+ ved_setup_fw_dump(ved_priv, dma_channel);
+ ved_release_mtx_control_from_dash(ved_priv);
+ return;
+ }
+
+ /* Assert that the MTX DMA port is all done aswell */
+ if (ved_wait_for_register(ved_priv,
+ MTX_SYSC_CDMAS0_OFFSET,
+ 1, 1, 2000000, 5)) {
+ ved_release_mtx_control_from_dash(ved_priv);
+ return;
+ }
+
+ ved_release_mtx_control_from_dash(ved_priv);
+
+ IPVR_DEBUG_VED("VED: Upload done\n");
+}
+
+static int ved_get_fw_bo(struct ved_private *ved_priv,
+ const struct firmware **raw, char *name)
+{
+ int rc = 0;
+ size_t fw_size;
+ void *ptr = NULL;
+ void *fw_bo_addr = NULL;
+ u32 *last_word;
+ struct ved_fw *fw;
+
+ rc = request_firmware(raw, name, &ved_priv->dev_priv->dev->platformdev->dev);
+ if (rc)
+ return rc;
+
+ if (!*raw) {
+ rc = -ENOMEM;
+ IPVR_ERROR("VED: %s request_firmware failed: Reason %d.\n", name, rc);
+ goto out;
+ }
+
+ if ((*raw)->size < sizeof(struct ved_fw)) {
+ rc = -ENOMEM;
+ IPVR_ERROR("VED: %s is is not correct size(%zd).\n", name, (*raw)->size);
+ goto out;
+ }
+
+ ptr = (void *)((*raw))->data;
+ if (!ptr) {
+ rc = -ENOMEM;
+ IPVR_ERROR("VED: Failed to load %s.\n", name);
+ goto out;
+ }
+
+ /* another sanity check... */
+ fw_size = sizeof(struct ved_fw) +
+ sizeof(u32) * ((struct ved_fw *) ptr)->text_size +
+ sizeof(u32) * ((struct ved_fw *) ptr)->data_size;
+ if ((*raw)->size < fw_size) {
+ rc = -ENOMEM;
+ IPVR_ERROR("VED: %s is is not correct size(%zd).\n",
+ name, (*raw)->size);
+ goto out;
+ }
+
+ fw_bo_addr = ipvr_gem_object_vmap(ved_priv->fw_bo);
+ if (!fw_bo_addr) {
+ rc = -ENOMEM;
+ IPVR_ERROR("VED: kmap failed for fw buffer.\n");
+ goto out;
+ }
+
+ fw = (struct ved_fw *)ptr;
+ memset(fw_bo_addr, UNINITILISE_MEM, ved_priv->mtx_mem_size);
+ memcpy(fw_bo_addr, ptr + sizeof(struct ved_fw),
+ sizeof(u32) * fw->text_size);
+ memcpy(fw_bo_addr + (fw->data_location - MSVDX_MTX_DATA_LOCATION),
+ (void *)ptr + sizeof(struct ved_fw) + sizeof(u32) * fw->text_size,
+ sizeof(u32) * fw->data_size);
+ last_word = (u32 *)(fw_bo_addr + ved_priv->mtx_mem_size - 4);
+ /*
+ * Write a know value to last word in mtx memory
+ * Usefull for detection of stack overrun
+ */
+ *last_word = STACKGUARDWORD;
+
+ vunmap(fw_bo_addr);
+ IPVR_DEBUG_VED("VED: releasing firmware resouces.\n");
+ IPVR_DEBUG_VED("VED: Load firmware into BO successfully.\n");
+out:
+ release_firmware(*raw);
+ return rc;
+}
+
+static u32 *
+ved_get_fw(struct ved_private *ved_priv, const struct firmware **raw, char *name)
+{
+ int rc = 0;
+ size_t fw_size;
+ void *ptr = NULL;
+ struct ved_fw *fw;
+ ved_priv->ved_fw_ptr = NULL;
+
+ rc = request_firmware(raw, name, &ved_priv->dev_priv->dev->platformdev->dev);
+ if (rc)
+ return NULL;
+
+ if (!*raw) {
+ IPVR_ERROR("VED: %s request_firmware failed: Reason %d\n",
+ name, rc);
+ goto out;
+ }
+
+ if ((*raw)->size < sizeof(struct ved_fw)) {
+ IPVR_ERROR("VED: %s is is not correct size(%zd)\n",
+ name, (*raw)->size);
+ goto out;
+ }
+
+ ptr = (int *)((*raw))->data;
+ if (!ptr) {
+ IPVR_ERROR("VED: Failed to load %s.\n", name);
+ goto out;
+ }
+ fw = (struct ved_fw *)ptr;
+
+ /* another sanity check... */
+ fw_size = sizeof(fw) +
+ sizeof(u32) * fw->text_size +
+ sizeof(u32) * fw->data_size;
+ if ((*raw)->size < fw_size) {
+ IPVR_ERROR("VED: %s is is not correct size(%zd).\n",
+ name, (*raw)->size);
+ goto out;
+ }
+
+ ved_priv->ved_fw_ptr = kzalloc(fw_size, GFP_KERNEL);
+ if (!ved_priv->ved_fw_ptr)
+ IPVR_ERROR("VED: allocate FW buffer failed.\n");
+ else {
+ memcpy(ved_priv->ved_fw_ptr, ptr, fw_size);
+ ved_priv->ved_fw_size = fw_size;
+ }
+
+out:
+ IPVR_DEBUG_VED("VED: releasing firmware resouces.\n");
+ release_firmware(*raw);
+ return ved_priv->ved_fw_ptr;
+}
+
+static void
+ved_write_mtx_core_reg(struct ved_private *ved_priv,
+ const u32 core_reg, const u32 val)
+{
+ u32 reg = 0;
+ struct drm_ipvr_private *dev_priv = ved_priv->dev_priv;
+
+ /* Put data in MTX_RW_DATA */
+ IPVR_REG_WRITE32(val, MTX_REGISTER_READ_WRITE_DATA_OFFSET);
+
+ /* DREADY is set to 0 and request a write */
+ reg = core_reg;
+ REGIO_WRITE_FIELD_LITE(reg, MTX_REGISTER_READ_WRITE_REQUEST,
+ MTX_RNW, 0);
+ REGIO_WRITE_FIELD_LITE(reg, MTX_REGISTER_READ_WRITE_REQUEST,
+ MTX_DREADY, 0);
+ IPVR_REG_WRITE32(reg, MTX_REGISTER_READ_WRITE_REQUEST_OFFSET);
+
+ ved_wait_for_register(ved_priv,
+ MTX_REGISTER_READ_WRITE_REQUEST_OFFSET,
+ MTX_REGISTER_READ_WRITE_REQUEST_MTX_DREADY_MASK,
+ MTX_REGISTER_READ_WRITE_REQUEST_MTX_DREADY_MASK,
+ 2000000, 5);
+}
+
+int ved_alloc_fw_bo(struct ved_private *ved_priv)
+{
+ u32 core_rev;
+ int ret;
+ struct drm_ipvr_private *dev_priv = ved_priv->dev_priv;
+
+ core_rev = IPVR_REG_READ32(MSVDX_CORE_REV_OFFSET);
+
+ if ((core_rev & 0xffffff) < 0x020000)
+ ved_priv->mtx_mem_size = 16 * 1024;
+ else
+ ved_priv->mtx_mem_size = 56 * 1024;
+
+ IPVR_DEBUG_INIT("VED: MTX mem size is 0x%08x bytes,"
+ "allocate firmware BO size 0x%08x.\n",
+ ved_priv->mtx_mem_size,
+ ved_priv->mtx_mem_size + 4096);
+
+ /* Allocate the new object */
+ ved_priv->fw_bo = ipvr_gem_create(ved_priv->dev_priv,
+ ved_priv->mtx_mem_size + 4096, 0, 0);
+ if (IS_ERR(ved_priv->fw_bo)) {
+ IPVR_ERROR("VED: failed to allocate fw buffer: %ld.\n",
+ PTR_ERR(ved_priv->fw_bo));
+ ved_priv->fw_bo = NULL;
+ return -ENOMEM;
+ }
+ ved_priv->fw_offset = ipvr_gem_object_mmu_offset(ved_priv->fw_bo);
+ if (IPVR_IS_ERR(ved_priv->fw_offset)) {
+ ved_priv->fw_bo = NULL;
+ goto err_free_fw_bo;
+ }
+ return 0;
+err_free_fw_bo:
+ drm_gem_object_unreference_unlocked(&ved_priv->fw_bo->base);
+ return ret;
+}
+
+int ved_setup_fw(struct ved_private *ved_priv)
+{
+ u32 ram_bank_size;
+ struct drm_ipvr_private *dev_priv = ved_priv->dev_priv;
+ int ret = 0;
+ struct ved_fw *fw;
+ u32 *fw_ptr = NULL;
+ u32 *text_ptr = NULL;
+ u32 *data_ptr = NULL;
+ const struct firmware *raw = NULL;
+
+ /* todo : Assert the clock is on - if not turn it on to upload code */
+ IPVR_DEBUG_VED("VED: ved_setup_fw.\n");
+
+ ved_set_clocks(ved_priv, clk_enable_all);
+
+ /* Reset MTX */
+ IPVR_REG_WRITE32(MTX_SOFT_RESET_MTX_RESET_MASK,
+ MTX_SOFT_RESET_OFFSET);
+
+ IPVR_REG_WRITE32(FIRMWAREID, MSVDX_COMMS_FIRMWARE_ID);
+
+ IPVR_REG_WRITE32(0, MSVDX_COMMS_ERROR_TRIG);
+ IPVR_REG_WRITE32(199, MTX_SYSC_TIMERDIV_OFFSET); /* MTX_SYSC_TIMERDIV */
+ IPVR_REG_WRITE32(0, MSVDX_EXT_FW_ERROR_STATE); /* EXT_FW_ERROR_STATE */
+ IPVR_REG_WRITE32(0, MSVDX_COMMS_MSG_COUNTER);
+ IPVR_REG_WRITE32(0, MSVDX_COMMS_SIGNATURE);
+ IPVR_REG_WRITE32(0, MSVDX_COMMS_TO_HOST_RD_INDEX);
+ IPVR_REG_WRITE32(0, MSVDX_COMMS_TO_HOST_WRT_INDEX);
+ IPVR_REG_WRITE32(0, MSVDX_COMMS_TO_MTX_RD_INDEX);
+ IPVR_REG_WRITE32(0, MSVDX_COMMS_TO_MTX_WRT_INDEX);
+ IPVR_REG_WRITE32(0, MSVDX_COMMS_FW_STATUS);
+ IPVR_REG_WRITE32(DSIABLE_IDLE_GPIO_SIG |
+ DSIABLE_Auto_CLOCK_GATING |
+ RETURN_VDEB_DATA_IN_COMPLETION |
+ NOT_ENABLE_ON_HOST_CONCEALMENT,
+ MSVDX_COMMS_OFFSET_FLAGS);
+ IPVR_REG_WRITE32(0, MSVDX_COMMS_SIGNATURE);
+
+ /* read register bank size */
+ {
+ u32 bank_size, reg;
+ reg = IPVR_REG_READ32(MSVDX_MTX_RAM_BANK_OFFSET);
+ bank_size =
+ REGIO_READ_FIELD(reg, MSVDX_MTX_RAM_BANK,
+ MTX_RAM_BANK_SIZE);
+ ram_bank_size = (u32)(1 << (bank_size + 2));
+ }
+
+ IPVR_DEBUG_VED("VED: RAM bank size = %d bytes\n", ram_bank_size);
+
+ /* if FW already loaded from storage */
+ if (ved_priv->ved_fw_ptr) {
+ fw_ptr = ved_priv->ved_fw_ptr;
+ } else {
+ fw_ptr = ved_get_fw(ved_priv, &raw, FIRMWARE_NAME);
+ IPVR_DEBUG_VED("VED:load msvdx_fw_mfld_DE2.0.bin by udevd\n");
+ }
+ if (!fw_ptr) {
+ IPVR_ERROR("VED:load ved_fw.bin failed,is udevd running?\n");
+ ret = 1;
+ goto out;
+ }
+
+ if (!ved_priv->fw_loaded_to_bo) { /* Load firmware into BO */
+ IPVR_DEBUG_VED("MSVDX:load ved_fw.bin by udevd into BO\n");
+ ret = ved_get_fw_bo(ved_priv, &raw, FIRMWARE_NAME);
+ if (ret) {
+ IPVR_ERROR("VED: failed to call ved_get_fw_bo: %d.\n", ret);
+ goto out;
+ }
+ ved_priv->fw_loaded_to_bo = true;
+ }
+
+ fw = (struct ved_fw *) fw_ptr;
+
+ /* need check fw->ver? */
+ text_ptr = (u32 *)((u8 *) fw_ptr + sizeof(struct ved_fw));
+ data_ptr = text_ptr + fw->text_size;
+
+ /* maybe we can judge fw version according to fw text size */
+
+ IPVR_DEBUG_VED("VED: Retrieved pointers for firmware\n");
+ IPVR_DEBUG_VED("VED: text_size: %d\n", fw->text_size);
+ IPVR_DEBUG_VED("VED: data_size: %d\n", fw->data_size);
+ IPVR_DEBUG_VED("VED: data_location: 0x%x\n", fw->data_location);
+ IPVR_DEBUG_VED("VED: First 4 bytes of text: 0x%x\n", *text_ptr);
+ IPVR_DEBUG_VED("VED: First 4 bytes of data: 0x%x\n", *data_ptr);
+ IPVR_DEBUG_VED("VED: Uploading firmware\n");
+
+ ved_upload_fw(ved_priv, 0, ved_priv->mtx_mem_size / 4);
+
+ /* -- Set starting PC address */
+ ved_write_mtx_core_reg(ved_priv, MTX_PC, PC_START_ADDRESS);
+
+ /* -- Turn on the thread */
+ IPVR_REG_WRITE32(MTX_ENABLE_MTX_ENABLE_MASK, MTX_ENABLE_OFFSET);
+
+ /* Wait for the signature value to be written back */
+ ret = ved_wait_for_register(ved_priv, MSVDX_COMMS_SIGNATURE,
+ MSVDX_COMMS_SIGNATURE_VALUE,
+ 0xffffffff, /* Enabled bits */
+ 2000000, 5);
+ if (ret) {
+ IPVR_ERROR("VED: firmware fails to initialize.\n");
+ goto out;
+ }
+
+ IPVR_DEBUG_VED("VED: MTX Initial indications OK.\n");
+ IPVR_DEBUG_VED("VED: MSVDX_COMMS_AREA_ADDR = %08x.\n",
+ MSVDX_COMMS_AREA_ADDR);
+out:
+ /* no need to put fw bo, we will do it at driver unload */
+ return ret;
+}
+
+
+/* This value is hardcoded in FW */
+#define WDT_CLOCK_DIVIDER 128
+int ved_post_boot_init(struct ved_private *ved_priv)
+{
+ u32 device_node_flags =
+ DSIABLE_IDLE_GPIO_SIG | DSIABLE_Auto_CLOCK_GATING |
+ RETURN_VDEB_DATA_IN_COMPLETION |
+ NOT_ENABLE_ON_HOST_CONCEALMENT;
+ int reg_val = 0;
+
+ /* DDK set fe_wdt_clks as 0x820 and be_wdt_clks as 0x8200 */
+ u32 fe_wdt_clks = 0x334 * WDT_CLOCK_DIVIDER;
+ u32 be_wdt_clks = 0x2008 * WDT_CLOCK_DIVIDER;
+ struct drm_ipvr_private *dev_priv = ved_priv->dev_priv;
+
+ IPVR_REG_WRITE32(FIRMWAREID, MSVDX_COMMS_FIRMWARE_ID);
+ IPVR_REG_WRITE32(device_node_flags, MSVDX_COMMS_OFFSET_FLAGS);
+
+ /* read register bank size */
+ {
+ u32 ram_bank_size;
+ u32 bank_size, reg;
+ reg = IPVR_REG_READ32(MSVDX_MTX_RAM_BANK_OFFSET);
+ bank_size =
+ REGIO_READ_FIELD(reg, MSVDX_MTX_RAM_BANK,
+ MTX_RAM_BANK_SIZE);
+ ram_bank_size = (u32)(1 << (bank_size + 2));
+ IPVR_DEBUG_INIT("VED: RAM bank size = %d bytes\n",
+ ram_bank_size);
+ }
+ /* host end */
+
+ /* DDK setup tiling region here */
+ /* DDK set MMU_CONTROL2 register */
+
+ /* set watchdog timer here */
+ REGIO_WRITE_FIELD(reg_val, FE_MSVDX_WDT_CONTROL,
+ FE_WDT_CNT_CTRL, 0x3);
+ REGIO_WRITE_FIELD(reg_val, FE_MSVDX_WDT_CONTROL,
+ FE_WDT_ENABLE, 0);
+ REGIO_WRITE_FIELD(reg_val, FE_MSVDX_WDT_CONTROL,
+ FE_WDT_ACTION0, 1);
+ REGIO_WRITE_FIELD(reg_val, FE_MSVDX_WDT_CONTROL,
+ FE_WDT_CLEAR_SELECT, 1);
+ REGIO_WRITE_FIELD(reg_val, FE_MSVDX_WDT_CONTROL,
+ FE_WDT_CLKDIV_SELECT, 7);
+ IPVR_REG_WRITE32(fe_wdt_clks / WDT_CLOCK_DIVIDER,
+ FE_MSVDX_WDT_COMPAREMATCH_OFFSET);
+ IPVR_REG_WRITE32(reg_val, FE_MSVDX_WDT_CONTROL_OFFSET);
+
+ reg_val = 0;
+ /* DDK set BE_WDT_CNT_CTRL as 0x5 and BE_WDT_CLEAR_SELECT as 0x1 */
+ REGIO_WRITE_FIELD(reg_val, BE_MSVDX_WDT_CONTROL,
+ BE_WDT_CNT_CTRL, 0x7);
+ REGIO_WRITE_FIELD(reg_val, BE_MSVDX_WDT_CONTROL,
+ BE_WDT_ENABLE, 0);
+ REGIO_WRITE_FIELD(reg_val, BE_MSVDX_WDT_CONTROL,
+ BE_WDT_ACTION0, 1);
+ REGIO_WRITE_FIELD(reg_val, BE_MSVDX_WDT_CONTROL,
+ BE_WDT_CLEAR_SELECT, 0xd);
+ REGIO_WRITE_FIELD(reg_val, BE_MSVDX_WDT_CONTROL,
+ BE_WDT_CLKDIV_SELECT, 7);
+
+ IPVR_REG_WRITE32(be_wdt_clks / WDT_CLOCK_DIVIDER,
+ BE_MSVDX_WDT_COMPAREMATCH_OFFSET);
+ IPVR_REG_WRITE32(reg_val, BE_MSVDX_WDT_CONTROL_OFFSET);
+
+ return ved_rendec_init_by_msg(ved_priv);
+}
+
+int ved_post_init(struct ved_private *ved_priv)
+{
+ u32 cmd;
+ int ret;
+ struct drm_ipvr_private *dev_priv;
+
+ if (!ved_priv)
+ return -EINVAL;
+
+ ved_priv->ved_busy = false;
+ dev_priv = ved_priv->dev_priv;
+
+ /* Enable MMU by removing all bypass bits */
+ IPVR_REG_WRITE32(0, MSVDX_MMU_CONTROL0_OFFSET);
+
+ ved_rendec_init_by_reg(ved_priv);
+ if (!ved_priv->fw_bo) {
+ ret = ved_alloc_fw_bo(ved_priv);
+ if (ret) {
+ IPVR_ERROR("VED: ved_alloc_fw_bo failed: %d.\n", ret);
+ return ret;
+ }
+ }
+ /* move fw loading to the place receiving first cmd buffer */
+ ved_priv->ved_fw_loaded = false; /* need to load firware */
+ /* it should be set at punit post boot init phase */
+ IPVR_REG_WRITE32(820, FE_MSVDX_WDT_COMPAREMATCH_OFFSET);
+ IPVR_REG_WRITE32(8200, BE_MSVDX_WDT_COMPAREMATCH_OFFSET);
+
+ IPVR_REG_WRITE32(820, FE_MSVDX_WDT_COMPAREMATCH_OFFSET);
+ IPVR_REG_WRITE32(8200, BE_MSVDX_WDT_COMPAREMATCH_OFFSET);
+
+ ved_clear_irq(ved_priv);
+ ved_enable_irq(ved_priv);
+
+ cmd = 0;
+ cmd = IPVR_REG_READ32(VEC_SHIFTREG_CONTROL_OFFSET);
+ REGIO_WRITE_FIELD(cmd, VEC_SHIFTREG_CONTROL,
+ SR_MASTER_SELECT, 1); /* Host */
+ IPVR_REG_WRITE32(cmd, VEC_SHIFTREG_CONTROL_OFFSET);
+
+ return 0;
+}
+
+int __must_check ved_core_init(struct drm_ipvr_private *dev_priv)
+{
+ int ret;
+ struct ved_private *ved_priv;
+ if (!dev_priv->ved_private) {
+ ved_priv = kzalloc(sizeof(struct ved_private), GFP_KERNEL);
+ if (!ved_priv) {
+ IPVR_ERROR("VED: alloc ved_private failed.\n");
+ return -ENOMEM;
+ }
+
+ dev_priv->ved_private = ved_priv;
+ ved_priv->dev_priv = dev_priv;
+
+ /* Initialize comand ved queueing */
+ INIT_LIST_HEAD(&ved_priv->ved_queue);
+ mutex_init(&ved_priv->ved_mutex);
+ spin_lock_init(&ved_priv->ved_lock);
+ ved_priv->mmu_recover_page = alloc_page(GFP_DMA32);
+ if (!ved_priv->mmu_recover_page) {
+ ret = -ENOMEM;
+ IPVR_ERROR("VED: alloc mmu_recover_page failed: %d.\n", ret);
+ goto err_free_ved_priv;
+ }
+ IPVR_DEBUG_INIT("VED: successfully initialized ved_private.\n");
+ dev_priv->ved_private= ved_priv;
+ }
+ ved_priv = dev_priv->ved_private;
+
+ ret = ved_alloc_ccb_for_rendec(dev_priv->ved_private,
+ RENDEC_A_SIZE, RENDEC_B_SIZE);
+ if (unlikely(ret)) {
+ IPVR_ERROR("VED: msvdx_alloc_ccb_for_rendec failed: %d.\n", ret);
+ goto err_free_mmu_recover_page;
+ }
+
+ ret = ved_post_init(ved_priv);
+ if (unlikely(ret)) {
+ IPVR_ERROR("VED: ved_post_init failed: %d.\n", ret);
+ goto err_free_ccb;
+ }
+
+ return 0;
+err_free_ccb:
+ ved_free_ccb(ved_priv);
+err_free_mmu_recover_page:
+ __free_page(ved_priv->mmu_recover_page);
+err_free_ved_priv:
+ kfree(ved_priv);
+ dev_priv->ved_private = NULL;
+ return ret;
+}
+
+int ved_core_deinit(struct drm_ipvr_private *dev_priv)
+{
+ struct ved_private *ved_priv = dev_priv->ved_private;
+ if (NULL == ved_priv) {
+ IPVR_ERROR("VED: ved_priv is NULL!\n");
+ return -1;
+ }
+
+ IPVR_DEBUG_INIT("VED: set the VED clock to 0.\n");
+ ved_set_clocks(ved_priv, 0);
+
+ if (ved_priv->ccb0 || ved_priv->ccb1)
+ ved_free_ccb(ved_priv);
+
+ if (ved_priv->fw_bo) {
+ drm_gem_object_unreference_unlocked(&ved_priv->fw_bo->base);
+ ved_priv->fw_bo = NULL;
+ }
+
+ if (ved_priv->ved_fw_ptr)
+ kfree(ved_priv->ved_fw_ptr);
+
+ if (ved_priv->mmu_recover_page)
+ __free_page(ved_priv->mmu_recover_page);
+
+ kfree(ved_priv);
+ dev_priv->ved_private = NULL;
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/ipvr/ved_fw.h b/drivers/gpu/drm/ipvr/ved_fw.h
new file mode 100644
index 0000000..3ced466
--- /dev/null
+++ b/drivers/gpu/drm/ipvr/ved_fw.h
@@ -0,0 +1,81 @@
+/**************************************************************************
+ * ved_fw.h: VED firmware support header file
+ *
+ * Copyright (c) 2014 Intel Corporation, Hillsboro, OR, USA
+ * Copyright (c) Imagination Technologies Limited, UK
+ * Copyright (c) 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Authors:
+ * Fei Jiang <fei.jiang at intel.com>
+ * Yao Cheng <yao.cheng at intel.com>
+ *
+ **************************************************************************/
+
+
+#ifndef _VED_FW_H_
+#define _VED_FW_H_
+
+#include "ipvr_drv.h"
+
+#define FIRMWAREID 0x014d42ab
+
+/* Non-Optimal Invalidation is not default */
+#define MSVDX_DEVICE_NODE_FLAGS_MMU_NONOPT_INV 2
+
+#define FW_VA_RENDER_HOST_INT 0x00004000
+#define MSVDX_DEVICE_NODE_FLAGS_MMU_HW_INVALIDATION 0x00000020
+#define FW_DEVA_ERROR_DETECTED 0x08000000
+
+/* There is no work currently underway on the hardware */
+#define MSVDX_FW_STATUS_HW_IDLE 0x00000001
+#define MSVDX_DEVICE_NODE_FLAG_BRN23154_BLOCK_ON_FE 0x00000200
+#define MSVDX_DEVICE_NODE_FLAGS_DEFAULT_D0 \
+ (MSVDX_DEVICE_NODE_FLAGS_MMU_NONOPT_INV | \
+ MSVDX_DEVICE_NODE_FLAGS_MMU_HW_INVALIDATION | \
+ MSVDX_DEVICE_NODE_FLAG_BRN23154_BLOCK_ON_FE)
+
+#define MSVDX_DEVICE_NODE_FLAGS_DEFAULT_D1 \
+ (MSVDX_DEVICE_NODE_FLAGS_MMU_HW_INVALIDATION | \
+ MSVDX_DEVICE_NODE_FLAG_BRN23154_BLOCK_ON_FE)
+
+#define MTX_CODE_BASE (0x80900000)
+#define MTX_DATA_BASE (0x82880000)
+#define PC_START_ADDRESS (0x80900000)
+
+#define MTX_CORE_CODE_MEM (0x10)
+#define MTX_CORE_DATA_MEM (0x18)
+
+#define RENDEC_A_SIZE (4 * 1024 * 1024)
+#define RENDEC_B_SIZE (1024 * 1024)
+
+#define TERMINATION_SIZE 48
+
+#define MSVDX_RESET_NEEDS_REUPLOAD_FW (0x2)
+#define MSVDX_RESET_NEEDS_INIT_FW (0x1)
+
+/* init/deinit all ved_private related */
+int __must_check ved_core_init(struct drm_ipvr_private *dev_priv);
+int ved_core_deinit(struct drm_ipvr_private *dev_priv);
+
+/* used for resetting VED after power saving */
+int ved_setup_fw(struct ved_private *ved_priv);
+int ved_core_reset(struct ved_private *ved_priv);
+int ved_wait_for_register(struct ved_private *ved_priv,
+ u32 offset, u32 value, u32 enable,
+ u32 poll_cnt, u32 timeout);
+
+#endif
diff --git a/drivers/gpu/drm/ipvr/ved_msg.h b/drivers/gpu/drm/ipvr/ved_msg.h
new file mode 100644
index 0000000..1a1e89d
--- /dev/null
+++ b/drivers/gpu/drm/ipvr/ved_msg.h
@@ -0,0 +1,256 @@
+/**************************************************************************
+ * ved_msg.h: VED message definition
+ *
+ * Copyright (c) 2014 Intel Corporation, Hillsboro, OR, USA
+ * Copyright (c) 2003 Imagination Technologies Limited, UK
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Authors:
+ * Fei Jiang <fei.jiang at intel.com>
+ * Li Zeng <li.zeng at intel.com>
+ * Yao Cheng <yao.cheng at intel.com>
+ *
+ **************************************************************************/
+
+#ifndef _VED_MSG_H_
+#define _VED_MSG_H_
+
+/* Start of parser specific Host->MTX messages. */
+#define FWRK_MSGID_START_PSR_HOSTMTX_MSG (0x80)
+
+/* Start of parser specific MTX->Host messages. */
+#define FWRK_MSGID_START_PSR_MTXHOST_MSG (0xC0)
+
+/* Host defined msg, just for host use, MTX not recgnize */
+#define FWRK_MSGID_HOST_EMULATED (0x40)
+
+/* This type defines the framework specified message ids */
+enum {
+ /* ! Sent by the VA driver on the host to the mtx firmware.
+ */
+ MTX_MSGID_PADDING = 0,
+ MTX_MSGID_INIT = FWRK_MSGID_START_PSR_HOSTMTX_MSG,
+ MTX_MSGID_DECODE_FE,
+ MTX_MSGID_DEBLOCK,
+ MTX_MSGID_INTRA_OOLD,
+ MTX_MSGID_DECODE_BE,
+ MTX_MSGID_HOST_BE_OPP,
+
+ /*! Sent by the mtx firmware to itself.
+ */
+ MTX_MSGID_RENDER_MC_INTERRUPT,
+
+ /* used to ditinguish mrst and mfld */
+ MTX_MSGID_DEBLOCK_MFLD = FWRK_MSGID_HOST_EMULATED,
+ MTX_MSGID_INTRA_OOLD_MFLD,
+ MTX_MSGID_DECODE_BE_MFLD,
+ MTX_MSGID_HOST_BE_OPP_MFLD,
+
+ /*! Sent by the DXVA firmware on the MTX to the host.
+ */
+ MTX_MSGID_COMPLETED = FWRK_MSGID_START_PSR_MTXHOST_MSG,
+ MTX_MSGID_COMPLETED_BATCH,
+ MTX_MSGID_DEBLOCK_REQUIRED,
+ MTX_MSGID_TEST_RESPONCE,
+ MTX_MSGID_ACK,
+ MTX_MSGID_FAILED,
+ MTX_MSGID_CONTIGUITY_WARNING,
+ MTX_MSGID_HW_PANIC,
+};
+
+#define MTX_GENMSG_SIZE_TYPE u8
+#define MTX_GENMSG_SIZE_MASK (0xFF)
+#define MTX_GENMSG_SIZE_SHIFT (0)
+#define MTX_GENMSG_SIZE_OFFSET (0x0000)
+
+#define MTX_GENMSG_ID_TYPE u8
+#define MTX_GENMSG_ID_MASK (0xFF)
+#define MTX_GENMSG_ID_SHIFT (0)
+#define MTX_GENMSG_ID_OFFSET (0x0001)
+
+#define MTX_GENMSG_HEADER_SIZE 2
+
+#define MTX_GENMSG_FENCE_TYPE u16
+#define MTX_GENMSG_FENCE_MASK (0xFFFF)
+#define MTX_GENMSG_FENCE_OFFSET (0x0002)
+#define MTX_GENMSG_FENCE_SHIFT (0)
+
+#define FW_INVALIDATE_MMU (0x0010)
+
+union msg_header {
+ struct {
+ u32 msg_size:8;
+ u32 msg_type:8;
+ u32 msg_fence:16;
+ } bits;
+ u32 value;
+};
+
+struct fw_init_msg {
+ union {
+ struct {
+ u32 msg_size:8;
+ u32 msg_type:8;
+ u32 reserved:16;
+ } bits;
+ u32 value;
+ } header;
+ u32 rendec_addr0;
+ u32 rendec_addr1;
+ union {
+ struct {
+ u32 rendec_size0:16;
+ u32 rendec_size1:16;
+ } bits;
+ u32 value;
+ } rendec_size;
+};
+
+struct fw_decode_msg {
+ union {
+ struct {
+ u32 msg_size:8;
+ u32 msg_type:8;
+ u32 msg_fence:16;
+ } bits;
+ u32 value;
+ } header;
+ union {
+ struct {
+ u32 flags:16;
+ u32 buffer_size:16;
+ } bits;
+ u32 value;
+ } flag_size;
+ u32 crtl_alloc_addr;
+ union {
+ struct {
+ u32 context:8;
+ u32 mmu_ptd:24;
+ } bits;
+ u32 value;
+ } mmu_context;
+ u32 operating_mode;
+};
+
+struct fw_deblock_msg {
+ union {
+ struct {
+ u32 msg_size:8;
+ u32 msg_type:8;
+ u32 msg_fence:16;
+ } bits;
+ u32 value;
+ } header;
+ union {
+ struct {
+ u32 flags:16;
+ u32 slice_field_type:2;
+ u32 reserved:14;
+ } bits;
+ u32 value;
+ } flag_type;
+ u32 operating_mode;
+ union {
+ struct {
+ u32 context:8;
+ u32 mmu_ptd:24;
+ } bits;
+ u32 value;
+ } mmu_context;
+ union {
+ struct {
+ u32 frame_height_mb:16;
+ u32 pic_width_mb:16;
+ } bits;
+ u32 value;
+ } pic_size;
+ u32 address_a0;
+ u32 address_a1;
+ u32 mb_param_address;
+ u32 ext_stride_a;
+ u32 address_b0;
+ u32 address_b1;
+ u32 alt_output_flags_b;
+ /* additional msg outside of IMG msg */
+ u32 address_c0;
+ u32 address_c1;
+};
+
+#define MTX_PADMSG_SIZE 2
+struct fw_padding_msg {
+ union {
+ struct {
+ u32 msg_size:8;
+ u32 msg_type:8;
+ } bits;
+ u16 value;
+ } header;
+};
+
+struct fw_msg_header {
+ union {
+ struct {
+ u32 msg_size:8;
+ u32 msg_type:8;
+ u32 msg_fence:16;
+ } bits;
+ u32 value;
+ } header;
+};
+
+struct fw_completed_msg {
+ union {
+ struct {
+ u32 msg_size:8;
+ u32 msg_type:8;
+ u32 msg_fence:16;
+ } bits;
+ u32 value;
+ } header;
+ union {
+ struct {
+ u32 start_mb:16;
+ u32 last_mb:16;
+ } bits;
+ u32 value;
+ } mb;
+ u32 flags;
+ u32 vdebcr;
+};
+
+struct fw_panic_msg {
+ union {
+ struct {
+ u32 msg_size:8;
+ u32 msg_type:8;
+ u32 msg_fence:16;
+ } bits;
+ u32 value;
+ } header;
+ u32 fe_status;
+ u32 be_status;
+ union {
+ struct {
+ u32 last_mb:16;
+ u32 reserved2:16;
+ } bits;
+ u32 value;
+ } mb;
+};
+
+
+#endif
diff --git a/drivers/gpu/drm/ipvr/ved_pm.c b/drivers/gpu/drm/ipvr/ved_pm.c
new file mode 100644
index 0000000..7402f56
--- /dev/null
+++ b/drivers/gpu/drm/ipvr/ved_pm.c
@@ -0,0 +1,332 @@
+/**************************************************************************
+ * ved_pm.c: VED power management support
+ *
+ * Copyright (c) 2014 Intel Corporation, Hillsboro, OR, USA
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Authors:
+ * Yao Cheng <yao.cheng at intel.com>
+ *
+ **************************************************************************/
+
+
+#include "ipvr_trace.h"
+#include "ved_pm.h"
+#include "ved_reg.h"
+#include "ved_cmd.h"
+#include "ved_fw.h"
+#ifdef CONFIG_INTEL_SOC_PMC
+#include <linux/intel_mid_pm.h>
+#endif
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+
+extern int drm_ipvr_freq;
+
+#define PCI_ROOT_MSGBUS_CTRL_REG 0xD0
+#define PCI_ROOT_MSGBUS_DATA_REG 0xD4
+#define PCI_ROOT_MSGBUS_CTRL_EXT_REG 0xD8
+#define PCI_ROOT_MSGBUS_READ 0x10
+#define PCI_ROOT_MSGBUS_WRITE 0x11
+#define PCI_ROOT_MSGBUS_DWORD_ENABLE 0xf0
+
+/* VED power state set/get */
+#define PUNIT_PORT 0x04
+#define VEDSSPM0 0x32
+#define VEDSSPM1 0x33
+#define VEDSSC 0x1
+
+/* VED frequency set/get */
+#define IP_FREQ_VALID 0x80 /* Freq is valid bit */
+
+#define IP_FREQ_SIZE 5 /* number of bits in freq fields */
+#define IP_FREQ_MASK 0x1f /* Bit mask for freq field */
+
+/* Positions of various frequency fields */
+#define IP_FREQ_POS 0 /* Freq control [4:0] */
+#define IP_FREQ_GUAR_POS 8 /* Freq guar [12:8] */
+#define IP_FREQ_STAT_POS 24 /* Freq status [28:24] */
+
+enum APM_VED_STATUS {
+ VED_APM_STS_D0 = 0,
+ VED_APM_STS_D1,
+ VED_APM_STS_D2,
+ VED_APM_STS_D3
+};
+
+#define GET_FREQ_NUMBER(freq_code) ((1600 * 2)/((freq_code) + 1))
+/* valid freq code: 0x9, 0xb, 0xd, 0xf, 0x11, 0x13 */
+#define FREQ_CODE_CLAMP(code) ((code < 0x9)? 0x9: ((code > 0x13)? 0x13: code))
+#define GET_FREQ_CODE(freq_num) FREQ_CODE_CLAMP((((1600 * 2/freq_num + 1) >> 1) << 1) - 1)
+
+#ifdef CONFIG_INTEL_SOC_PMC
+extern int pmc_nc_set_power_state(int islands, int state_type, int reg);
+extern int pmc_nc_get_power_state(int islands, int reg);
+#endif
+
+static int ved_save_context(struct ved_private *ved_priv)
+{
+ int offset;
+ int ret;
+ struct drm_ipvr_private *dev_priv = ved_priv->dev_priv;
+
+ ved_priv->ved_needs_reset = 1;
+ /* Reset MTX */
+ IPVR_REG_WRITE32(MTX_SOFT_RESET_MTXRESET, MTX_SOFT_RESET_OFFSET);
+
+ /* why need reset msvdx before power off it, need check IMG */
+ ret = ved_core_reset(ved_priv);
+ if (unlikely(ret))
+ IPVR_DEBUG_WARN("failed to call ved_core_reset: %d\n", ret);
+
+ /* Initialize VEC Local RAM */
+ for (offset = 0; offset < VEC_LOCAL_MEM_BYTE_SIZE / 4; ++offset)
+ IPVR_REG_WRITE32(0, VEC_LOCAL_MEM_OFFSET + offset * 4);
+
+ return 0;
+}
+
+static u32 ipvr_msgbus_read32(struct pci_dev *pci_root, u8 port, u32 addr)
+{
+ u32 data;
+ u32 cmd;
+ u32 cmdext;
+
+ cmd = (PCI_ROOT_MSGBUS_READ << 24) | (port << 16) |
+ ((addr & 0xff) << 8) | PCI_ROOT_MSGBUS_DWORD_ENABLE;
+ cmdext = addr & 0xffffff00;
+
+ if (cmdext) {
+ /* This resets to 0 automatically, no need to write 0 */
+ pci_write_config_dword(pci_root, PCI_ROOT_MSGBUS_CTRL_EXT_REG,
+ cmdext);
+ }
+
+ pci_write_config_dword(pci_root, PCI_ROOT_MSGBUS_CTRL_REG, cmd);
+ pci_read_config_dword(pci_root, PCI_ROOT_MSGBUS_DATA_REG, &data);
+
+ return data;
+}
+
+static void ipvr_msgbus_write32(struct pci_dev *pci_root, u8 port, u32 addr, u32 data)
+{
+ u32 cmd;
+ u32 cmdext;
+
+ cmd = (PCI_ROOT_MSGBUS_WRITE << 24) | (port << 16) |
+ ((addr & 0xFF) << 8) | PCI_ROOT_MSGBUS_DWORD_ENABLE;
+ cmdext = addr & 0xffffff00;
+
+ pci_write_config_dword(pci_root, PCI_ROOT_MSGBUS_DATA_REG, data);
+
+ if (cmdext) {
+ /* This resets to 0 automatically, no need to write 0 */
+ pci_write_config_dword(pci_root, PCI_ROOT_MSGBUS_CTRL_EXT_REG,
+ cmdext);
+ }
+
+ pci_write_config_dword(pci_root, PCI_ROOT_MSGBUS_CTRL_REG, cmd);
+}
+
+static int ipvr_pm_cmd_freq_wait(struct pci_dev *pci_root, u32 reg_freq, u32 *freq_code_rlzd)
+{
+ int tcount;
+ u32 freq_val;
+
+ for (tcount = 0; ; tcount++) {
+ freq_val = ipvr_msgbus_read32(pci_root, PUNIT_PORT, reg_freq);
+ if ((freq_val & IP_FREQ_VALID) == 0)
+ break;
+ if (tcount > 500) {
+ IPVR_ERROR("P-Unit freq request wait timeout %x",
+ freq_val);
+ return -EBUSY;
+ }
+ udelay(1);
+ }
+
+ if (freq_code_rlzd) {
+ *freq_code_rlzd = ((freq_val >> IP_FREQ_STAT_POS) &
+ IP_FREQ_MASK);
+ }
+
+ return 0;
+}
+
+static int ipvr_pm_cmd_freq_get(struct pci_dev *pci_root, u32 reg_freq)
+{
+ u32 freq_val;
+ int freq_code = 0;
+
+ ipvr_pm_cmd_freq_wait(pci_root, reg_freq, NULL);
+
+ freq_val = ipvr_msgbus_read32(pci_root, PUNIT_PORT, reg_freq);
+ freq_code =(int)((freq_val>>IP_FREQ_STAT_POS) & ~IP_FREQ_VALID);
+ return freq_code;
+}
+
+static int ipvr_pm_cmd_freq_set(struct pci_dev *pci_root, u32 reg_freq, u32 freq_code, u32 *p_freq_code_rlzd)
+{
+ u32 freq_val;
+ u32 freq_code_realized;
+ int rva;
+
+ rva = ipvr_pm_cmd_freq_wait(pci_root, reg_freq, NULL);
+ if (rva < 0) {
+ IPVR_ERROR("pm_cmd_freq_wait 1 failed: %d\n", rva);
+ return rva;
+ }
+
+ freq_val = IP_FREQ_VALID | freq_code;
+ ipvr_msgbus_write32(pci_root, PUNIT_PORT, reg_freq, freq_val);
+
+ rva = ipvr_pm_cmd_freq_wait(pci_root, reg_freq, &freq_code_realized);
+ if (rva < 0) {
+ IPVR_ERROR("pm_cmd_freq_wait 2 failed: %d\n", rva);
+ return rva;
+ }
+
+ if (p_freq_code_rlzd)
+ *p_freq_code_rlzd = freq_code_realized;
+
+ return rva;
+}
+
+static int ved_set_freq(struct drm_ipvr_private *dev_priv, u32 freq_code)
+{
+ u32 freq_code_rlzd = 0;
+ int ret;
+
+ ret = ipvr_pm_cmd_freq_set(dev_priv->pci_root,
+ VEDSSPM1, freq_code, &freq_code_rlzd);
+ if (ret < 0) {
+ IPVR_ERROR("failed to set freqency, current is %x\n",
+ freq_code_rlzd);
+ }
+
+ return ret;
+}
+
+static int ved_get_freq(struct drm_ipvr_private *dev_priv)
+{
+ return ipvr_pm_cmd_freq_get(dev_priv->pci_root, VEDSSPM1);
+}
+
+#ifdef CONFIG_INTEL_SOC_PMC
+static inline bool do_power_on(struct drm_ipvr_private *dev_priv)
+{
+ if (pmc_nc_set_power_state(VEDSSC, 0, VEDSSPM0)) {
+ IPVR_ERROR("VED: pmu_nc_set_power_state ON fail!\n");
+ return false;
+ }
+ return true;
+}
+static inline bool do_power_off(struct drm_ipvr_private *dev_priv)
+{
+ if (pmc_nc_set_power_state(VEDSSC, 1, VEDSSPM0)) {
+ IPVR_ERROR("VED: pmu_nc_set_power_state OFF fail!\n");
+ return false;
+ }
+ return true;
+}
+#else
+static inline bool do_power_on(struct drm_ipvr_private *dev_priv)
+{
+ u32 pwr_sts;
+ do {
+ ipvr_msgbus_write32(dev_priv->pci_root, PUNIT_PORT, VEDSSPM0, VED_APM_STS_D0);
+ udelay(10);
+ pwr_sts = ipvr_msgbus_read32(dev_priv->pci_root, PUNIT_PORT, VEDSSPM0);
+ } while (pwr_sts != 0x0);
+ do {
+ ipvr_msgbus_write32(dev_priv->pci_root, PUNIT_PORT, VEDSSPM0, VED_APM_STS_D3);
+ udelay(10);
+ pwr_sts = ipvr_msgbus_read32(dev_priv->pci_root, PUNIT_PORT, VEDSSPM0);
+ } while (pwr_sts != 0x03000003);
+ do {
+ ipvr_msgbus_write32(dev_priv->pci_root, PUNIT_PORT, VEDSSPM0, VED_APM_STS_D0);
+ udelay(10);
+ pwr_sts = ipvr_msgbus_read32(dev_priv->pci_root, PUNIT_PORT, VEDSSPM0);
+ } while (pwr_sts != 0x0);
+ return true;
+}
+static inline bool do_power_off(struct drm_ipvr_private *dev_priv)
+{
+ u32 pwr_sts;
+ do {
+ ipvr_msgbus_write32(dev_priv->pci_root, PUNIT_PORT, VEDSSPM0, VED_APM_STS_D3);
+ udelay(10);
+ pwr_sts = ipvr_msgbus_read32(dev_priv->pci_root, PUNIT_PORT, VEDSSPM0);
+ } while (pwr_sts != 0x03000003);
+ return true;
+}
+#endif
+
+bool ved_power_on(struct drm_ipvr_private *dev_priv)
+{
+ int ved_freq_code_before, ved_freq_code_requested, ved_freq_code_after;
+ IPVR_DEBUG_PM("VED: power on msvdx.\n");
+
+ if (!do_power_on(dev_priv))
+ return false;
+
+ ved_freq_code_before = ved_get_freq(dev_priv);
+ ved_freq_code_requested = GET_FREQ_CODE(drm_ipvr_freq);
+ if (ved_set_freq(dev_priv, ved_freq_code_requested)) {
+ IPVR_ERROR("Failed to set VED frequency\n");
+ }
+
+ ved_freq_code_after = ved_get_freq(dev_priv);
+ IPVR_DEBUG_PM("VED freqency requested %dMHz: actual %dMHz => %dMHz\n",
+ drm_ipvr_freq, GET_FREQ_NUMBER(ved_freq_code_before),
+ GET_FREQ_NUMBER(ved_freq_code_after));
+ drm_ipvr_freq = GET_FREQ_NUMBER(ved_freq_code_after);
+
+ trace_ved_power_on(drm_ipvr_freq);
+ return true;
+}
+
+bool ved_power_off(struct drm_ipvr_private *dev_priv)
+{
+ int ved_freq_code;
+ int ret;
+ IPVR_DEBUG_PM("VED: power off msvdx.\n");
+
+ if (dev_priv->ved_private) {
+ ret = ved_save_context(dev_priv->ved_private);
+ if (unlikely(ret)) {
+ IPVR_ERROR("Failed to save VED context: %d, stop powering off\n", ret);
+ return false;
+ }
+ }
+
+ ved_freq_code = ved_get_freq(dev_priv);
+ drm_ipvr_freq = GET_FREQ_NUMBER(ved_freq_code);
+ IPVR_DEBUG_PM("VED freqency: code %d (%dMHz)\n", ved_freq_code, drm_ipvr_freq);
+
+ if (!do_power_off(dev_priv))
+ return false;
+
+ trace_ved_power_off(drm_ipvr_freq);
+ return true;
+}
+
+bool is_ved_on(struct drm_ipvr_private *dev_priv)
+{
+ u32 pwr_sts;
+ pwr_sts = ipvr_msgbus_read32(dev_priv->pci_root, PUNIT_PORT, VEDSSPM0);
+ return (pwr_sts == VED_APM_STS_D0);
+}
diff --git a/drivers/gpu/drm/ipvr/ved_pm.h b/drivers/gpu/drm/ipvr/ved_pm.h
new file mode 100644
index 0000000..4ed1485
--- /dev/null
+++ b/drivers/gpu/drm/ipvr/ved_pm.h
@@ -0,0 +1,36 @@
+/**************************************************************************
+ * ved_pm.h: VED power management header file
+ *
+ * Copyright (c) 2014 Intel Corporation, Hillsboro, OR, USA
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Authors:
+ * Yao Cheng <yao.cheng at intel.com>
+ *
+ **************************************************************************/
+
+#ifndef _VED_PM_H_
+#define _VED_PM_H_
+
+#include "ipvr_drv.h"
+
+bool is_ved_on(struct drm_ipvr_private *dev_priv);
+
+bool __must_check ved_power_on(struct drm_ipvr_private *dev_priv);
+
+bool ved_power_off(struct drm_ipvr_private *dev_priv);
+
+#endif
diff --git a/drivers/gpu/drm/ipvr/ved_reg.h b/drivers/gpu/drm/ipvr/ved_reg.h
new file mode 100644
index 0000000..b7c69cf
--- /dev/null
+++ b/drivers/gpu/drm/ipvr/ved_reg.h
@@ -0,0 +1,561 @@
+/**************************************************************************
+ * ved_reg.h: VED register definition
+ *
+ * Copyright (c) 2014 Intel Corporation, Hillsboro, OR, USA
+ * Copyright (c) Imagination Technologies Limited, UK
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Authors:
+ * Fei Jiang <fei.jiang at intel.com>
+ * Yao Cheng <yao.cheng at intel.com>
+ *
+ **************************************************************************/
+
+#ifndef _VED_REG_H_
+#define _VED_REG_H_
+
+#include "ipvr_drv.h"
+
+#define REGISTER(__group__, __reg__) (__group__##_##__reg__##_OFFSET)
+
+#define MTX_INTERNAL_REG(R_SPECIFIER , U_SPECIFIER) \
+ (((R_SPECIFIER)<<4) | (U_SPECIFIER))
+#define MTX_PC MTX_INTERNAL_REG(0, 5)
+
+#define MEMIO_READ_FIELD(vpMem, field) \
+ ((u32)(((*((field##_TYPE*)(((u32)vpMem) + field##_OFFSET))) \
+ & field##_MASK) >> field##_SHIFT)) \
+
+#define MEMIO_WRITE_FIELD(vpMem, field, value) \
+do { \
+ ((*((field##_TYPE*)(((u32)vpMem) + field##_OFFSET))) = \
+ ((*((field##_TYPE*)(((u32)vpMem) + field##_OFFSET))) \
+ & (field##_TYPE)~field##_MASK) | \
+ (field##_TYPE)(((u32)(value) << field##_SHIFT) & field##_MASK)); \
+} while (0)
+
+#define MEMIO_WRITE_FIELD_LITE(vpMem, field, value) \
+do { \
+ (*((field##_TYPE*)(((u32)vpMem) + field##_OFFSET))) = \
+ ((*((field##_TYPE*)(((u32)vpMem) + field##_OFFSET))) | \
+ (field##_TYPE)(((u32)(value) << field##_SHIFT))); \
+} while (0)
+
+#define REGIO_READ_FIELD(reg_val, reg, field) \
+ ((reg_val & reg##_##field##_MASK) >> reg##_##field##_SHIFT)
+
+#define REGIO_WRITE_FIELD(reg_val, reg, field, value) \
+do { \
+ (reg_val) = \
+ ((reg_val) & ~(reg##_##field##_MASK)) | \
+ (((value) << (reg##_##field##_SHIFT)) & (reg##_##field##_MASK)); \
+} while (0)
+
+
+#define REGIO_WRITE_FIELD_LITE(reg_val, reg, field, value) \
+do { \
+ (reg_val) = ((reg_val) | ((value) << (reg##_##field##_SHIFT))); \
+} while (0)
+
+/****** MSVDX.Technical Reference Manual.2.0.2.4.External VXD38x **************
+Offset address Name Identifier
+0x0000 - 0x03FF (1024B) MTX Register REG_MSVDX_MTX
+0x0400 - 0x047F (128B) VDMC Register REG_MSVDX _VDMC
+0x0480 - 0x04FF (128B) VDEB Register REG_MSVDX _VDEB
+0x0500 - 0x05FF (256B) DMAC Register REG_MSVDX _DMAC
+0x0600 - 0x06FF (256B) MSVDX Core Register REG_MSVDX _SYS
+0x0700 - 0x07FF (256B) VEC iQ Matrix RAM REG_MSVDX_VEC_IQRAM
+0x0800 - 0x0FFF (2048B) VEC Registers REG_MSVDX _VEC
+0x1000 - 0x1FFF (4kB) Command Register REG_MSVDX _CMD
+0x2000 - 0x2FFF (4kB) VEC Local RAM REG_MSVDX _VEC_RAM
+0x3000 - 0x4FFF (8kB) VEC VLC Table RAM REG_MSVDX _VEC_VLC
+0x5000 - 0x5FFF (4kB) AXI Register REG_MSVDX _AXI
+******************************************************************************/
+
+/*************** MTX registers start: 0x0000 - 0x03FF (1024B) ****************/
+#define MTX_ENABLE_OFFSET (0x0000)
+#define MTX_ENABLE_MTX_ENABLE_MASK (0x00000001)
+#define MTX_ENABLE_MTX_ENABLE_SHIFT (0)
+
+#define MTX_KICK_INPUT_OFFSET (0x0080)
+
+#define MTX_REGISTER_READ_WRITE_REQUEST_OFFSET (0x00FC)
+#define MTX_REGISTER_READ_WRITE_REQUEST_MTX_DREADY_MASK (0x80000000)
+#define MTX_REGISTER_READ_WRITE_REQUEST_MTX_DREADY_SHIFT (31)
+#define MTX_REGISTER_READ_WRITE_REQUEST_MTX_RNW_MASK (0x00010000)
+#define MTX_REGISTER_READ_WRITE_REQUEST_MTX_RNW_SHIFT (16)
+
+#define MTX_REGISTER_READ_WRITE_DATA_OFFSET (0x00F8)
+
+#define MTX_RAM_ACCESS_DATA_TRANSFER_OFFSET (0x0104)
+
+#define MTX_RAM_ACCESS_CONTROL_OFFSET (0x0108)
+#define MTX_RAM_ACCESS_CONTROL_MTX_MCMID_MASK (0x0FF00000)
+#define MTX_RAM_ACCESS_CONTROL_MTX_MCMID_SHIFT (20)
+#define MTX_RAM_ACCESS_CONTROL_MTX_MCM_ADDR_MASK (0x000FFFFC)
+#define MTX_RAM_ACCESS_CONTROL_MTX_MCM_ADDR_SHIFT (2)
+#define MTX_RAM_ACCESS_CONTROL_MTX_MCMAI_MASK (0x00000002)
+#define MTX_RAM_ACCESS_CONTROL_MTX_MCMAI_SHIFT (1)
+#define MTX_RAM_ACCESS_CONTROL_MTX_MCMR_MASK (0x00000001)
+#define MTX_RAM_ACCESS_CONTROL_MTX_MCMR_SHIFT (0)
+
+#define MTX_RAM_ACCESS_STATUS_OFFSET (0x010C)
+
+#define MTX_SOFT_RESET_OFFSET (0x0200)
+#define MTX_SOFT_RESET_MTX_RESET_MASK (0x00000001)
+#define MTX_SOFT_RESET_MTX_RESET_SHIFT (0)
+#define MTX_SOFT_RESET_MTXRESET (0x00000001)
+
+#define MTX_SYSC_TIMERDIV_OFFSET (0x0208)
+
+#define MTX_SYSC_CDMAC_OFFSET (0x0340)
+#define MTX_SYSC_CDMAC_BURSTSIZE_MASK (0x07000000)
+#define MTX_SYSC_CDMAC_BURSTSIZE_SHIFT (24)
+#define MTX_SYSC_CDMAC_RNW_MASK (0x00020000)
+#define MTX_SYSC_CDMAC_RNW_SHIFT (17)
+#define MTX_SYSC_CDMAC_ENABLE_MASK (0x00010000)
+#define MTX_SYSC_CDMAC_ENABLE_SHIFT (16)
+#define MTX_SYSC_CDMAC_LENGTH_MASK (0x0000FFFF)
+#define MTX_SYSC_CDMAC_LENGTH_SHIFT (0)
+
+#define MTX_SYSC_CDMAA_OFFSET (0x0344)
+
+#define MTX_SYSC_CDMAS0_OFFSET (0x0348)
+
+#define MTX_SYSC_CDMAT_OFFSET (0x0350)
+/************************** MTX registers end **************************/
+
+/**************** DMAC Registers: 0x0500 - 0x05FF (256B) ***************/
+#define DMAC_DMAC_COUNT_EN_MASK (0x00010000)
+#define DMAC_DMAC_IRQ_STAT_TRANSFER_FIN_MASK (0x00020000)
+
+#define DMAC_DMAC_SETUP_OFFSET (0x0500)
+
+#define DMAC_DMAC_COUNT_OFFSET (0x0504)
+#define DMAC_DMAC_COUNT_BSWAP_LSBMASK (0x00000001)
+#define DMAC_DMAC_COUNT_BSWAP_SHIFT (30)
+#define DMAC_DMAC_COUNT_PW_LSBMASK (0x00000003)
+#define DMAC_DMAC_COUNT_PW_SHIFT (27)
+#define DMAC_DMAC_COUNT_DIR_LSBMASK (0x00000001)
+#define DMAC_DMAC_COUNT_DIR_SHIFT (26)
+#define DMAC_DMAC_COUNT_PI_LSBMASK (0x00000003)
+#define DMAC_DMAC_COUNT_PI_SHIFT (24)
+#define DMAC_DMAC_COUNT_CNT_LSBMASK (0x0000FFFF)
+#define DMAC_DMAC_COUNT_CNT_SHIFT (0)
+#define DMAC_DMAC_COUNT_EN_MASK (0x00010000)
+#define DMAC_DMAC_COUNT_EN_SHIFT (16)
+
+#define DMAC_DMAC_PERIPH_OFFSET (0x0508)
+#define DMAC_DMAC_PERIPH_ACC_DEL_LSBMASK (0x00000007)
+#define DMAC_DMAC_PERIPH_ACC_DEL_SHIFT (29)
+#define DMAC_DMAC_PERIPH_INCR_LSBMASK (0x00000001)
+#define DMAC_DMAC_PERIPH_INCR_SHIFT (27)
+#define DMAC_DMAC_PERIPH_BURST_LSBMASK (0x00000007)
+#define DMAC_DMAC_PERIPH_BURST_SHIFT (24)
+
+#define DMAC_DMAC_IRQ_STAT_OFFSET (0x050C)
+#define DMAC_DMAC_IRQ_STAT_TRANSFER_FIN_MASK (0x00020000)
+
+#define DMAC_DMAC_PERIPHERAL_ADDR_OFFSET (0x0514)
+#define DMAC_DMAC_PERIPHERAL_ADDR_ADDR_MASK (0x007FFFFF)
+#define DMAC_DMAC_PERIPHERAL_ADDR_ADDR_LSBMASK (0x007FFFFF)
+#define DMAC_DMAC_PERIPHERAL_ADDR_ADDR_SHIFT (0)
+
+/* DMAC control */
+#define IPVR_DMAC_VALUE_COUNT(BSWAP, PW, DIR, PERIPH_INCR, COUNT) \
+ ((((BSWAP) & DMAC_DMAC_COUNT_BSWAP_LSBMASK) << \
+ DMAC_DMAC_COUNT_BSWAP_SHIFT) | \
+ (((PW) & DMAC_DMAC_COUNT_PW_LSBMASK) << \
+ DMAC_DMAC_COUNT_PW_SHIFT) | \
+ (((DIR) & DMAC_DMAC_COUNT_DIR_LSBMASK) << \
+ DMAC_DMAC_COUNT_DIR_SHIFT) | \
+ (((PERIPH_INCR) & DMAC_DMAC_COUNT_PI_LSBMASK) << \
+ DMAC_DMAC_COUNT_PI_SHIFT) | \
+ (((COUNT) & DMAC_DMAC_COUNT_CNT_LSBMASK) << \
+ DMAC_DMAC_COUNT_CNT_SHIFT))
+
+#define IPVR_DMAC_VALUE_PERIPH_PARAM(ACC_DEL, INCR, BURST) \
+ ((((ACC_DEL) & DMAC_DMAC_PERIPH_ACC_DEL_LSBMASK) << \
+ DMAC_DMAC_PERIPH_ACC_DEL_SHIFT) | \
+ (((INCR) & DMAC_DMAC_PERIPH_INCR_LSBMASK) << \
+ DMAC_DMAC_PERIPH_INCR_SHIFT) | \
+ (((BURST) & DMAC_DMAC_PERIPH_BURST_LSBMASK) << \
+ DMAC_DMAC_PERIPH_BURST_SHIFT))
+
+typedef enum {
+ /* !< No byte swapping will be performed. */
+ IPVR_DMAC_BSWAP_NO_SWAP = 0x0,
+ /* !< Byte order will be reversed. */
+ IPVR_DMAC_BSWAP_REVERSE = 0x1,
+} DMAC_eBSwap;
+
+typedef enum {
+ /* !< Data from memory to peripheral. */
+ IPVR_DMAC_DIR_MEM_TO_PERIPH = 0x0,
+ /* !< Data from peripheral to memory. */
+ IPVR_DMAC_DIR_PERIPH_TO_MEM = 0x1,
+} DMAC_eDir;
+
+typedef enum {
+ IPVR_DMAC_ACC_DEL_0 = 0x0, /* !< Access delay zero clock cycles */
+ IPVR_DMAC_ACC_DEL_256 = 0x1, /* !< Access delay 256 clock cycles */
+ IPVR_DMAC_ACC_DEL_512 = 0x2, /* !< Access delay 512 clock cycles */
+ IPVR_DMAC_ACC_DEL_768 = 0x3, /* !< Access delay 768 clock cycles */
+ IPVR_DMAC_ACC_DEL_1024 = 0x4, /* !< Access delay 1024 clock cycles */
+ IPVR_DMAC_ACC_DEL_1280 = 0x5, /* !< Access delay 1280 clock cycles */
+ IPVR_DMAC_ACC_DEL_1536 = 0x6, /* !< Access delay 1536 clock cycles */
+ IPVR_DMAC_ACC_DEL_1792 = 0x7, /* !< Access delay 1792 clock cycles */
+} DMAC_eAccDel;
+
+typedef enum {
+ IPVR_DMAC_INCR_OFF = 0, /* !< Static peripheral address. */
+ IPVR_DMAC_INCR_ON = 1, /* !< Incrementing peripheral address. */
+} DMAC_eIncr;
+
+typedef enum {
+ IPVR_DMAC_BURST_0 = 0x0, /* !< burst size of 0 */
+ IPVR_DMAC_BURST_1 = 0x1, /* !< burst size of 1 */
+ IPVR_DMAC_BURST_2 = 0x2, /* !< burst size of 2 */
+ IPVR_DMAC_BURST_3 = 0x3, /* !< burst size of 3 */
+ IPVR_DMAC_BURST_4 = 0x4, /* !< burst size of 4 */
+ IPVR_DMAC_BURST_5 = 0x5, /* !< burst size of 5 */
+ IPVR_DMAC_BURST_6 = 0x6, /* !< burst size of 6 */
+ IPVR_DMAC_BURST_7 = 0x7, /* !< burst size of 7 */
+} DMAC_eBurst;
+/************************** DMAC Registers end **************************/
+
+/**************** MSVDX Core Registers: 0x0600 - 0x06FF (256B) ***************/
+#define MSVDX_CONTROL_OFFSET (0x0600)
+#define MSVDX_CONTROL_MSVDX_SOFT_RESET_MASK (0x00000100)
+#define MSVDX_CONTROL_MSVDX_SOFT_RESET_SHIFT (8)
+#define MSVDX_CONTROL_DMAC_CH0_SELECT_MASK (0x00001000)
+#define MSVDX_CONTROL_DMAC_CH0_SELECT_SHIFT (12)
+#define MSVDX_CONTROL_MSVDX_SOFT_RESET_MASK (0x00000100)
+#define MSVDX_CONTROL_MSVDX_SOFT_RESET_SHIFT (8)
+#define MSVDX_CONTROL_MSVDX_FE_SOFT_RESET_MASK (0x00010000)
+#define MSVDX_CONTROL_MSVDX_BE_SOFT_RESET_MASK (0x00100000)
+#define MSVDX_CONTROL_MSVDX_VEC_MEMIF_SOFT_RESET_MASK (0x01000000)
+#define MSVDX_CONTROL_MSVDX_VEC_RENDEC_DEC_SOFT_RESET_MASK (0x10000000)
+#define msvdx_sw_reset_all \
+ (MSVDX_CONTROL_MSVDX_SOFT_RESET_MASK | \
+ MSVDX_CONTROL_MSVDX_FE_SOFT_RESET_MASK | \
+ MSVDX_CONTROL_MSVDX_BE_SOFT_RESET_MASK | \
+ MSVDX_CONTROL_MSVDX_VEC_MEMIF_SOFT_RESET_MASK | \
+ MSVDX_CONTROL_MSVDX_VEC_RENDEC_DEC_SOFT_RESET_MASK)
+
+#define MSVDX_INTERRUPT_CLEAR_OFFSET (0x060C)
+
+#define MSVDX_INTERRUPT_STATUS_OFFSET (0x0608)
+#define MSVDX_INTERRUPT_STATUS_MMU_FAULT_IRQ_MASK (0x00000F00)
+#define MSVDX_INTERRUPT_STATUS_MMU_FAULT_IRQ_SHIFT (8)
+#define MSVDX_INTERRUPT_STATUS_MTX_IRQ_MASK (0x00004000)
+#define MSVDX_INTERRUPT_STATUS_MTX_IRQ_SHIFT (14)
+
+#define MSVDX_HOST_INTERRUPT_ENABLE_OFFSET (0x0610)
+
+#define MSVDX_MAN_CLK_ENABLE_OFFSET (0x0620)
+#define MSVDX_MAN_CLK_ENABLE_CORE_MAN_CLK_ENABLE_MASK (0x00000001)
+#define MSVDX_MAN_CLK_ENABLE_VDEB_PROCESS_MAN_CLK_ENABLE_MASK (0x00000002)
+#define MSVDX_MAN_CLK_ENABLE_VDEB_ACCESS_MAN_CLK_ENABLE_MASK (0x00000004)
+#define MSVDX_MAN_CLK_ENABLE_VDMC_MAN_CLK_ENABLE_MASK (0x00000008)
+#define MSVDX_MAN_CLK_ENABLE_VEC_ENTDEC_MAN_CLK_ENABLE_MASK (0x00000010)
+#define MSVDX_MAN_CLK_ENABLE_VEC_ITRANS_MAN_CLK_ENABLE_MASK (0x00000020)
+#define MSVDX_MAN_CLK_ENABLE_MTX_MAN_CLK_ENABLE_MASK (0x00000040)
+#define MSVDX_MAN_CLK_ENABLE_VDEB_PROCESS_AUTO_CLK_ENABLE_MASK (0x00020000)
+#define MSVDX_MAN_CLK_ENABLE_VDEB_ACCESS_AUTO_CLK_ENABLE_MASK (0x00040000)
+#define MSVDX_MAN_CLK_ENABLE_VDMC_AUTO_CLK_ENABLE_MASK (0x00080000)
+#define MSVDX_MAN_CLK_ENABLE_VEC_ENTDEC_AUTO_CLK_ENABLE_MASK (0x00100000)
+#define MSVDX_MAN_CLK_ENABLE_VEC_ITRANS_AUTO_CLK_ENABLE_MASK (0x00200000)
+
+#define clk_enable_all \
+ (MSVDX_MAN_CLK_ENABLE_CORE_MAN_CLK_ENABLE_MASK | \
+ MSVDX_MAN_CLK_ENABLE_VDEB_PROCESS_MAN_CLK_ENABLE_MASK | \
+ MSVDX_MAN_CLK_ENABLE_VDEB_ACCESS_MAN_CLK_ENABLE_MASK | \
+ MSVDX_MAN_CLK_ENABLE_VDMC_MAN_CLK_ENABLE_MASK | \
+ MSVDX_MAN_CLK_ENABLE_VEC_ENTDEC_MAN_CLK_ENABLE_MASK | \
+ MSVDX_MAN_CLK_ENABLE_VEC_ITRANS_MAN_CLK_ENABLE_MASK | \
+ MSVDX_MAN_CLK_ENABLE_MTX_MAN_CLK_ENABLE_MASK)
+
+#define clk_enable_minimal \
+ (MSVDX_MAN_CLK_ENABLE_CORE_MAN_CLK_ENABLE_MASK | \
+ MSVDX_MAN_CLK_ENABLE_MTX_MAN_CLK_ENABLE_MASK)
+
+#define clk_enable_auto \
+ (MSVDX_MAN_CLK_ENABLE_VDEB_PROCESS_AUTO_CLK_ENABLE_MASK | \
+ MSVDX_MAN_CLK_ENABLE_VDEB_ACCESS_AUTO_CLK_ENABLE_MASK | \
+ MSVDX_MAN_CLK_ENABLE_VDMC_AUTO_CLK_ENABLE_MASK | \
+ MSVDX_MAN_CLK_ENABLE_VEC_ENTDEC_AUTO_CLK_ENABLE_MASK | \
+ MSVDX_MAN_CLK_ENABLE_VEC_ITRANS_AUTO_CLK_ENABLE_MASK | \
+ MSVDX_MAN_CLK_ENABLE_CORE_MAN_CLK_ENABLE_MASK | \
+ MSVDX_MAN_CLK_ENABLE_MTX_MAN_CLK_ENABLE_MASK)
+
+#define MSVDX_CORE_ID_OFFSET (0x0630)
+#define MSVDX_CORE_REV_OFFSET (0x0640)
+
+#define MSVDX_DMAC_STREAM_STATUS_OFFSET (0x0648)
+
+#define MSVDX_MMU_CONTROL0_OFFSET (0x0680)
+#define MSVDX_MMU_CONTROL0_MMU_PAUSE_MASK (0x00000002)
+#define MSVDX_MMU_CONTROL0_MMU_PAUSE_SHIFT (1)
+#define MSVDX_MMU_CONTROL0_MMU_INVALDC_MASK (0x00000008)
+#define MSVDX_MMU_CONTROL0_MMU_INVALDC_SHIFT (3)
+
+#define MSVDX_MMU_BANK_INDEX_OFFSET (0x0688)
+
+#define MSVDX_MMU_STATUS_OFFSET (0x068C)
+
+#define MSVDX_MMU_CONTROL2_OFFSET (0x0690)
+
+#define MSVDX_MMU_DIR_LIST_BASE_OFFSET (0x0694)
+
+#define MSVDX_MMU_MEM_REQ_OFFSET (0x06D0)
+
+#define MSVDX_MMU_TILE_BASE0_OFFSET (0x06D4)
+
+#define MSVDX_MMU_TILE_BASE1_OFFSET (0x06D8)
+
+#define MSVDX_MTX_RAM_BANK_OFFSET (0x06F0)
+#define MSVDX_MTX_RAM_BANK_MTX_RAM_BANK_SIZE_MASK (0x000F0000)
+#define MSVDX_MTX_RAM_BANK_MTX_RAM_BANK_SIZE_SHIFT (16)
+
+#define MSVDX_MTX_DEBUG_OFFSET MSVDX_MTX_RAM_BANK_OFFSET
+#define MSVDX_MTX_DEBUG_MTX_DBG_IS_SLAVE_MASK (0x00000004)
+#define MSVDX_MTX_DEBUG_MTX_DBG_IS_SLAVE_LSBMASK (0x00000001)
+#define MSVDX_MTX_DEBUG_MTX_DBG_IS_SLAVE_SHIFT (2)
+#define MSVDX_MTX_DEBUG_MTX_DBG_GPIO_IN_MASK (0x00000003)
+#define MSVDX_MTX_DEBUG_MTX_DBG_GPIO_IN_LSBMASK (0x00000003)
+#define MSVDX_MTX_DEBUG_MTX_DBG_GPIO_IN_SHIFT (0)
+
+/*watch dog for FE and BE*/
+#define FE_MSVDX_WDT_CONTROL_OFFSET (0x0664)
+/* MSVDX_CORE, CR_FE_MSVDX_WDT_CONTROL, FE_WDT_CNT_CTRL */
+#define FE_MSVDX_WDT_CONTROL_FE_WDT_CNT_CTRL_MASK (0x00060000)
+#define FE_MSVDX_WDT_CONTROL_FE_WDT_CNT_CTRL_LSBMASK (0x00000003)
+#define FE_MSVDX_WDT_CONTROL_FE_WDT_CNT_CTRL_SHIFT (17)
+/* MSVDX_CORE, CR_FE_MSVDX_WDT_CONTROL, FE_WDT_ENABLE */
+#define FE_MSVDX_WDT_CONTROL_FE_WDT_ENABLE_MASK (0x00010000)
+#define FE_MSVDX_WDT_CONTROL_FE_WDT_ENABLE_LSBMASK (0x00000001)
+#define FE_MSVDX_WDT_CONTROL_FE_WDT_ENABLE_SHIFT (16)
+/* MSVDX_CORE, CR_FE_MSVDX_WDT_CONTROL, FE_WDT_ACTION1 */
+#define FE_MSVDX_WDT_CONTROL_FE_WDT_ACTION1_MASK (0x00003000)
+#define FE_MSVDX_WDT_CONTROL_FE_WDT_ACTION1_LSBMASK (0x00000003)
+#define FE_MSVDX_WDT_CONTROL_FE_WDT_ACTION1_SHIFT (12)
+/* MSVDX_CORE, CR_FE_MSVDX_WDT_CONTROL, FE_WDT_ACTION0 */
+#define FE_MSVDX_WDT_CONTROL_FE_WDT_ACTION0_MASK (0x00000100)
+#define FE_MSVDX_WDT_CONTROL_FE_WDT_ACTION0_LSBMASK (0x00000001)
+#define FE_MSVDX_WDT_CONTROL_FE_WDT_ACTION0_SHIFT (8)
+/* MSVDX_CORE, CR_FE_MSVDX_WDT_CONTROL, FE_WDT_CLEAR_SELECT */
+#define FE_MSVDX_WDT_CONTROL_FE_WDT_CLEAR_SELECT_MASK (0x00000030)
+#define FE_MSVDX_WDT_CONTROL_FE_WDT_CLEAR_SELECT_LSBMASK (0x00000003)
+#define FE_MSVDX_WDT_CONTROL_FE_WDT_CLEAR_SELECT_SHIFT (4)
+/* MSVDX_CORE, CR_FE_MSVDX_WDT_CONTROL, FE_WDT_CLKDIV_SELECT */
+#define FE_MSVDX_WDT_CONTROL_FE_WDT_CLKDIV_SELECT_MASK (0x00000007)
+#define FE_MSVDX_WDT_CONTROL_FE_WDT_CLKDIV_SELECT_LSBMASK (0x00000007)
+#define FE_MSVDX_WDT_CONTROL_FE_WDT_CLKDIV_SELECT_SHIFT (0)
+
+#define FE_MSVDX_WDTIMER_OFFSET (0x0668)
+/* MSVDX_CORE, CR_FE_MSVDX_WDTIMER, FE_WDT_COUNTER */
+#define FE_MSVDX_WDTIMER_FE_WDT_COUNTER_MASK (0x0000FFFF)
+#define FE_MSVDX_WDTIMER_FE_WDT_COUNTER_LSBMASK (0x0000FFFF)
+#define FE_MSVDX_WDTIMER_FE_WDT_COUNTER_SHIFT (0)
+
+#define FE_MSVDX_WDT_COMPAREMATCH_OFFSET (0x066c)
+/* MSVDX_CORE, CR_FE_MSVDX_WDT_COMPAREMATCH, FE_WDT_CM1 */
+#define FE_MSVDX_WDT_COMPAREMATCH_FE_WDT_CM1_MASK (0xFFFF0000)
+#define FE_MSVDX_WDT_COMPAREMATCH_FE_WDT_CM1_LSBMASK (0x0000FFFF)
+#define FE_MSVDX_WDT_COMPAREMATCH_FE_WDT_CM1_SHIFT (16)
+/* MSVDX_CORE, CR_FE_MSVDX_WDT_COMPAREMATCH, FE_WDT_CM0 */
+#define FE_MSVDX_WDT_COMPAREMATCH_FE_WDT_CM0_MASK (0x0000FFFF)
+#define FE_MSVDX_WDT_COMPAREMATCH_FE_WDT_CM0_LSBMASK (0x0000FFFF)
+#define FE_MSVDX_WDT_COMPAREMATCH_FE_WDT_CM0_SHIFT (0)
+
+#define BE_MSVDX_WDT_CONTROL_OFFSET (0x0670)
+/* MSVDX_CORE, CR_BE_MSVDX_WDT_CONTROL, BE_WDT_CNT_CTRL */
+#define BE_MSVDX_WDT_CONTROL_BE_WDT_CNT_CTRL_MASK (0x001E0000)
+#define BE_MSVDX_WDT_CONTROL_BE_WDT_CNT_CTRL_LSBMASK (0x0000000F)
+#define BE_MSVDX_WDT_CONTROL_BE_WDT_CNT_CTRL_SHIFT (17)
+/* MSVDX_CORE, CR_BE_MSVDX_WDT_CONTROL, BE_WDT_ENABLE */
+#define BE_MSVDX_WDT_CONTROL_BE_WDT_ENABLE_MASK (0x00010000)
+#define BE_MSVDX_WDT_CONTROL_BE_WDT_ENABLE_LSBMASK (0x00000001)
+#define BE_MSVDX_WDT_CONTROL_BE_WDT_ENABLE_SHIFT (16)
+/* MSVDX_CORE, CR_BE_MSVDX_WDT_CONTROL, BE_WDT_ACTION0 */
+#define BE_MSVDX_WDT_CONTROL_BE_WDT_ACTION0_MASK (0x00000100)
+#define BE_MSVDX_WDT_CONTROL_BE_WDT_ACTION0_LSBMASK (0x00000001)
+#define BE_MSVDX_WDT_CONTROL_BE_WDT_ACTION0_SHIFT (8)
+/* MSVDX_CORE, CR_BE_MSVDX_WDT_CONTROL, BE_WDT_CLEAR_SELECT */
+#define BE_MSVDX_WDT_CONTROL_BE_WDT_CLEAR_SELECT_MASK (0x000000F0)
+#define BE_MSVDX_WDT_CONTROL_BE_WDT_CLEAR_SELECT_LSBMASK (0x0000000F)
+#define BE_MSVDX_WDT_CONTROL_BE_WDT_CLEAR_SELECT_SHIFT (4)
+/* MSVDX_CORE, CR_BE_MSVDX_WDT_CONTROL, BE_WDT_CLKDIV_SELECT */
+#define BE_MSVDX_WDT_CONTROL_BE_WDT_CLKDIV_SELECT_MASK (0x00000007)
+#define BE_MSVDX_WDT_CONTROL_BE_WDT_CLKDIV_SELECT_LSBMASK (0x00000007)
+#define BE_MSVDX_WDT_CONTROL_BE_WDT_CLKDIV_SELECT_SHIFT (0)
+
+#define BE_MSVDX_WDTIMER_OFFSET (0x0674)
+/* MSVDX_CORE, CR_BE_MSVDX_WDTIMER, BE_WDT_COUNTER */
+#define BE_MSVDX_WDTIMER_BE_WDT_COUNTER_MASK (0x0000FFFF)
+#define BE_MSVDX_WDTIMER_BE_WDT_COUNTER_LSBMASK (0x0000FFFF)
+#define BE_MSVDX_WDTIMER_BE_WDT_COUNTER_SHIFT (0)
+
+#define BE_MSVDX_WDT_COMPAREMATCH_OFFSET (0x678)
+/* MSVDX_CORE, CR_BE_MSVDX_WDT_COMPAREMATCH, BE_WDT_CM0 */
+#define BE_MSVDX_WDT_COMPAREMATCH_BE_WDT_CM0_MASK (0x0000FFFF)
+#define BE_MSVDX_WDT_COMPAREMATCH_BE_WDT_CM0_LSBMASK (0x0000FFFF)
+#define BE_MSVDX_WDT_COMPAREMATCH_BE_WDT_CM0_SHIFT (0)
+
+/*watch dog end*/
+/************************** MSVDX Core Registers end *************************/
+
+/******************* VEC Registers: 0x0800 - 0x0FFF (2048B) ******************/
+#define VEC_SHIFTREG_CONTROL_OFFSET (0x0818)
+#define VEC_SHIFTREG_CONTROL_SR_MASTER_SELECT_MASK (0x00000300)
+#define VEC_SHIFTREG_CONTROL_SR_MASTER_SELECT_SHIFT (8)
+/************************** VEC Registers end **************************/
+
+/************************** RENDEC Registers **************************/
+#define MSVDX_RENDEC_CONTROL0_OFFSET (0x0868)
+#define MSVDX_RENDEC_CONTROL0_RENDEC_INITIALISE_MASK (0x00000001)
+#define MSVDX_RENDEC_CONTROL0_RENDEC_INITIALISE_SHIFT (0)
+
+#define MSVDX_RENDEC_CONTROL1_OFFSET (0x086C)
+#define MSVDX_RENDEC_CONTROL1_RENDEC_DECODE_START_SIZE_MASK (0x000000FF)
+#define MSVDX_RENDEC_CONTROL1_RENDEC_DECODE_START_SIZE_SHIFT (0)
+#define MSVDX_RENDEC_CONTROL1_RENDEC_BURST_SIZE_W_MASK (0x000C0000)
+#define MSVDX_RENDEC_CONTROL1_RENDEC_BURST_SIZE_W_SHIFT (18)
+#define MSVDX_RENDEC_CONTROL1_RENDEC_BURST_SIZE_R_MASK (0x00030000)
+#define MSVDX_RENDEC_CONTROL1_RENDEC_BURST_SIZE_R_SHIFT (16)
+#define MSVDX_RENDEC_CONTROL1_RENDEC_EXTERNAL_MEMORY_MASK (0x01000000)
+#define MSVDX_RENDEC_CONTROL1_RENDEC_EXTERNAL_MEMORY_SHIFT (24)
+#define MSVDX_RENDEC_CONTROL1_RENDEC_DEC_DISABLE_MASK (0x08000000)
+#define MSVDX_RENDEC_CONTROL1_RENDEC_DEC_DISABLE_SHIFT (27)
+
+#define MSVDX_RENDEC_BUFFER_SIZE_OFFSET (0x0870)
+#define MSVDX_RENDEC_BUFFER_SIZE_RENDEC_BUFFER_SIZE0_MASK (0x0000FFFF)
+#define MSVDX_RENDEC_BUFFER_SIZE_RENDEC_BUFFER_SIZE0_SHIFT (0)
+#define MSVDX_RENDEC_BUFFER_SIZE_RENDEC_BUFFER_SIZE1_MASK (0xFFFF0000)
+#define MSVDX_RENDEC_BUFFER_SIZE_RENDEC_BUFFER_SIZE1_SHIFT (16)
+
+#define MSVDX_RENDEC_BASE_ADDR0_OFFSET (0x0874)
+
+#define MSVDX_RENDEC_BASE_ADDR1_OFFSET (0x0878)
+
+#define MSVDX_RENDEC_READ_DATA_OFFSET (0x0898)
+
+#define MSVDX_RENDEC_CONTEXT0_OFFSET (0x0950)
+
+#define MSVDX_RENDEC_CONTEXT1_OFFSET (0x0954)
+
+#define MSVDX_RENDEC_CONTEXT2_OFFSET (0x0958)
+
+#define MSVDX_RENDEC_CONTEXT3_OFFSET (0x095C)
+
+#define MSVDX_RENDEC_CONTEXT4_OFFSET (0x0960)
+
+#define MSVDX_RENDEC_CONTEXT5_OFFSET (0x0964)
+/*************************** RENDEC registers end ****************************/
+
+/******************** CMD Register: 0x1000 - 0x1FFF (4kB) ********************/
+#define MSVDX_CMDS_END_SLICE_PICTURE_OFFSET (0x1404)
+/****************************** CMD Register end *****************************/
+
+/******************** VEC Local RAM: 0x2000 - 0x2FFF (4kB) *******************/
+/* vec local MEM save/restore */
+#define VEC_LOCAL_MEM_BYTE_SIZE (4 * 1024)
+#define VEC_LOCAL_MEM_OFFSET 0x2000
+
+#define MSVDX_EXT_FW_ERROR_STATE (0x2CC4)
+/* Decode operations in progress or not complete */
+#define MSVDX_FW_STATUS_IN_PROGRESS 0x00000000
+/* there's no work underway on the hardware, idle, can be powered down */
+#define MSVDX_FW_STATUS_HW_IDLE 0x00000001
+/* Panic, waiting to be reloaded */
+#define MSVDX_FW_STATUS_HW_PANIC 0x00000003
+
+/*
+ * This defines the MSVDX communication buffer
+ */
+#define MSVDX_COMMS_SIGNATURE_VALUE (0xA5A5A5A5) /*!< Signature value */
+/*!< Host buffer size (in 32-bit words) */
+#define NUM_WORDS_HOST_BUF (100)
+/*!< MTX buffer size (in 32-bit words) */
+#define NUM_WORDS_MTX_BUF (100)
+
+#define MSVDX_COMMS_AREA_ADDR (0x02fe0)
+#define MSVDX_COMMS_CORE_WTD (MSVDX_COMMS_AREA_ADDR - 0x08)
+#define MSVDX_COMMS_ERROR_TRIG (MSVDX_COMMS_AREA_ADDR - 0x08)
+#define MSVDX_COMMS_FIRMWARE_ID (MSVDX_COMMS_AREA_ADDR - 0x0C)
+#define MSVDX_COMMS_OFFSET_FLAGS (MSVDX_COMMS_AREA_ADDR + 0x18)
+#define MSVDX_COMMS_MSG_COUNTER (MSVDX_COMMS_AREA_ADDR - 0x04)
+#define MSVDX_COMMS_FW_STATUS (MSVDX_COMMS_AREA_ADDR - 0x10)
+#define MSVDX_COMMS_SIGNATURE (MSVDX_COMMS_AREA_ADDR + 0x00)
+#define MSVDX_COMMS_TO_HOST_BUF_SIZE (MSVDX_COMMS_AREA_ADDR + 0x04)
+#define MSVDX_COMMS_TO_HOST_RD_INDEX (MSVDX_COMMS_AREA_ADDR + 0x08)
+#define MSVDX_COMMS_TO_HOST_WRT_INDEX (MSVDX_COMMS_AREA_ADDR + 0x0C)
+#define MSVDX_COMMS_TO_MTX_BUF_SIZE (MSVDX_COMMS_AREA_ADDR + 0x10)
+#define MSVDX_COMMS_TO_MTX_RD_INDEX (MSVDX_COMMS_AREA_ADDR + 0x14)
+#define MSVDX_COMMS_TO_MTX_CB_RD_INDEX (MSVDX_COMMS_AREA_ADDR + 0x18)
+#define MSVDX_COMMS_TO_MTX_WRT_INDEX (MSVDX_COMMS_AREA_ADDR + 0x1C)
+#define MSVDX_COMMS_TO_HOST_BUF (MSVDX_COMMS_AREA_ADDR + 0x20)
+#define MSVDX_COMMS_TO_MTX_BUF \
+ (MSVDX_COMMS_TO_HOST_BUF + (NUM_WORDS_HOST_BUF << 2))
+
+/*
+ * FW FLAGs: it shall be written by the host prior to starting the Firmware.
+ */
+/* Disable Firmware based Watch dog timers. */
+#define DSIABLE_FW_WDT 0x0008
+ /* Abort Immediately on errors */
+#define ABORT_ON_ERRORS_IMMEDIATE 0x0010
+ /* Aborts faulted slices as soon as possible. Allows non faulted slices
+ * to reach backend but faulted slice will not be allowed to start. */
+#define ABORT_FAULTED_SLICE_IMMEDIATE 0x0020
+ /* Flush faulted slices - Debug option */
+#define FLUSH_FAULTED_SLICES 0x0080
+ /* Don't interrupt host when to host buffer becomes full.
+ * Stall until space is freed up by host on it's own. */
+#define NOT_INTERRUPT_WHEN_HOST_IS_FULL 0x0200
+ /* Contiguity warning msg will be send to host for stream with
+ * FW_ERROR_DETECTION_AND_RECOVERY flag set if non-contiguous
+ * macroblocks are detected. */
+#define NOT_ENABLE_ON_HOST_CONCEALMENT 0x0400
+ /* Return VDEB Signature Value in Completion message.
+ * This requires a VDEB data flush every slice for constant results.*/
+#define RETURN_VDEB_DATA_IN_COMPLETION 0x0800
+ /* Disable Auto Clock Gating. */
+#define DSIABLE_Auto_CLOCK_GATING 0x1000
+ /* Disable Idle GPIO signal. */
+#define DSIABLE_IDLE_GPIO_SIG 0x2000
+ /* Enable Setup, FE and BE Time stamps in completion message.
+ * Used by IMG only for firmware profiling. */
+#define ENABLE_TIMESTAMPS_IN_COMPLETE_MSG 0x4000
+ /* Disable off-host 2nd pass Deblocking in Firmware. */
+#define DSIABLE_OFFHOST_SECOND_DEBLOCK 0x20000
+ /* Sum address signature to data signature
+ * when returning VDEB signature values. */
+#define SUM_ADD_SIG_TO_DATA_SIGNATURE 0x80000
+
+/*
+#define MSVDX_COMMS_AREA_END \
+ (MSVDX_COMMS_TO_MTX_BUF + (NUM_WORDS_HOST_BUF << 2))
+*/
+#define MSVDX_COMMS_AREA_END 0x03000
+
+#if (MSVDX_COMMS_AREA_END != 0x03000)
+#error
+#endif
+/***************************** VEC Local RAM end *****************************/
+
+#endif
--
2.1.0
More information about the dri-devel
mailing list