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@xxxxxxxxx> --- 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@xxxxxxxxx> + * + **************************************************************************/ + +#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@xxxxxxxxx> + * + **************************************************************************/ + + +#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@xxxxxxxxx> + * Yao Cheng <yao.cheng@xxxxxxxxx> + * + **************************************************************************/ + +#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@xxxxxxxxx> + * Yao Cheng <yao.cheng@xxxxxxxxx> + * + **************************************************************************/ + + +#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@xxxxxxxxx> + * Yao Cheng <yao.cheng@xxxxxxxxx> + * + **************************************************************************/ + + +/* 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@xxxxxxxxx> + * Yao Cheng <yao.cheng@xxxxxxxxx> + * + **************************************************************************/ + +#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@xxxxxxxxx> + * Yao Cheng <yao.cheng@xxxxxxxxx> + * + **************************************************************************/ + +#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@xxxxxxxxx> + * Yao Cheng <yao.cheng@xxxxxxxxx> + * + **************************************************************************/ + +#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@xxxxxxxxx> + * Yao Cheng <yao.cheng@xxxxxxxxx> + * + **************************************************************************/ + +#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@xxxxxxxxx> + * Yao Cheng <yao.cheng@xxxxxxxxx> + * + **************************************************************************/ + +#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@xxxxxxxxx> + * Yao Cheng <yao.cheng@xxxxxxxxx> + * + **************************************************************************/ + +#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@xxxxxxxxx> + * Yao Cheng <yao.cheng@xxxxxxxxx> + * + **************************************************************************/ + +#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@xxxxxxxxx> + * Yao Cheng <yao.cheng@xxxxxxxxx> + * + **************************************************************************/ + +#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@xxxxxxxxx> + * Yao Cheng <yao.cheng@xxxxxxxxx> + * + **************************************************************************/ + +#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@xxxxxxxxxx> + * Fei Jiang <fei.jiang@xxxxxxxxx> + * Yao Cheng <yao.cheng@xxxxxxxxx> + * + **************************************************************************/ + +#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@xxxxxxxxx>> + */ + +#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@xxxxxxxxx> + * Yao Cheng <yao.cheng@xxxxxxxxx> + * + **************************************************************************/ + +#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@xxxxxxxxx> + * Yao Cheng <yao.cheng@xxxxxxxxx> + * + **************************************************************************/ + +#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@xxxxxxxxx> + * Yao Cheng <yao.cheng@xxxxxxxxx> + * + **************************************************************************/ + +#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@xxxxxxxxx> + * Yao Cheng <yao.cheng@xxxxxxxxx> + * + **************************************************************************/ + +#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@xxxxxxxxx> + * Yao Cheng <yao.cheng@xxxxxxxxx> + * + **************************************************************************/ + + +#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@xxxxxxxxx> + * Li Zeng <li.zeng@xxxxxxxxx> + * Yao Cheng <yao.cheng@xxxxxxxxx> + * + **************************************************************************/ + +#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@xxxxxxxxx> + * + **************************************************************************/ + + +#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@xxxxxxxxx> + * + **************************************************************************/ + +#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@xxxxxxxxx> + * Yao Cheng <yao.cheng@xxxxxxxxx> + * + **************************************************************************/ + +#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 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/dri-devel