[Meego-kernel] [MFLD Camera - PATCH v3 5/9] ISP driver - The low level memory management of ISP MMU block.
Zhang, Xiaolin
xiaolin.zhang at intel.com
Wed Dec 1 10:53:03 PST 2010
>From 49ef7ea85e424b2622cc1263a55a5dce4f02383e Mon Sep 17 00:00:00 2001
From: Xiaolin Zhang <xiaolin.zhang at intel.com>
Date: Wed, 1 Dec 2010 22:13:58 +0800
Subject: [MFLD Camera - PATCH v3 5/9] ISP driver - The low level memory management of ISP MMU block.
Signed-off-by: Xiaolin Zhang <xiaolin.zhang at intel.com>
---
drivers/media/video/mfld_ci/mfldisp/hmm/hmm.c | 372 +++++++
drivers/media/video/mfld_ci/mfldisp/hmm/hmm_bo.c | 1159 ++++++++++++++++++++
.../media/video/mfld_ci/mfldisp/hmm/hmm_bo_dev.c | 294 +++++
drivers/media/video/mfld_ci/mfldisp/hmm/hmm_vm.c | 204 ++++
.../mfld_ci/mfldisp/hrt/hive_isp_css_ddr_hrt.c | 317 ++++++
.../mfld_ci/mfldisp/hrt/hive_isp_css_mm_hrt.c | 138 +++
.../media/video/mfld_ci/mfldisp/include/hmm/hmm.h | 71 ++
.../video/mfld_ci/mfldisp/include/hmm/hmm_bo.h | 313 ++++++
.../video/mfld_ci/mfldisp/include/hmm/hmm_bo_dev.h | 124 +++
.../video/mfld_ci/mfldisp/include/hmm/hmm_common.h | 121 ++
.../video/mfld_ci/mfldisp/include/hmm/hmm_vm.h | 67 ++
11 files changed, 3180 insertions(+), 0 deletions(-)
create mode 100644 drivers/media/video/mfld_ci/mfldisp/hmm/hmm.c
create mode 100644 drivers/media/video/mfld_ci/mfldisp/hmm/hmm_bo.c
create mode 100644 drivers/media/video/mfld_ci/mfldisp/hmm/hmm_bo_dev.c
create mode 100644 drivers/media/video/mfld_ci/mfldisp/hmm/hmm_vm.c
create mode 100644 drivers/media/video/mfld_ci/mfldisp/hrt/hive_isp_css_ddr_hrt.c
create mode 100644 drivers/media/video/mfld_ci/mfldisp/hrt/hive_isp_css_mm_hrt.c
create mode 100644 drivers/media/video/mfld_ci/mfldisp/include/hmm/hmm.h
create mode 100644 drivers/media/video/mfld_ci/mfldisp/include/hmm/hmm_bo.h
create mode 100644 drivers/media/video/mfld_ci/mfldisp/include/hmm/hmm_bo_dev.h
create mode 100644 drivers/media/video/mfld_ci/mfldisp/include/hmm/hmm_common.h
create mode 100644 drivers/media/video/mfld_ci/mfldisp/include/hmm/hmm_vm.h
diff --git a/drivers/media/video/mfld_ci/mfldisp/hmm/hmm.c b/drivers/media/video/mfld_ci/mfldisp/hmm/hmm.c
new file mode 100644
index 0000000..8504fe5
--- /dev/null
+++ b/drivers/media/video/mfld_ci/mfldisp/hmm/hmm.c
@@ -0,0 +1,372 @@
+/*
+ * Support for Medifield PNW Camera Imaging ISP subsystem.
+ *
+ * Copyright (c) 2010 Intel Corporation. All Rights Reserved.
+ *
+ * Copyright (c) 2010 Silicon Hive www.siliconhive.com.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/highmem.h> /* for kmap */
+
+#include <linux/io.h> /* for page_to_phys */
+
+#include "hmm/hmm.h"
+#include "hmm/hmm_bo.h"
+#include "hmm/hmm_bo_dev.h"
+
+#include "mmu/isp_mmu.h"
+#include "mmu/sh_mmu.h"
+#include "mfldisp_internal.h"
+
+static struct hmm_bo_device __bo_device;
+void *__dummy_ptr;
+
+int hmm_init()
+{
+ int ret;
+
+ ret = hmm_bo_device_init(&__bo_device, &sh_mmu_driver,
+ ISP_VM_START, ISP_VM_SIZE);
+ if (ret)
+ mfld_isp_dbg(KERN_ERR, "hmm_bo_device_init failed.\n");
+
+ /*
+ * As hmm use NULL to indicate invalid ISP virtual address,
+ * and ISP_VM_START is defined to 0 too, so we allocate
+ * one piece of dummy memory, which should return value 0,
+ * at the beginning, to avoid hmm_alloc return 0 in the
+ * further allocation.
+ */
+ __dummy_ptr = hmm_alloc(1, HMM_BO_PRIVATE, 0, 0);
+ return ret;
+}
+
+void hmm_cleanup()
+{
+ /*
+ * free dummy memory first
+ */
+ hmm_free(__dummy_ptr);
+ __dummy_ptr = NULL;
+
+ hmm_bo_device_exit(&__bo_device);
+}
+
+void *hmm_alloc(size_t bytes, enum hmm_bo_type type,
+ int from_highmem, unsigned int userptr)
+{
+ unsigned int pgnr;
+ struct hmm_buffer_object *bo;
+ int ret;
+
+ pgnr = __size_to_pgnr_ceil(bytes);
+
+ bo = hmm_bo_create(&__bo_device, pgnr);
+ if (!bo) {
+ mfld_isp_dbg(KERN_ERR, "hmm_bo_create failed.\n");
+ goto create_bo_err;
+ }
+
+ ret = hmm_bo_alloc_vm(bo);
+ if (ret) {
+ mfld_isp_dbg(KERN_ERR, "hmm_bo_alloc_vm failed.\n");
+ goto alloc_vm_err;
+ }
+
+ ret = hmm_bo_alloc_pages(bo, type, from_highmem, userptr);
+ if (ret) {
+ mfld_isp_dbg(KERN_ERR, "hmm_bo_alloc_pages failed.\n");
+ goto alloc_page_err;
+ }
+
+ ret = hmm_bo_bind(bo);
+ if (ret) {
+ mfld_isp_dbg(KERN_ERR, "hmm_bo_bind failed.\n");
+ goto bind_err;
+ }
+
+ return (void *)bo->vm_node->start;
+
+bind_err:
+ hmm_bo_free_pages(bo);
+alloc_page_err:
+ hmm_bo_free_vm(bo);
+alloc_vm_err:
+ hmm_bo_unref(bo);
+create_bo_err:
+ return NULL;
+}
+
+void hmm_free(void *virt)
+{
+ struct hmm_buffer_object *bo;
+
+ bo = hmm_bo_device_search_start(&__bo_device, (unsigned int)virt);
+
+ if (!bo) {
+ mfld_isp_dbg(KERN_ERR, "can not find buffer object start with "
+ "address 0x%x\n", (unsigned int)virt);
+ return;
+ }
+
+ hmm_bo_unbind(bo);
+
+ hmm_bo_free_pages(bo);
+
+ hmm_bo_free_vm(bo);
+
+ hmm_bo_unref(bo);
+}
+
+int hmm_load(void *virt, void *data, unsigned int bytes)
+{
+ unsigned int ptr;
+ struct hmm_buffer_object *bo;
+ unsigned int idx, offset, len;
+ char *src, *des;
+
+ ptr = (unsigned int)virt;
+
+ bo = hmm_bo_device_search_in_range(&__bo_device, ptr);
+ if (!bo) {
+ mfld_isp_dbg(KERN_ERR, "can not find buffer object contains "
+ "address 0x%x\n", (unsigned int)ptr);
+ return -EINVAL;
+ }
+
+ if (!hmm_bo_page_allocated(bo)) {
+ mfld_isp_dbg(KERN_ERR,
+ "buffer object has no page allocated.\n");
+ return -EINVAL;
+ }
+
+ if (!hmm_bo_vm_allocated(bo)) {
+ mfld_isp_dbg(KERN_ERR,
+ "buffer object has no virtual"
+ " address space allocated.\n");
+ return -EINVAL;
+ }
+
+ des = (char *)data;
+ while (bytes) {
+ idx = (ptr - bo->vm_node->start) >> PAGE_SHIFT;
+ offset = (ptr - bo->vm_node->start) - (idx << PAGE_SHIFT);
+
+ src = (char *)kmap(bo->pages[idx]);
+ if (!src) {
+ mfld_isp_dbg(KERN_ERR,
+ "kmap buffer object page failed: "
+ "pg_idx = %d\n", idx);
+ return -EINVAL;
+ }
+
+ src += offset;
+
+ if ((bytes + offset) >= PAGE_SIZE) {
+ len = PAGE_SIZE - offset;
+ bytes -= len;
+ } else {
+ len = bytes;
+ bytes = 0;
+ }
+
+ ptr += len; /* update ptr for next loop */
+
+ while (len--)
+ *des++ = *src++;
+
+ kunmap(bo->pages[idx]);
+ }
+
+ return 0;
+}
+
+int hmm_store(void *virt, const void *data, unsigned int bytes)
+{
+ unsigned int ptr;
+ struct hmm_buffer_object *bo;
+ unsigned int idx, offset, len;
+ char *src, *des;
+
+ ptr = (unsigned int)virt;
+
+ bo = hmm_bo_device_search_in_range(&__bo_device, ptr);
+ if (!bo) {
+ mfld_isp_dbg(KERN_ERR, "can not find buffer object contains "
+ "address 0x%x\n", (unsigned int)ptr);
+ return -EINVAL;
+ }
+
+ if (!hmm_bo_page_allocated(bo)) {
+ mfld_isp_dbg(KERN_ERR,
+ "buffer object has no page allocated.\n");
+ return -EINVAL;
+ }
+
+ if (!hmm_bo_vm_allocated(bo)) {
+ mfld_isp_dbg(KERN_ERR,
+ "buffer object has no virtual address"
+ " space allocated.\n");
+ return -EINVAL;
+ }
+
+ src = (char *)data;
+ while (bytes) {
+ idx = (ptr - bo->vm_node->start) >> PAGE_SHIFT;
+ offset = (ptr - bo->vm_node->start) - (idx << PAGE_SHIFT);
+
+ des = (char *)kmap(bo->pages[idx]);
+ if (!des) {
+ mfld_isp_dbg(KERN_ERR,
+ "kmap buffer object page failed: "
+ "pg_idx = %d\n", idx);
+ return -EINVAL;
+ }
+
+ des += offset;
+
+ if ((bytes + offset) >= PAGE_SIZE) {
+ len = PAGE_SIZE - offset;
+ bytes -= len;
+ } else {
+ len = bytes;
+ bytes = 0;
+ }
+
+ ptr += len;
+
+ while (len--)
+ *des++ = *src++;
+
+ kunmap(bo->pages[idx]);
+ }
+
+ return 0;
+}
+
+int hmm_set(void *virt, char c, unsigned int bytes)
+{
+ unsigned int ptr;
+ struct hmm_buffer_object *bo;
+ unsigned int idx, offset, len;
+ char *des;
+
+ ptr = (unsigned int)virt;
+
+ bo = hmm_bo_device_search_in_range(&__bo_device, ptr);
+ if (!bo) {
+ mfld_isp_dbg(KERN_ERR, "can not find buffer object contains "
+ "address 0x%x\n", (unsigned int)ptr);
+ return -EINVAL;
+ }
+
+ if (!hmm_bo_page_allocated(bo)) {
+ mfld_isp_dbg(KERN_ERR,
+ "buffer object has no page allocated.\n");
+ return -EINVAL;
+ }
+
+ if (!hmm_bo_vm_allocated(bo)) {
+ mfld_isp_dbg(KERN_ERR,
+ "buffer object has no virtual address"
+ " space allocated.\n");
+ return -EINVAL;
+ }
+
+ while (bytes) {
+ idx = (ptr - bo->vm_node->start) >> PAGE_SHIFT;
+ offset = (ptr - bo->vm_node->start) - (idx << PAGE_SHIFT);
+
+ des = (char *)kmap(bo->pages[idx]);
+ if (!des) {
+ mfld_isp_dbg(KERN_ERR,
+ "kmap buffer object page failed: "
+ "pg_idx = %d\n", idx);
+ return -EINVAL;
+ }
+ des += offset;
+
+ if ((bytes + offset) >= PAGE_SIZE) {
+ len = PAGE_SIZE - offset;
+ bytes -= len;
+ } else {
+ len = bytes;
+ bytes = 0;
+ }
+
+ ptr += len;
+
+ while (len--)
+ *des++ = c;
+
+ kunmap(bo->pages[idx]);
+ }
+
+ return 0;
+}
+
+unsigned int hmm_virt_to_phys(void *virt)
+{
+ unsigned int ptr = (unsigned int)virt;
+ unsigned int idx, offset;
+ struct hmm_buffer_object *bo;
+
+ bo = hmm_bo_device_search_in_range(&__bo_device, ptr);
+ if (!bo) {
+ mfld_isp_dbg(KERN_ERR, "can not find buffer object contains "
+ "address 0x%x\n", ptr);
+ return -1;
+ }
+
+ idx = (ptr - bo->vm_node->start) >> PAGE_SHIFT;
+ offset = (ptr - bo->vm_node->start) - (idx << PAGE_SHIFT);
+
+ return page_to_phys(bo->pages[idx]) + offset;
+}
+
+int hmm_mmap(struct vm_area_struct *vma, void *virt)
+{
+ unsigned int ptr = (unsigned int)virt;
+ struct hmm_buffer_object *bo;
+
+ bo = hmm_bo_device_search_start(&__bo_device, ptr);
+ if (!bo) {
+ mfld_isp_dbg(KERN_ERR, "can not find buffer object start with "
+ "address 0x%x\n", (unsigned int)virt);
+ return -EINVAL;
+ }
+
+ return hmm_bo_mmap(vma, bo);
+}
+
+void *hmm_vmap(void *virt)
+{
+ unsigned int ptr = (unsigned int)virt;
+ struct hmm_buffer_object *bo;
+
+ bo = hmm_bo_device_search_start(&__bo_device, ptr);
+ if (!bo) {
+ mfld_isp_dbg(KERN_ERR, "can not find buffer object start with "
+ "address 0x%x\n", (unsigned int)virt);
+ return NULL;
+ }
+
+ return hmm_bo_vmap(bo);
+}
diff --git a/drivers/media/video/mfld_ci/mfldisp/hmm/hmm_bo.c b/drivers/media/video/mfld_ci/mfldisp/hmm/hmm_bo.c
new file mode 100644
index 0000000..b6b5632
--- /dev/null
+++ b/drivers/media/video/mfld_ci/mfldisp/hmm/hmm_bo.c
@@ -0,0 +1,1159 @@
+/*
+ * Support for Medifield PNW Camera Imaging ISP subsystem.
+ *
+ * Copyright (c) 2010 Intel Corporation. All Rights Reserved.
+ *
+ * Copyright (c) 2010 Silicon Hive www.siliconhive.com.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/gfp.h> /* for GFP_ATOMIC */
+#include <linux/mm.h>
+#include <linux/mm_types.h>
+#include <linux/slab.h> /* for kmalloc */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/string.h>
+#include <linux/list.h>
+#include <linux/errno.h>
+#include <asm/cacheflush.h>
+#include <linux/io.h>
+#include <asm/current.h>
+#include <linux/sched.h>
+
+#ifdef USE_DRM_MM
+#include <drm/drm_mm.h>
+#else
+#include "hmm/hmm_vm.h"
+#endif
+
+#include "hmm/hmm_bo.h"
+#include "hmm/hmm_bo_dev.h"
+#include "hmm/hmm_common.h"
+#include "mfldisp_internal.h"
+
+#define __check_bo_status_yes_goto(bo, _status, label) \
+ __var_not_equal_goto((bo->status & (_status)), (_status), \
+ label, \
+ "HMM buffer status not contain %s.\n", \
+ #_status)
+
+#define __check_bo_status_no_goto(bo, _status, label) \
+ __var_equal_goto((bo->status & (_status)), (_status), \
+ label, \
+ "HMM buffer status contains %s.\n", \
+ #_status)
+#define HMM_MAX_ORDER 10
+
+static inline unsigned int order_to_nr(unsigned int order)
+{
+ return 1U << order;
+}
+
+static inline unsigned int nr_to_order_ceil(unsigned int nr)
+{
+ unsigned int order = 0;
+
+ for (; nr / 2; nr = nr / 2 + nr % 2)
+ order++;
+
+ return order;
+}
+
+static inline unsigned int nr_to_order_bottom(unsigned int nr)
+{
+ unsigned int order = 0;
+
+ for (; nr /= 2;)
+ order++;
+
+ return order;
+}
+
+static void __free_bo_internal(struct hmm_buffer_object *bo)
+{
+ kfree(bo);
+}
+
+/*
+ * use these functions to dynamically alloc hmm_buffer_object.
+ * hmm_bo_init will called for that allocated buffer object, and
+ * the release callback is set to kfree.
+ */
+struct hmm_buffer_object *hmm_bo_create(struct hmm_bo_device *bdev, int pgnr)
+{
+ struct hmm_buffer_object *bo;
+ int ret;
+
+ bo = kmalloc(sizeof(*bo), GFP_KERNEL);
+ if (!bo) {
+ mfld_isp_dbg(KERN_ERR, "out of memory.\n");
+ return NULL;
+ }
+
+ ret = hmm_bo_init(bdev, bo, pgnr, __free_bo_internal);
+ if (ret) {
+ mfld_isp_dbg(KERN_ERR, "hmm_bo_init failed\n");
+ kfree(bo);
+ return NULL;
+ }
+
+ return bo;
+}
+
+/*
+ * use this function to initialize pre-allocated hmm_buffer_object.
+ * as hmm_buffer_object may be used as an embedded object in an upper
+ * level object, a release callback must be provided. if it is
+ * embedded in upper level object, set release call back to release
+ * function of that object. if no upper level object, set release
+ * callback to NULL.
+ *
+ * bo->kref is inited to 1.
+ */
+int hmm_bo_init(struct hmm_bo_device *bdev,
+ struct hmm_buffer_object *bo,
+ unsigned int pgnr, void (*release) (struct hmm_buffer_object *))
+{
+ if (bdev == NULL) {
+ mfld_isp_dbg(KERN_WARNING, "NULL hmm_bo_device.\n");
+ return -EINVAL;
+ }
+
+ /* hmm_bo_device must be already inited */
+ __var_equal_return(hmm_bo_device_inited(bdev), 0, -EINVAL,
+ "hmm_bo_device not inited yet.\n");
+
+ /* prevent zero size buffer object */
+ if (pgnr == 0) {
+ mfld_isp_dbg(KERN_ERR, "0 size buffer is not allowed.\n");
+ return -EINVAL;
+ }
+
+ memset(bo, 0, sizeof(*bo));
+
+ kref_init(&bo->kref);
+
+ mutex_init(&bo->mutex);
+
+ INIT_LIST_HEAD(&bo->list);
+
+ bo->pgnr = pgnr;
+ bo->bdev = bdev;
+
+ INIT_LIST_HEAD(&bo->pgblocks);
+
+ bo->release = release;
+
+ if (!bo->release)
+ mfld_isp_dbg(KERN_WARNING, "no release callback specified.\n");
+
+ /*
+ * add to active_bo_list
+ */
+ mutex_lock(&bdev->ablist_mutex);
+ list_add_tail(&bo->list, &bdev->active_bo_list);
+ bo->status |= HMM_BO_ACTIVE;
+ mutex_unlock(&bdev->ablist_mutex);
+
+ return 0;
+}
+
+static void hmm_bo_release(struct hmm_buffer_object *bo)
+{
+ struct hmm_bo_device *bdev;
+
+ __check_bo_null_return(bo, (void)0);
+
+ bdev = bo->bdev;
+
+ /*
+ * FIX ME:
+ *
+ * how to destroy the bo when it is stilled MMAPED?
+ *
+ * ideally, this will not happened as hmm_bo_release
+ * will only be called when kref reaches 0, and in mmap
+ * operation the hmm_bo_ref will eventually be called.
+ * so, if this happened, something goes wrong.
+ */
+ if (bo->status & HMM_BO_MMAPED) {
+ mfld_isp_dbg(KERN_ERR,
+ "destroy bo which is MMAPED, do nothing\n");
+ goto err;
+ }
+#ifdef HMM_BO_VMAP_SUPPORT
+ if (bo->status & HMM_BO_VMAPED) {
+ mfld_isp_dbg(KERN_WARNING,
+ "the bo is still vmapped, unmap it first...\n");
+ hmm_bo_vunmap(bo);
+ }
+#endif
+
+ if (bo->status & HMM_BO_BINDED) {
+ mfld_isp_dbg(KERN_WARNING,
+ "the bo is still binded, unbind it first...\n");
+ hmm_bo_unbind(bo);
+ }
+ if (bo->status & HMM_BO_PAGE_ALLOCED) {
+ mfld_isp_dbg(KERN_WARNING,
+ "the pages is not freed, free pages first\n");
+ hmm_bo_free_pages(bo);
+ }
+ if (bo->status & HMM_BO_VM_ALLOCED) {
+ mfld_isp_dbg(KERN_WARNING,
+ "the vm is still not freed, free vm first...\n");
+ hmm_bo_free_vm(bo);
+ }
+
+ /*
+ * remove it from buffer device's buffer object list.
+ */
+ if (hmm_bo_activated(bo)) {
+ mutex_lock(&bdev->ablist_mutex);
+ list_del(&bo->list);
+ mutex_unlock(&bdev->ablist_mutex);
+ } else {
+ mutex_lock(&bdev->fblist_mutex);
+ list_del(&bo->list);
+ mutex_unlock(&bdev->fblist_mutex);
+ }
+
+ if (bo->release)
+ bo->release(bo);
+err:
+ return;
+}
+
+int hmm_bo_activated(struct hmm_buffer_object *bo)
+{
+ __check_bo_null_return(bo, 0);
+
+ return bo->status & HMM_BO_ACTIVE;
+}
+
+void hmm_bo_unactivate(struct hmm_buffer_object *bo)
+{
+ struct hmm_bo_device *bdev;
+
+ __check_bo_null_return(bo, (void)0);
+
+ __check_bo_status_no_goto(bo, HMM_BO_ACTIVE, status_err);
+
+ bdev = bo->bdev;
+
+ mutex_lock(&bdev->ablist_mutex);
+ list_del(&bo->list);
+ mutex_unlock(&bdev->ablist_mutex);
+
+ mutex_lock(&bdev->fblist_mutex);
+ list_add_tail(&bo->list, &bdev->free_bo_list);
+ bo->status &= (~HMM_BO_ACTIVE);
+ mutex_unlock(&bdev->fblist_mutex);
+
+ return;
+
+status_err:
+ mfld_isp_dbg(KERN_ERR, "buffer object already unactivated.\n");
+ return;
+}
+
+int hmm_bo_alloc_vm(struct hmm_buffer_object *bo)
+{
+ struct hmm_bo_device *bdev;
+
+ __check_bo_null_return(bo, -EINVAL);
+
+ mutex_lock(&bo->mutex);
+
+ __check_bo_status_no_goto(bo, HMM_BO_VM_ALLOCED, status_err);
+
+ bdev = bo->bdev;
+
+#ifdef USE_DRM_MM
+retry:
+ ret = drm_mm_pre_get(&bdev->vaddr_space);
+ if (unlikely(ret != 0)) {
+ ret = -ENOMEM;
+ goto out_of_vm;
+ }
+
+ write_lock(&bdev->vm_lock);
+ /*
+ * unit of size parameter is page number or bytes?
+ * how to use alignment?
+ */
+ bo->vm_node = drm_mm_search_free(&bdev->vaddr_space, bo->pgnr, 0, 0);
+
+ if (unlikely(ret != 0)) {
+ ret = -ENOMEM;
+ goto out_of_vm_lock;
+ }
+
+ bo->vm_node = drm_mm_get_block_atomic(bo->vm_node, bo->pgnr, 0);
+
+ if (unlikely(ret != 0)) {
+ write_unlock(&bdev->vm_lock);
+ goto retry;
+ }
+
+ bo->status |= HMM_BO_VM_ALLOCED;
+
+ write_unlock(&bdev->vm_lock);
+
+ mutex_unlock(&bo->mutex);
+
+ return 0;
+
+out_of_vm_lock:
+ write_unlock(&bdev->vm_lock);
+out_of_vm:
+ mutex_unlock(&bo->mutex);
+ return ret;
+
+#else
+ bo->vm_node = hmm_vm_alloc_node(&bdev->vaddr_space, bo->pgnr);
+ if (unlikely(!bo->vm_node)) {
+ mfld_isp_dbg(KERN_ERR, "hmm_vm_alloc_node err.\n");
+ goto null_vm;
+ }
+
+ bo->status |= HMM_BO_VM_ALLOCED;
+
+ mutex_unlock(&bo->mutex);
+
+ return 0;
+null_vm:
+ mutex_unlock(&bo->mutex);
+ return -ENOMEM;
+#endif
+
+status_err:
+ mutex_unlock(&bo->mutex);
+ mfld_isp_dbg(KERN_ERR, "buffer object already has vm allocated.\n");
+ return -EINVAL;
+}
+
+void hmm_bo_free_vm(struct hmm_buffer_object *bo)
+{
+ struct hmm_bo_device *bdev;
+
+ __check_bo_null_return(bo, (void)0);
+
+ mutex_lock(&bo->mutex);
+
+ __check_bo_status_yes_goto(bo, HMM_BO_VM_ALLOCED, status_err);
+
+ bdev = bo->bdev;
+
+#ifdef USE_DRM_MM
+ write_lock(&bdev->vm_lock);
+
+ drm_mm_put_block(bo->vm_node);
+ bo->vm_node = NULL;
+
+ bo->status &= (~HMM_BO_VM_ALLOCED);
+
+ write_unlock(&bdev->vm_lock);
+#else
+ hmm_vm_free_node(bo->vm_node);
+ bo->vm_node = NULL;
+ bo->status &= (~HMM_BO_VM_ALLOCED);
+#endif
+ mutex_unlock(&bo->mutex);
+
+ return;
+
+status_err:
+ mutex_unlock(&bo->mutex);
+ mfld_isp_dbg(KERN_ERR, "buffer object has no vm allocated.\n");
+}
+
+int hmm_bo_vm_allocated(struct hmm_buffer_object *bo)
+{
+ int ret;
+
+ __check_bo_null_return(bo, 0);
+
+ mutex_lock(&bo->mutex);
+
+ ret = (bo->status & HMM_BO_VM_ALLOCED);
+
+ mutex_unlock(&bo->mutex);
+
+ return ret;
+}
+
+#ifdef CI_VA_SHARE_BUFFER
+static int __alloc_share_pages(struct hmm_buffer_object *bo, int from_highmem)
+{
+ int ret;
+
+ /*
+ * FIX ME:
+ *
+ * how to control ttm to alloc memory from/not from highmem.
+ */
+
+ ret = drm_bo_create_external(0, (bo->pgnr) << PAGE_SHIFT, &bo->handle);
+
+ if (ret) {
+ mfld_isp_dbg(KERN_ERR, "unable to alloc pages: %d\n", pgnr);
+ return ret;
+ }
+
+ /*
+ * Will bo->pgnr be larger than pgnr that we want to
+ * allocate?
+ */
+ drm_bo_external_get_pages(bo->handle, &bo->pages, &bo->pgnr);
+}
+
+static void __free_share_pages(struct hmm_buffer_object *bo)
+{
+ return drm_bo_external_destroy(bo->handle);
+}
+#endif
+
+static int __alloc_private_pages(struct hmm_buffer_object *bo, int from_highmem)
+{
+ int ret;
+ unsigned int pgnr, order, blk_pgnr;
+ struct page *pages;
+ struct __page_block *pgblk;
+ gfp_t gfp;
+ int i, j;
+
+ gfp = GFP_KERNEL;
+ if (from_highmem)
+ gfp |= __GFP_HIGHMEM;
+
+ pgnr = bo->pgnr;
+
+ bo->pages = kmalloc(sizeof(struct page *) *pgnr, GFP_KERNEL);
+ if (unlikely(!bo->pages))
+ goto out_of_mem;
+
+ i = 0;
+ while (pgnr) {
+ order = nr_to_order_bottom(pgnr);
+ if (order > HMM_MAX_ORDER)
+ order = HMM_MAX_ORDER;
+retry:
+ pages = alloc_pages(gfp, order);
+ if (unlikely(!pages)) {
+ order--;
+ if (order < 0)
+ goto out_of_mem;
+ else
+ goto retry;
+ } else {
+ blk_pgnr = order_to_nr(order);
+
+ pgblk = kmalloc(sizeof(*pgblk), GFP_KERNEL);
+ if (unlikely(!pgblk))
+ goto out_of_mem;
+
+ INIT_LIST_HEAD(&pgblk->list);
+ pgblk->pages = pages;
+ pgblk->order = order;
+
+ list_add_tail(&pgblk->list, &bo->pgblocks);
+
+ for (j = 0; j < blk_pgnr; j++)
+ bo->pages[i++] = pages + j;
+
+ pgnr -= blk_pgnr;
+
+ /*
+ * set memory to uncacheable -- UC_MINUS
+ */
+ ret = set_pages_uc(pages, blk_pgnr);
+ if (ret) {
+ mfld_isp_dbg(KERN_ERR,
+ "set page uncacheable failed.\n");
+ goto set_uc_mem_fail;
+ }
+ }
+ }
+
+ return 0;
+set_uc_mem_fail:
+ /* FIX ME: select one better */
+ ret = -ENOMEM;
+ goto cleanup;
+out_of_mem:
+ ret = -ENOMEM;
+ goto cleanup;
+cleanup:
+ while (!list_empty(&bo->pgblocks)) {
+ pgblk = list_first_entry(&bo->pgblocks,
+ struct __page_block, list);
+
+ list_del(&pgblk->list);
+
+ ret = set_pages_wb(pgblk->pages, order_to_nr(pgblk->order));
+ if (ret)
+ mfld_isp_dbg(KERN_ERR, "set page to WB err...\n");
+
+ __free_pages(pgblk->pages, pgblk->order);
+ kfree(pgblk);
+ }
+
+ return ret;
+}
+
+static void __free_private_pages(struct hmm_buffer_object *bo)
+{
+ struct __page_block *pgblk;
+ int ret;
+
+ while (!list_empty(&bo->pgblocks)) {
+ pgblk = list_first_entry(&bo->pgblocks,
+ struct __page_block, list);
+
+ list_del(&pgblk->list);
+
+ ret = set_pages_wb(pgblk->pages, order_to_nr(pgblk->order));
+ if (ret)
+ mfld_isp_dbg(KERN_ERR, "set page to WB err...\n");
+
+ __free_pages(pgblk->pages, pgblk->order);
+ kfree(pgblk);
+ }
+
+ kfree(bo->pages);
+}
+
+static int __alloc_user_pages(struct hmm_buffer_object *bo,
+ unsigned int userptr)
+{
+ unsigned int page_nr;
+ unsigned int i;
+ struct __page_block *pgblk;
+ int ret;
+
+ bo->pages = kmalloc(sizeof(struct page *) *bo->pgnr, GFP_KERNEL);
+ if (unlikely(!bo->pages)) {
+ mfld_isp_dbg(KERN_ERR, "out of memory...\n");
+ return -ENOMEM;
+ }
+
+ page_nr = get_user_pages(current, current->mm, (unsigned long)userptr,
+ (int)(bo->pgnr), 1, 0, bo->pages, NULL);
+ /* can be written by caller, not forced */
+ if (page_nr != bo->pgnr) {
+ mfld_isp_dbg(KERN_ERR, "get_user_pages err: bo->pgnr = %d, "
+ "pgnr actually pinned = %d.\n", bo->pgnr, page_nr);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < bo->pgnr; i++) {
+ pgblk = kmalloc(sizeof(*pgblk), GFP_KERNEL);
+ if (unlikely(!pgblk))
+ goto out_of_mem;
+
+ INIT_LIST_HEAD(&pgblk->list);
+ pgblk->pages = bo->pages[i];
+ pgblk->order = 0;
+
+ list_add_tail(&pgblk->list, &bo->pgblocks);
+ }
+
+ for (i = 0; i < bo->pgnr; i++) {
+ ret = set_pages_uc(bo->pages[i], 1);
+ if (ret) {
+ mfld_isp_dbg(KERN_ERR,
+ "set page to uncacheable failed.\n");
+ goto cleanup;
+ }
+ }
+
+ return 0;
+
+out_of_mem:
+ ret = -ENOMEM;
+ goto cleanup;
+cleanup:
+ while (!list_empty(&bo->pgblocks)) {
+ pgblk = list_first_entry(&bo->pgblocks,
+ struct __page_block, list);
+
+ list_del(&pgblk->list);
+
+ kfree(pgblk);
+ }
+
+ return ret;
+}
+
+static void __free_user_pages(struct hmm_buffer_object *bo)
+{
+ unsigned int i;
+ struct __page_block *pgblk;
+ int ret;
+
+ for (i = 0; i < bo->pgnr; i++) {
+ ret = set_pages_wb(bo->pages[i], 1);
+ if (ret)
+ mfld_isp_dbg(KERN_ERR,
+ "set page to WB again failed.\n");
+ }
+
+ while (!list_empty(&bo->pgblocks)) {
+ pgblk = list_first_entry(&bo->pgblocks,
+ struct __page_block, list);
+
+ list_del(&pgblk->list);
+
+ kfree(pgblk);
+ }
+
+ kfree(bo->pages);
+}
+
+/*
+ * allocate/free physical pages for the bo.
+ *
+ * type indicate where are the pages from. currently we have 3 types
+ * of memory: HMM_BO_PRIVATE, HMM_BO_USER, HMM_BO_SHARE.
+ *
+ * from_highmem is only valid when type is HMM_BO_PRIVATE, it will
+ * try to alloc memory from highmem if from_highmem is set.
+ *
+ * userptr is only valid when type is HMM_BO_USER, it indicates
+ * the start address from user space task.
+ *
+ * from_highmem and userptr will both be ignored when type is
+ * HMM_BO_SHARE.
+ */
+int hmm_bo_alloc_pages(struct hmm_buffer_object *bo,
+ enum hmm_bo_type type, int from_highmem,
+ unsigned int userptr)
+{
+ int ret;
+
+ __check_bo_null_return(bo, -EINVAL);
+
+ mutex_lock(&bo->mutex);
+
+ __check_bo_status_no_goto(bo, HMM_BO_PAGE_ALLOCED, status_err);
+
+ /*
+ * TO DO:
+ * add HMM_BO_USER type
+ */
+ if (type == HMM_BO_PRIVATE)
+ ret = __alloc_private_pages(bo, from_highmem);
+ else if (type == HMM_BO_USER)
+ ret = __alloc_user_pages(bo, userptr);
+#ifdef CI_VA_SHARE_BUFFER
+ else if (type == HMM_BO_SHARE)
+ ret = __alloc_share_pages(bo, from_highmem);
+#endif
+ else {
+ mfld_isp_dbg(KERN_ERR, "invalid buffer type.\n");
+ ret = -EINVAL;
+ }
+
+ if (ret)
+ goto alloc_err;
+
+ bo->type = type;
+
+ bo->status |= HMM_BO_PAGE_ALLOCED;
+
+ mutex_unlock(&bo->mutex);
+
+ return 0;
+
+alloc_err:
+ mutex_unlock(&bo->mutex);
+ mfld_isp_dbg(KERN_ERR, "alloc pages err...\n");
+ return ret;
+status_err:
+ mutex_unlock(&bo->mutex);
+ mfld_isp_dbg(KERN_ERR, "buffer object has already page allocated.\n");
+ return -EINVAL;
+}
+
+/*
+ * free physical pages of the bo.
+ */
+void hmm_bo_free_pages(struct hmm_buffer_object *bo)
+{
+ __check_bo_null_return(bo, (void)0);
+
+ mutex_lock(&bo->mutex);
+
+#ifdef HMM_BO_VMAP_SUPPORT
+ __check_bo_status_no_goto(bo, HMM_BO_VMAPED, status_err1);
+#endif
+
+ __check_bo_status_yes_goto(bo, HMM_BO_PAGE_ALLOCED, status_err2);
+
+ if (bo->type == HMM_BO_PRIVATE)
+ __free_private_pages(bo);
+ else if (bo->type == HMM_BO_USER)
+ __free_user_pages(bo);
+#ifdef CI_VA_SHARE_BUFFER
+ else if __free_share_pages
+ (bo);
+#endif
+ else
+ mfld_isp_dbg(KERN_ERR, "invalid buffer type.\n");
+ /* clear the flag anyway. */
+ bo->status &= (~HMM_BO_PAGE_ALLOCED);
+
+ mutex_unlock(&bo->mutex);
+
+ return;
+
+status_err2:
+ mutex_unlock(&bo->mutex);
+ mfld_isp_dbg(KERN_ERR, "buffer object not page allocated yet.\n");
+
+#ifdef HMM_BO_VMAP_SUPPORT
+status_err1:
+ mutex_unlock(&bo->mutex);
+ mfld_isp_dbg(KERN_ERR,
+ "buffer object still vmapped, dont free pages here.\n");
+#endif
+}
+
+int hmm_bo_page_allocated(struct hmm_buffer_object *bo)
+{
+ int ret;
+
+ __check_bo_null_return(bo, 0);
+
+ mutex_lock(&bo->mutex);
+
+ ret = bo->status & HMM_BO_PAGE_ALLOCED;
+
+ mutex_unlock(&bo->mutex);
+
+ return ret;
+}
+
+/*
+ * get physical page info of the bo.
+ */
+int hmm_bo_get_page_info(struct hmm_buffer_object *bo,
+ struct page ***pages, int *pgnr)
+{
+ __check_bo_null_return(bo, -EINVAL);
+
+ mutex_lock(&bo->mutex);
+
+ __check_bo_status_yes_goto(bo, HMM_BO_PAGE_ALLOCED, status_err);
+
+ *pages = bo->pages;
+ *pgnr = bo->pgnr;
+
+ mutex_unlock(&bo->mutex);
+
+ return 0;
+
+status_err:
+ mfld_isp_dbg(KERN_ERR, "buffer object not page allocated yet.\n");
+ mutex_unlock(&bo->mutex);
+ return -EINVAL;
+}
+
+/*
+ * bind the physical pages to a virtual address space.
+ */
+int hmm_bo_bind(struct hmm_buffer_object *bo)
+{
+ int ret;
+ unsigned int virt;
+ struct hmm_bo_device *bdev;
+#ifdef __MMU_MAP_BLOCK
+ struct list_head *pos;
+ struct __page_block *pgblk;
+ unsigned int blk_pgnr;
+#else
+ unsigned int i;
+#endif
+
+ __check_bo_null_return(bo, -EINVAL);
+
+ mutex_lock(&bo->mutex);
+
+ __check_bo_status_yes_goto(bo,
+ HMM_BO_PAGE_ALLOCED | HMM_BO_VM_ALLOCED,
+ status_err1);
+
+ __check_bo_status_no_goto(bo, HMM_BO_BINDED, status_err2);
+
+ bdev = bo->bdev;
+
+ virt = bo->vm_node->start;
+
+#ifdef __MMU_MAP_BLOCK
+ /*
+ * FIXME:
+ *
+ * bo->pgblocks is empty in case of HMM_BO_USER, so CANNOT use this way
+ * to setup address mapping.
+ * DONOT define __MMU_MAP_BLOCK macro currently.
+ */
+ list_for_each(pos, &bo->pgblocks) {
+ pgblk = list_entry(pos, struct __page_block, list);
+ blk_pgnr = order_to_nr(pgblk->order);
+ ret = isp_mmu_map(&bdev->mmu, virt, page_to_phys(pgblk->pages),
+ blk_pgnr);
+ if (ret)
+ goto map_err;
+ virt += (blk_pgnr << PAGE_SHIFT);
+ }
+#else
+ for (i = 0; i < bo->pgnr; i++) {
+ ret =
+ isp_mmu_map(&bdev->mmu, virt, page_to_phys(bo->pages[i]),
+ 1);
+ if (ret)
+ goto map_err;
+ virt += (1 << PAGE_SHIFT);
+ }
+#endif
+ /*
+ * flush TBL here.
+ *
+ * theoretically, we donot need to flush TLB as we didnot change
+ * any existed address mappings, but for Silicon Hive's MMU, its
+ * really a bug here. I guess when fetching PTEs (page table entity)
+ * to TLB, its MMU will fetch additional INVALID PTEs automatically
+ * for performance issue. EX, we only set up 1 page address mapping,
+ * meaning updating 1 PTE, but the MMU fetches 4 PTE at one time,
+ * so the additional 3 PTEs are invalid.
+ */
+ isp_mmu_flush_tlb_range(&bdev->mmu, bo->vm_node->start,
+ (bo->pgnr << PAGE_SHIFT));
+
+ bo->status |= HMM_BO_BINDED;
+
+ mutex_unlock(&bo->mutex);
+
+ return 0;
+
+map_err:
+ mutex_unlock(&bo->mutex);
+ mfld_isp_dbg(KERN_ERR, "setup MMU address mapping failed.\n");
+ return ret;
+#if 0
+size_err:
+ mfld_isp_dbg(KERN_ERR, "vm size and page size not equal.\n");
+ return -EINVAL;
+#endif
+status_err2:
+ mutex_unlock(&bo->mutex);
+ mfld_isp_dbg(KERN_ERR, "buffer object already binded.\n");
+ return -EINVAL;
+status_err1:
+ mutex_unlock(&bo->mutex);
+ mfld_isp_dbg(KERN_ERR,
+ "buffer object vm_node or page not allocated.\n");
+ return -EINVAL;
+}
+
+/*
+ * unbind the physical pages with related virtual address space.
+ */
+void hmm_bo_unbind(struct hmm_buffer_object *bo)
+{
+ unsigned int virt;
+ struct hmm_bo_device *bdev;
+#ifdef __MMU_MAP_BLOCK
+ struct list_head *pos;
+ struct __page_block *pgblk;
+ unsigned int blk_pgnr;
+#else
+ unsigned int i;
+#endif
+
+ __check_bo_null_return(bo, (void)0);
+
+ mutex_lock(&bo->mutex);
+
+ __check_bo_status_yes_goto(bo,
+ HMM_BO_PAGE_ALLOCED |
+ HMM_BO_VM_ALLOCED |
+ HMM_BO_BINDED, status_err);
+
+ bdev = bo->bdev;
+
+ virt = bo->vm_node->start;
+
+#ifdef __MMU_MAP_BLOCK
+ list_for_each(pos, &bo->pgblocks) {
+ pgblk = list_entry(pos, struct __page_block, list);
+ blk_pgnr = order_to_nr(pgblk->order);
+ isp_mmu_unmap(&bdev->mmu, virt, blk_pgnr);
+ virt += __pgnr_to_size(blk_pgnr);
+ }
+#else
+ for (i = 0; i < bo->pgnr; i++) {
+ isp_mmu_unmap(&bdev->mmu, virt, 1);
+ virt += __pgnr_to_size(1);
+ }
+#endif
+ /*
+ * flush TLB as the address mapping has been removed and
+ * related TLBs should be invalidated.
+ */
+ isp_mmu_flush_tlb_range(&bdev->mmu, bo->vm_node->start,
+ (bo->pgnr << PAGE_SHIFT));
+
+ bo->status &= (~HMM_BO_BINDED);
+
+ mutex_unlock(&bo->mutex);
+
+ return;
+
+status_err:
+ mutex_unlock(&bo->mutex);
+ mfld_isp_dbg(KERN_ERR,
+ "buffer vm or page not allocated or not binded yet.\n");
+}
+
+int hmm_bo_binded(struct hmm_buffer_object *bo)
+{
+ int ret;
+
+ __check_bo_null_return(bo, 0);
+
+ mutex_lock(&bo->mutex);
+
+ ret = bo->status & HMM_BO_BINDED;
+
+ mutex_unlock(&bo->mutex);
+
+ return ret;
+}
+
+#ifdef HMM_BO_VMAP_SUPPORT
+
+int hmm_bo_vmap(struct hmm_buffer_object *bo)
+{
+ int ret;
+
+ __check_bo_null_return(bo, -EINVAL);
+
+ mutex_lock(&bo->mutex);
+
+ __check_bo_status_yes_goto(bo, HMM_BO_PAGE_ALLOCED, status_err1);
+
+ __check_bo_status_no_goto(bo, HMM_BO_VMAPED, status_err2);
+
+ bo->virt = vmap(bo->pages, bo->pgnr, VM_MAP, PAGE_KERNEL_NOCACHE);
+
+ if (bo->virt)
+ goto vmap_err;
+
+ bo->status |= HMM_BO_VMAPED;
+
+ mutex_unlock(&bo->mutex);
+
+ return 0;
+vmap_err:
+ mutex_unlock(&bo->mutex);
+ mfld_isp_dbg(KERN_ERR, "buffer object vmap failed.\n");
+ return -ENOMEM;
+status_err2:
+ mutex_unlock(&bo->mutex);
+ mfld_isp_dbg(KERN_ERR, "buffer object already vmapped.\n");
+ return -EINVAL;
+status_err1:
+ mutex_unlock(&bo->mutex);
+ mfld_isp_dbg(KERN_ERR, "buffer object has no pages allocated.\n");
+ return -EINVAL;
+}
+
+void hmm_bo_vunmap(struct hmm_buffer_object *bo)
+{
+ __check_bo_null_return(bo, (void)0);
+
+ mutex_lock(&bo->mutex);
+
+ __check_bo_status_yes_goto(bo, HMM_BO_VMAPED, status_err);
+
+ vunmap(bo->virt);
+
+ bo->status &= (~HMM_BO_VMAPED);
+
+ mutex_unlock(&bo->mutex);
+
+status_err:
+ mutex_unlock(&bo->mutex);
+ mfld_isp_dbg(KERN_ERR, "buffer object not vmapped.\n");
+}
+
+int hmm_bo_vmapped(struct hmm_buffer_object *bo)
+{
+ int ret;
+
+ __check_bo_null_return(bo, 0);
+
+ mutex_lock(&bo->mutex);
+
+ ret = bo->status & HMM_BO_VMAPED;
+
+ mutex_unlock(&bo->mutex);
+
+ return ret;
+}
+
+#else
+
+void *hmm_bo_vmap(struct hmm_buffer_object *bo)
+{
+ __check_bo_null_return(bo, NULL);
+
+ return vmap(bo->pages, bo->pgnr, VM_MAP, PAGE_KERNEL_NOCACHE);
+}
+
+#endif
+
+void hmm_bo_ref(struct hmm_buffer_object *bo)
+{
+ __check_bo_null_return(bo, (void)0);
+
+ kref_get(&bo->kref);
+}
+
+#define __kref_to_hmm_bo(kref_ptr) \
+ list_entry((kref_ptr), struct hmm_buffer_object, kref)
+
+static void __kref_hmm_bo_release(struct kref *kref)
+{
+ if (!kref)
+ return;
+
+ hmm_bo_release(__kref_to_hmm_bo(kref));
+}
+
+void hmm_bo_unref(struct hmm_buffer_object *bo)
+{
+ __check_bo_null_return(bo, (void)0);
+
+ kref_put(&bo->kref, __kref_hmm_bo_release);
+}
+
+static void hmm_bo_vm_open(struct vm_area_struct *vma)
+{
+ struct hmm_buffer_object *bo =
+ (struct hmm_buffer_object *)vma->vm_private_data;
+
+ __check_bo_null_return(bo, (void)0);
+
+ hmm_bo_ref(bo);
+
+ mutex_lock(&bo->mutex);
+
+ bo->status |= HMM_BO_MMAPED;
+
+ bo->mmap_count++;
+
+ mutex_unlock(&bo->mutex);
+}
+
+static void hmm_bo_vm_close(struct vm_area_struct *vma)
+{
+ struct hmm_buffer_object *bo =
+ (struct hmm_buffer_object *)vma->vm_private_data;
+
+ __check_bo_null_return(bo, (void)0);
+
+ hmm_bo_unref(bo);
+
+ mutex_lock(&bo->mutex);
+
+ bo->mmap_count--;
+
+ if (!bo->mmap_count) {
+ bo->status &= (~HMM_BO_MMAPED);
+ vma->vm_private_data = NULL;
+ }
+
+ mutex_unlock(&bo->mutex);
+}
+
+static const struct vm_operations_struct hmm_bo_vm_ops = {
+ .open = hmm_bo_vm_open,
+ .close = hmm_bo_vm_close,
+};
+
+/*
+ * mmap the bo to user space.
+ */
+int hmm_bo_mmap(struct vm_area_struct *vma, struct hmm_buffer_object *bo)
+{
+ unsigned int start, end;
+ unsigned int virt;
+ unsigned int pgnr, i;
+ unsigned int pfn;
+
+ __check_bo_null_return(bo, -EINVAL);
+
+ __check_bo_status_yes_goto(bo, HMM_BO_PAGE_ALLOCED, status_err);
+
+ pgnr = bo->pgnr;
+ start = vma->vm_start;
+ end = vma->vm_end;
+
+ /*
+ * check vma's virtual address space size and buffer object's size.
+ * must be the same.
+ */
+ if ((start + __pgnr_to_size(pgnr)) != end) {
+ mfld_isp_dbg(KERN_WARNING,
+ "vma's address space size not equal"
+ " to buffer object's size");
+ return -EINVAL;
+ }
+
+ virt = vma->vm_start;
+ for (i = 0; i < pgnr; i++) {
+ pfn = page_to_pfn(bo->pages[i]);
+ if (remap_pfn_range(vma, virt, pfn, PAGE_SIZE, PAGE_SHARED)) {
+ mfld_isp_dbg(KERN_WARNING, "remap_pfn_range failed: "
+ "virt = 0x%x, pfn = 0x%x,"
+ " mapped_pgnr = %d\n", virt, pfn, 1);
+ return -EINVAL;
+ }
+ virt += PAGE_SIZE;
+ }
+
+ vma->vm_private_data = bo;
+
+ vma->vm_ops = &hmm_bo_vm_ops;
+ vma->vm_flags |= (VM_RESERVED | VM_IO);
+
+ /*
+ * call hmm_bo_vm_open explictly.
+ */
+ hmm_bo_vm_open(vma);
+
+ return 0;
+
+status_err:
+ mfld_isp_dbg(KERN_ERR, "buffer page not allocated yet.\n");
+ return -EINVAL;
+}
diff --git a/drivers/media/video/mfld_ci/mfldisp/hmm/hmm_bo_dev.c b/drivers/media/video/mfld_ci/mfldisp/hmm/hmm_bo_dev.c
new file mode 100644
index 0000000..2d95221
--- /dev/null
+++ b/drivers/media/video/mfld_ci/mfldisp/hmm/hmm_bo_dev.c
@@ -0,0 +1,294 @@
+/*
+ * Support for Medifield PNW Camera Imaging ISP subsystem.
+ *
+ * Copyright (c) 2010 Intel Corporation. All Rights Reserved.
+ *
+ * Copyright (c) 2010 Silicon Hive www.siliconhive.com.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/gfp.h>
+#include <linux/mm.h> /* for GFP_ATOMIC */
+#include <linux/slab.h> /* for kmalloc */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/string.h>
+#include <linux/list.h>
+#include <linux/errno.h>
+
+#include "hmm/hmm_common.h"
+#include "hmm/hmm_bo_dev.h"
+#include "hmm/hmm_bo.h"
+#include "mfldisp_internal.h"
+
+/*
+ * hmm_bo_device functions.
+ */
+int hmm_bo_device_init(struct hmm_bo_device *bdev,
+ struct isp_mmu_driver *mmu_driver,
+ unsigned int vaddr_start, unsigned int size)
+{
+ int ret;
+
+ __check_bodev_null_return(bdev, -EINVAL);
+
+ ret = isp_mmu_init(&bdev->mmu, mmu_driver);
+ if (ret) {
+ mfld_isp_dbg(KERN_ERR, "isp_mmu_init failed.\n");
+ goto isp_mmu_init_err;
+ }
+
+#ifdef USE_DRM_MM
+ ret = drm_mm_init(&bdev->vaddr_space, vaddr_start, size);
+ if (ret) {
+ mfld_isp_dbg(KERN_ERR, "drm_mm_init falied. "
+ "vaddr_start = 0x%x, size = %d\n", vaddr_start,
+ size);
+ goto vm_init_err;
+ }
+ rwlock_init(&bdev->vm_lock);
+#else
+ ret = hmm_vm_init(&bdev->vaddr_space, vaddr_start, size);
+ if (ret) {
+ mfld_isp_dbg(KERN_ERR, "hmm_vm_init falied. "
+ "vaddr_start = 0x%x, size = %d\n", vaddr_start,
+ size);
+ goto vm_init_err;
+ }
+#endif
+
+ INIT_LIST_HEAD(&bdev->free_bo_list);
+ INIT_LIST_HEAD(&bdev->active_bo_list);
+
+ mutex_init(&bdev->fblist_mutex);
+ mutex_init(&bdev->ablist_mutex);
+
+ bdev->flag = HMM_BO_DEVICE_INITED;
+
+ return 0;
+
+vm_init_err:
+ isp_mmu_exit(&bdev->mmu);
+isp_mmu_init_err:
+ return ret;
+}
+
+void hmm_bo_device_exit(struct hmm_bo_device *bdev)
+{
+ __check_bodev_null_return(bdev, (void)0);
+
+ /*
+ * destroy all bos in the bo list, even they are in use.
+ */
+ if (!list_empty(&bdev->active_bo_list))
+ mfld_isp_dbg(KERN_WARNING,
+ "there're still activated bo in use. "
+ "force to free them.\n");
+
+ while (!list_empty(&bdev->active_bo_list))
+ hmm_bo_unref(__list_to_hmm_bo(bdev->active_bo_list.next));
+
+ if (!list_empty(&bdev->free_bo_list))
+ mfld_isp_dbg(KERN_WARNING, "there're still bo in free_bo_list. "
+ "force to free them.\n");
+
+ while (!list_empty(&bdev->free_bo_list))
+ hmm_bo_unref(__list_to_hmm_bo(bdev->free_bo_list.next));
+
+ isp_mmu_exit(&bdev->mmu);
+
+#ifdef USE_DRM_MM
+ drm_mm_takedown(&bdev->vaddr_space);
+#else
+ hmm_vm_clean(&bdev->vaddr_space);
+#endif
+}
+
+int hmm_bo_device_inited(struct hmm_bo_device *bdev)
+{
+ __check_bodev_null_return(bdev, -EINVAL);
+
+ return bdev->flag == HMM_BO_DEVICE_INITED;
+}
+
+/*
+ * find the buffer object with virtual address vaddr.
+ * return NULL if no such buffer object found.
+ */
+struct hmm_buffer_object *hmm_bo_device_search_start(struct hmm_bo_device *bdev,
+ unsigned int vaddr)
+{
+ struct list_head *pos;
+ struct hmm_buffer_object *bo;
+
+ __check_bodev_null_return(bdev, NULL);
+
+ mutex_lock(&bdev->ablist_mutex);
+ list_for_each(pos, &bdev->active_bo_list) {
+ bo = __list_to_hmm_bo(pos);
+ /* pass bo which has no vm_node allocated */
+ if (!hmm_bo_vm_allocated(bo))
+ continue;
+ if (bo->vm_node->start == vaddr)
+ goto found;
+ }
+ mutex_unlock(&bdev->ablist_mutex);
+ return NULL;
+found:
+ mutex_unlock(&bdev->ablist_mutex);
+ return bo;
+}
+
+static int __in_range(unsigned int start, unsigned int size, unsigned int addr)
+{
+ return (start <= addr) && (start + size > addr);
+}
+
+struct hmm_buffer_object *hmm_bo_device_search_in_range(struct hmm_bo_device
+ *bdev,
+ unsigned int vaddr)
+{
+ struct list_head *pos;
+ struct hmm_buffer_object *bo;
+
+ __check_bodev_null_return(bdev, NULL);
+
+ mutex_lock(&bdev->fblist_mutex);
+ list_for_each(pos, &bdev->active_bo_list) {
+ bo = __list_to_hmm_bo(pos);
+ /* pass bo which has no vm_node allocated */
+ if (!hmm_bo_vm_allocated(bo))
+ continue;
+ if (__in_range(bo->vm_node->start, bo->vm_node->size, vaddr))
+ goto found;
+ }
+ mutex_unlock(&bdev->fblist_mutex);
+ return NULL;
+found:
+ mutex_unlock(&bdev->fblist_mutex);
+ return bo;
+}
+
+/*
+ * find a buffer object with pgnr pages from free_bo_list and
+ * activate it (remove from free_bo_list and add to
+ * active_bo_list)
+ *
+ * return NULL if no such buffer object found.
+ */
+struct hmm_buffer_object *hmm_bo_device_get_bo(struct hmm_bo_device *bdev,
+ unsigned int pgnr)
+{
+ struct list_head *pos;
+ struct hmm_buffer_object *bo;
+
+ __check_bodev_null_return(bdev, NULL);
+
+ mutex_lock(&bdev->fblist_mutex);
+ list_for_each(pos, &bdev->free_bo_list) {
+ bo = __list_to_hmm_bo(pos);
+ if (bo->pgnr == pgnr)
+ goto found;
+ }
+ mutex_unlock(&bdev->fblist_mutex);
+ return NULL;
+found:
+ list_del(&bo->list);
+ mutex_unlock(&bdev->fblist_mutex);
+
+ mutex_lock(&bdev->ablist_mutex);
+ list_add(&bo->list, &bdev->active_bo_list);
+ mutex_unlock(&bdev->ablist_mutex);
+
+ return bo;
+}
+
+/*
+ * destroy all buffer objects in the free_bo_list.
+ */
+void hmm_bo_device_destroy_free_bo_list(struct hmm_bo_device *bdev)
+{
+ struct hmm_buffer_object *bo;
+
+ __check_bodev_null_return(bdev, (void)0);
+
+ mutex_lock(&bdev->fblist_mutex);
+ while (!list_empty(&bdev->free_bo_list)) {
+ bo = list_first_entry(&bdev->free_bo_list,
+ struct hmm_buffer_object, list);
+
+ list_del(&bo->list);
+ hmm_bo_unref(bo);
+ }
+ mutex_unlock(&bdev->fblist_mutex);
+}
+
+/*
+ * destroy buffer object with start virtual address vaddr.
+ */
+void hmm_bo_device_destroy_free_bo_addr(struct hmm_bo_device *bdev,
+ unsigned int vaddr)
+{
+ struct list_head *pos;
+ struct hmm_buffer_object *bo;
+
+ __check_bodev_null_return(bdev, (void)0);
+
+ mutex_lock(&bdev->fblist_mutex);
+ list_for_each(pos, &bdev->free_bo_list) {
+ bo = __list_to_hmm_bo(pos);
+ /* pass bo which has no vm_node allocated */
+ if (!hmm_bo_vm_allocated(bo))
+ continue;
+ if (bo->vm_node->start == vaddr)
+ goto found;
+ }
+ mutex_unlock(&bdev->fblist_mutex);
+ return;
+found:
+ list_del(&bo->list);
+ mutex_unlock(&bdev->fblist_mutex);
+ hmm_bo_unref(bo);
+}
+
+/*
+ * destroy all buffer objects with pgnr pages.
+ */
+void hmm_bo_device_destroy_free_bo_size(struct hmm_bo_device *bdev,
+ unsigned int pgnr)
+{
+ struct list_head *pos;
+ struct hmm_buffer_object *bo;
+
+ __check_bodev_null_return(bdev, (void)0);
+
+retry:
+ mutex_lock(&bdev->fblist_mutex);
+ list_for_each(pos, &bdev->free_bo_list) {
+ bo = __list_to_hmm_bo(pos);
+ if (bo->pgnr == pgnr)
+ goto found;
+ }
+ mutex_unlock(&bdev->fblist_mutex);
+ return;
+found:
+ list_del(&bo->list);
+ mutex_unlock(&bdev->fblist_mutex);
+ hmm_bo_unref(bo);
+ goto retry;
+}
diff --git a/drivers/media/video/mfld_ci/mfldisp/hmm/hmm_vm.c b/drivers/media/video/mfld_ci/mfldisp/hmm/hmm_vm.c
new file mode 100644
index 0000000..8080967
--- /dev/null
+++ b/drivers/media/video/mfld_ci/mfldisp/hmm/hmm_vm.c
@@ -0,0 +1,204 @@
+/*
+ * Support for Medifield PNW Camera Imaging ISP subsystem.
+ *
+ * Copyright (c) 2010 Intel Corporation. All Rights Reserved.
+ *
+ * Copyright (c) 2010 Silicon Hive www.siliconhive.com.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <asm/page.h>
+
+#include "mmu/isp_mmu.h"
+#include "hmm/hmm_vm.h"
+#include "hmm/hmm_common.h"
+#include "mfldisp_internal.h"
+
+static unsigned int vm_node_end(unsigned int start, unsigned int pgnr)
+{
+ return start + __pgnr_to_size(pgnr);
+}
+
+static int addr_in_vm_node(unsigned int addr,
+ struct hmm_vm_node *node)
+{
+ return (addr >= node->start) && (addr < (node->start + node->size));
+}
+
+int hmm_vm_init(struct hmm_vm *vm, unsigned int start,
+ unsigned int size)
+{
+ if (!vm)
+ return -1;
+
+ vm->start = start;
+ vm->pgnr = __size_to_pgnr_ceil(size);
+ vm->size = __pgnr_to_size(vm->pgnr);
+
+ INIT_LIST_HEAD(&vm->vm_node_list);
+ spin_lock_init(&vm->lock);
+
+ return 0;
+}
+
+void hmm_vm_clean(struct hmm_vm *vm)
+{
+ if (!vm)
+ return;
+
+ while (!list_empty(&vm->vm_node_list))
+ hmm_vm_free_node(__hmm_vm_node(vm->vm_node_list.next));
+}
+
+static struct hmm_vm_node *__alloc_hmm_vm_node(unsigned int start,
+ unsigned int pgnr,
+ struct hmm_vm *vm)
+{
+ struct hmm_vm_node *node;
+
+ node = kmalloc(sizeof(struct hmm_vm_node), GFP_KERNEL);
+ if (!node) {
+ mfld_isp_dbg(KERN_ERR, "out of memory.\n");
+ return NULL;
+ }
+
+ INIT_LIST_HEAD(&node->list);
+ node->start = start;
+ node->pgnr = pgnr;
+ node->size = __pgnr_to_size(pgnr);
+ node->vm = vm;
+
+ return node;
+}
+
+struct hmm_vm_node *hmm_vm_alloc_node(struct hmm_vm *vm, unsigned int pgnr)
+{
+ struct list_head *head, *p, *pos;
+ struct hmm_vm_node *node, *cur, *next;
+ unsigned int vm_start, vm_end;
+ unsigned int addr;
+ unsigned int size;
+
+ if (!vm)
+ return NULL;
+
+ vm_start = vm->start;
+ vm_end = vm_node_end(vm->start, vm->pgnr);
+ size = __pgnr_to_size(pgnr);
+
+ addr = vm_start;
+ pos = head = &vm->vm_node_list;
+
+ spin_lock(&vm->lock);
+
+ /*
+ * if list is empty, the loop code will not be executed.
+ */
+ list_for_each(p, head) {
+ pos = p;
+ cur = __hmm_vm_node(pos);
+ addr = vm_node_end(cur->start, cur->pgnr);
+ if (pos->next != head) {
+ next = __hmm_vm_node(pos->next);
+ if ((next->start - addr) >= size)
+ goto found;
+ }
+ }
+
+ if (addr + size > vm_end) {
+ mfld_isp_dbg(KERN_INFO, "no enough virtual address space.\n");
+ goto failed;
+ }
+
+found:
+
+ node = __alloc_hmm_vm_node(addr, pgnr, vm);
+ if (!node)
+ goto failed;
+
+ list_add(&node->list, pos);
+
+ spin_unlock(&vm->lock);
+
+ return node;
+failed:
+ mfld_isp_dbg(KERN_ERR, "failed...\n");
+ spin_unlock(&vm->lock);
+
+ return NULL;
+}
+
+void hmm_vm_free_node(struct hmm_vm_node *node)
+{
+ if (node) {
+ struct hmm_vm *vm = node->vm;
+ spin_lock(&vm->lock);
+ list_del(&node->list);
+ spin_unlock(&vm->lock);
+ kfree(node);
+ }
+}
+
+struct hmm_vm_node *hmm_vm_find_node_start(struct hmm_vm *vm, unsigned int addr)
+{
+ struct list_head *pos;
+ struct hmm_vm_node *node;
+
+ if (!vm)
+ return NULL;
+
+ spin_lock(&vm->lock);
+
+ list_for_each(pos, &vm->vm_node_list) {
+ node = __hmm_vm_node(pos);
+ if (node->start == addr)
+ goto found;
+ }
+
+ spin_unlock(&vm->lock);
+ return NULL;
+found:
+ spin_unlock(&vm->lock);
+ return node;
+}
+
+struct hmm_vm_node *hmm_vm_find_node_in_range(struct hmm_vm *vm,
+ unsigned int addr)
+{
+ struct list_head *pos;
+ struct hmm_vm_node *node;
+
+ if (!vm)
+ return NULL;
+
+ spin_lock(&vm->lock);
+
+ list_for_each(pos, &vm->vm_node_list) {
+ node = __hmm_vm_node(pos);
+ if (addr_in_vm_node(addr, node))
+ goto found;
+ }
+
+ spin_unlock(&vm->lock);
+ return NULL;
+found:
+ spin_unlock(&vm->lock);
+ return node;
+}
diff --git a/drivers/media/video/mfld_ci/mfldisp/hrt/hive_isp_css_ddr_hrt.c b/drivers/media/video/mfld_ci/mfldisp/hrt/hive_isp_css_ddr_hrt.c
new file mode 100644
index 0000000..766fab1
--- /dev/null
+++ b/drivers/media/video/mfld_ci/mfldisp/hrt/hive_isp_css_ddr_hrt.c
@@ -0,0 +1,317 @@
+/*
+ * Support for Medifield PNW Camera Imaging ISP subsystem.
+ *
+ * Copyright (c) 2010 Intel Corporation. All Rights Reserved.
+ *
+ * Copyright (c) 2010 Silicon Hive www.siliconhive.com.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+#include <vector.h>
+#ifdef HRT_CSIM
+#include <error.h>
+#endif
+#include "hive_isp_css_defs.h"
+#include "hive_isp_css_ddr_hrt.h"
+#include "hive_isp_css_mm_hrt.h"
+#include "mfldisp_internal.h"
+#define BYTES_PER_XWORD (HIVE_ISP_DDR_WORD_BITS/8)
+#define ceil_div(a, b) (((a)+(b)-1)/(b))
+
+struct dma_spec_s {
+ unsigned int num_elems;
+ unsigned int elem_bits;
+};
+
+static struct dma_spec_s specs[] = HIVE_ISP_DDR_DMA_SPECS;
+static unsigned int num_specs = sizeof(specs) / sizeof(*specs);
+
+static unsigned int
+dma_num_elems_to_elem_width(unsigned int num_elems, unsigned int *bits)
+{
+ unsigned int i;
+ for (i = 0; i < num_specs; i++) {
+ if (specs[i].num_elems == num_elems) {
+ *bits = specs[i].elem_bits;
+ return 1;
+ }
+ }
+ mfld_isp_dbg(KERN_WARNING,
+ "cannot translate number of elements (%d) to"
+ "element bits in dma",
+ num_elems);
+ return 0;
+}
+
+static unsigned int
+dma_elem_width_to_num_elems(unsigned int bits, unsigned int *num_elems)
+{
+ unsigned int i;
+ for (i = 0; i < num_specs; i++) {
+ if (specs[i].elem_bits == bits) {
+ *num_elems = specs[i].num_elems;
+ return 1;
+ }
+ }
+ mfld_isp_dbg(KERN_WARNING,
+ "cannot translate element width (%u) to number"
+ " of elements per word",
+ *num_elems);
+ return 0;
+}
+
+unsigned int
+hrt_isp_css_stride_of_image_in_ddr(unsigned int width,
+ unsigned int bits_per_element)
+{
+ unsigned int elems_per_xword, xwords_per_line;
+
+ if (dma_elem_width_to_num_elems(bits_per_element, &elems_per_xword) ==
+ 0)
+ return 0;
+ xwords_per_line = ceil_div(width, elems_per_xword);
+ return xwords_per_line * BYTES_PER_XWORD;
+}
+
+unsigned int
+hrt_isp_css_sizeof_image_in_ddr(unsigned int width,
+ unsigned int height,
+ unsigned int bits_per_element)
+{
+ return hrt_isp_css_stride_of_image_in_ddr(width,
+ bits_per_element) * height;
+}
+
+void *
+hrt_isp_css_alloc_image_in_ddr(unsigned int width,
+ unsigned int height,
+ unsigned int elems_per_xword)
+{
+ unsigned int elem_bits;
+ size_t bytes;
+
+ if (!dma_num_elems_to_elem_width(elems_per_xword, &elem_bits))
+ return NULL;
+ bytes = hrt_isp_css_sizeof_image_in_ddr(width, height, elem_bits);
+ return hrt_isp_css_mm_alloc(bytes);
+}
+
+void *
+hrt_isp_css_calloc_image_in_ddr(unsigned int width,
+ unsigned int height,
+ unsigned int elems_per_xword)
+{
+ unsigned int elem_bits;
+ size_t bytes;
+
+ if (!dma_num_elems_to_elem_width(elems_per_xword, &elem_bits))
+ return NULL;
+ bytes = hrt_isp_css_sizeof_image_in_ddr(width, height, elem_bits);
+ return hrt_isp_css_mm_calloc(bytes);
+}
+
+unsigned int
+hrt_isp_css_read_image_from_ddr(unsigned short *img_buf,
+ unsigned int width,
+ unsigned int height,
+ unsigned int elems_per_xword,
+ unsigned int sign_extend,
+ void *virt_addr)
+{
+ unsigned int i, j, k, elem_bits, xwords_per_line;
+ char xword_buf[BYTES_PER_XWORD];
+ /* this is the maximum number of elements in an xword */
+ int elem_buf[BYTES_PER_XWORD];
+
+ if (!dma_num_elems_to_elem_width(elems_per_xword, &elem_bits))
+ return 0;
+ if (elem_bits == 0)
+ return 0;
+ xwords_per_line = ceil_div(width, elems_per_xword);
+
+ for (i = 0; i < height; i++) {
+ unsigned int elems_in_line = width;
+ for (j = 0; j < xwords_per_line; j++) {
+ unsigned int elems_in_word = elems_per_xword;
+ if (elems_in_word > elems_in_line)
+ elems_in_word = elems_in_line;
+ hrt_isp_css_mm_load(virt_addr, xword_buf,
+ BYTES_PER_XWORD);
+ _hrt_decode_vector(xword_buf, elem_buf, elem_bits,
+ elem_bits, elems_per_xword,
+ sign_extend);
+ for (k = 0; k < elems_in_word;
+ k++, elems_in_line--, img_buf++)
+ *img_buf = (unsigned short)elem_buf[k];
+ virt_addr += BYTES_PER_XWORD;
+ }
+ }
+
+ return 1;
+}
+
+unsigned int
+hrt_isp_css_write_image_to_ddr(const unsigned short *img_buf,
+ unsigned int width,
+ unsigned int height,
+ unsigned int elems_per_xword,
+ unsigned int sign_extend,
+ void *virt_addr)
+{
+ unsigned int i, j, k, elem_bits, xwords_per_line;
+ char xword_buf[BYTES_PER_XWORD];
+ int elem_buf[BYTES_PER_XWORD];
+
+ if (!dma_num_elems_to_elem_width(elems_per_xword, &elem_bits))
+ return 0;
+ if (elem_bits == 0)
+ return 0;
+ xwords_per_line = ceil_div(width, elems_per_xword);
+
+ for (i = 0; i < height; i++) {
+ unsigned int elems_in_line = width;
+ for (j = 0; j < xwords_per_line; j++) {
+ unsigned int elems_in_word = elems_per_xword;
+ if (elems_in_word > elems_in_line)
+ elems_in_word = elems_in_line;
+ for (k = 0; k < elems_in_word;
+ k++, elems_in_line--, img_buf++)
+ elem_buf[k] = *img_buf;
+ _hrt_encode_vector(elem_buf, xword_buf, elem_bits,
+ elem_bits, elems_per_xword,
+ sign_extend);
+ hrt_isp_css_mm_store(virt_addr, xword_buf,
+ BYTES_PER_XWORD);
+ virt_addr += BYTES_PER_XWORD;
+ }
+ }
+
+ return 1;
+}
+
+void *
+hrt_isp_css_alloc_gdc_lut_in_ddr(void)
+{
+ return hrt_isp_css_mm_alloc(HRT_GDC_LUT_BYTES);
+}
+
+void
+hrt_isp_css_write_gdc_lut_to_ddr(short lut[4][HRT_GDC_N], void *virt_addr)
+{
+ unsigned int i;
+ for (i = 0; i < HRT_GDC_N; i++) {
+ unsigned int entry_0 = lut[0][i] & HRT_GDC_BCI_COEF_MASK,
+ entry_1 = lut[1][i] & HRT_GDC_BCI_COEF_MASK,
+ entry_2 = lut[2][i] & HRT_GDC_BCI_COEF_MASK,
+ entry_3 = lut[3][i] & HRT_GDC_BCI_COEF_MASK,
+ word_0 = entry_0 | (entry_1 << HRT_GDC_BCI_COEF_BITS),
+ word_1 = entry_2 | (entry_3 << HRT_GDC_BCI_COEF_BITS);
+ hrt_isp_css_mm_store_int(virt_addr, word_0);
+ virt_addr += 4;
+ hrt_isp_css_mm_store_int(virt_addr, word_1);
+ virt_addr += 4;
+ }
+}
+
+unsigned int
+hrt_isp_css_read_unsigned(unsigned short *target,
+ unsigned int width,
+ unsigned int height,
+ unsigned int source_bits_per_element,
+ void *source)
+{
+ unsigned int elems_per_xword;
+
+ if (dma_elem_width_to_num_elems(source_bits_per_element,
+ &elems_per_xword) == 0)
+ return 0;
+ return hrt_isp_css_read_image_from_ddr(target, width, height,
+ elems_per_xword, 0, source);
+}
+
+unsigned int
+hrt_isp_css_read_signed(short *target,
+ unsigned int width,
+ unsigned int height,
+ unsigned int source_bits_per_element,
+ void *source)
+{
+ unsigned int elems_per_xword;
+
+ if (dma_elem_width_to_num_elems(source_bits_per_element,
+ &elems_per_xword) == 0)
+ return 0;
+ return hrt_isp_css_read_image_from_ddr((unsigned short *)target, width,
+ height, elems_per_xword, 1,
+ source);
+}
+
+unsigned int
+hrt_isp_css_write_unsigned(const unsigned short *source,
+ unsigned int width,
+ unsigned int height,
+ unsigned int target_bits_per_element,
+ void *target)
+{
+ unsigned int elems_per_xword;
+
+ if (dma_elem_width_to_num_elems(target_bits_per_element,
+ &elems_per_xword) == 0)
+ return 0;
+ return hrt_isp_css_write_image_to_ddr(source, width, height,
+ elems_per_xword, 0, target);
+}
+
+unsigned int
+hrt_isp_css_write_signed(const short *source,
+ unsigned int width,
+ unsigned int height,
+ unsigned int target_bits_per_element,
+ void *target)
+{
+ unsigned int elems_per_xword;
+
+ if (dma_elem_width_to_num_elems(target_bits_per_element,
+ &elems_per_xword) == 0)
+ return 0;
+ return hrt_isp_css_write_image_to_ddr((const unsigned short *)source,
+ width, height, elems_per_xword, 1,
+ target);
+}
+
+void *
+hrt_isp_css_alloc(unsigned int width,
+ unsigned int height,
+ unsigned int bits_per_element)
+{
+ size_t bytes;
+
+ bytes =
+ hrt_isp_css_sizeof_image_in_ddr(width, height, bits_per_element);
+ return hrt_isp_css_mm_alloc(bytes);
+}
+
+void *
+hrt_isp_css_calloc(unsigned int width,
+ unsigned int height,
+ unsigned int bits_per_element)
+{
+ size_t bytes;
+
+ bytes =
+ hrt_isp_css_sizeof_image_in_ddr(width, height, bits_per_element);
+ return hrt_isp_css_mm_calloc(bytes);
+}
diff --git a/drivers/media/video/mfld_ci/mfldisp/hrt/hive_isp_css_mm_hrt.c b/drivers/media/video/mfld_ci/mfldisp/hrt/hive_isp_css_mm_hrt.c
new file mode 100644
index 0000000..e11cd9f
--- /dev/null
+++ b/drivers/media/video/mfld_ci/mfldisp/hrt/hive_isp_css_mm_hrt.c
@@ -0,0 +1,138 @@
+/*
+ * Support for Medifield PNW Camera Imaging ISP subsystem.
+ *
+ * Copyright (c) 2010 Intel Corporation. All Rights Reserved.
+ *
+ * Copyright (c) 2010 Silicon Hive www.siliconhive.com.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+#include <hmm/hmm.h>
+
+/* not sure if we need these two for page related macros,
+ * need to double check */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include "mfldisp_internal.h"
+
+#define __page_align(size) (((size) + (PAGE_SIZE-1)) & (~(PAGE_SIZE-1)))
+
+static unsigned init_done;
+void hrt_isp_css_mm_init(void)
+{
+ mfld_isp_dbg(KERN_DEBUG, "hrt_isp_css_mm_init...\n");
+ hmm_init();
+ init_done = 1;
+ mfld_isp_dbg(KERN_DEBUG, "hrt_isp_css_mm_init... done\n");
+}
+
+void hrt_isp_css_mm_load(void *virt_addr, void *data, size_t bytes)
+{
+ hmm_load(virt_addr, data, bytes);
+}
+
+void hrt_isp_css_mm_store(void *virt_addr, const void *data, size_t bytes)
+{
+ hmm_store(virt_addr, data, bytes);
+}
+
+void hrt_isp_css_mm_free(void *virt_addr)
+{
+ hmm_free(virt_addr);
+}
+
+void hrt_isp_css_mm_clear(void)
+{
+ if (init_done) {
+ hmm_cleanup();
+ init_done = 0;
+ }
+}
+
+static unsigned int my_userptr, my_num_pages;
+void hrt_isp_css_mm_set_user_ptr(unsigned int userptr, unsigned int num_pages)
+{
+ my_userptr = userptr;
+ my_num_pages = num_pages;
+}
+
+void *hrt_isp_css_mm_alloc(size_t bytes)
+{
+ if (!init_done)
+ hrt_isp_css_mm_init();
+
+ if (my_userptr == 0)
+ return (void *)hmm_alloc(bytes, HMM_BO_PRIVATE, 0, 0);
+ else {
+ if (my_num_pages < ((__page_align(bytes)) >> PAGE_SHIFT))
+ mfld_isp_dbg(KERN_ERR, "user space memory size is less"
+ " than the expected size..\n");
+ else if (my_num_pages > ((__page_align(bytes)) >> PAGE_SHIFT))
+ mfld_isp_dbg(KERN_ERR, "user space memory size is"
+ " large than the expected size..\n");
+ return (void *)hmm_alloc(bytes, HMM_BO_USER, 0, my_userptr);
+ }
+}
+
+void *hrt_isp_css_mm_calloc(size_t bytes)
+{
+ void *ptr = hrt_isp_css_mm_alloc(bytes);
+ if (!ptr)
+ hmm_set(ptr, 0, bytes);
+ return ptr;
+}
+
+int hrt_isp_css_mm_load_int(void *virt_addr)
+{
+ int v = 0;
+ hrt_isp_css_mm_load(virt_addr, &v, sizeof(v));
+ return v;
+}
+
+short hrt_isp_css_mm_load_short(void *virt_addr)
+{
+ short v = 0;
+ hrt_isp_css_mm_load(virt_addr, &v, sizeof(v));
+ return v;
+}
+
+char hrt_isp_css_mm_load_char(void *virt_addr)
+{
+ char v = 0;
+ hrt_isp_css_mm_load(virt_addr, &v, sizeof(v));
+ return v;
+}
+
+void hrt_isp_css_mm_store_char(void *virt_addr, char data)
+{
+ hrt_isp_css_mm_store(virt_addr, &data, sizeof(data));
+}
+
+void hrt_isp_css_mm_store_short(void *virt_addr, short data)
+{
+ hrt_isp_css_mm_store(virt_addr, &data, sizeof(data));
+}
+
+void hrt_isp_css_mm_store_int(void *virt_addr, int data)
+{
+ hrt_isp_css_mm_store(virt_addr, &data, sizeof(data));
+}
+
+void *hrt_isp_css_virt_to_phys(void *virt_addr)
+{
+ return (void *)hmm_virt_to_phys(virt_addr);
+}
diff --git a/drivers/media/video/mfld_ci/mfldisp/include/hmm/hmm.h b/drivers/media/video/mfld_ci/mfldisp/include/hmm/hmm.h
new file mode 100644
index 0000000..05c5e35
--- /dev/null
+++ b/drivers/media/video/mfld_ci/mfldisp/include/hmm/hmm.h
@@ -0,0 +1,71 @@
+/*
+ * Support for Medifield PNW Camera Imaging ISP subsystem.
+ *
+ * Copyright (c) 2010 Intel Corporation. All Rights Reserved.
+ *
+ * Copyright (c) 2010 Silicon Hive www.siliconhive.com.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#ifndef __HMM_H__
+#define __HMM_H__
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+
+#include "hmm/hmm_bo.h"
+
+int hmm_init(void);
+void hmm_cleanup(void);
+
+void *hmm_alloc(size_t bytes, enum hmm_bo_type type,
+ int from_highmem, unsigned int userptr);
+void hmm_free(void *ptr);
+int hmm_load(void *virt, void *data, unsigned int bytes);
+int hmm_store(void *virt, const void *data, unsigned int bytes);
+int hmm_set(void *virt, char c, unsigned int bytes);
+
+/*
+ * get kernel memory physical address from ISP virtual address.
+ */
+unsigned int hmm_virt_to_phys(void *virt);
+
+/*
+ * map ISP memory starts with virt to kernel virtual address
+ * by using vmap. return NULL if failed.
+ *
+ * !! user needs to use vunmap to unmap it manually before calling
+ * hmm_free to free the memory.
+ *
+ * virt must be the start address of ISP memory (return by hmm_alloc),
+ * do not pass any other address.
+ */
+void *hmm_vmap(void *virt);
+
+/*
+ * map ISP memory starts with virt to specific vma.
+ *
+ * used for mmap operation.
+ *
+ * virt must be the start address of ISP memory (return by hmm_alloc),
+ * do not pass any other address.
+ */
+int hmm_mmap(struct vm_area_struct *vma, void *virt);
+
+#endif
diff --git a/drivers/media/video/mfld_ci/mfldisp/include/hmm/hmm_bo.h b/drivers/media/video/mfld_ci/mfldisp/include/hmm/hmm_bo.h
new file mode 100644
index 0000000..50fc75b
--- /dev/null
+++ b/drivers/media/video/mfld_ci/mfldisp/include/hmm/hmm_bo.h
@@ -0,0 +1,313 @@
+/*
+ * Support for Medifield PNW Camera Imaging ISP subsystem.
+ *
+ * Copyright (c) 2010 Intel Corporation. All Rights Reserved.
+ *
+ * Copyright (c) 2010 Silicon Hive www.siliconhive.com.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#ifndef __HMM_BO_H__
+#define __HMM_BO_H__
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/kref.h>
+
+#include "hmm_common.h"
+
+#ifdef USE_DRM_MM
+#include <drm/drm_mm.h>
+#else
+#include "hmm/hmm_vm.h"
+#endif
+
+#define __check_bo_null_return(bo, exp) \
+ __check_null_return(bo, exp, "NULL hmm buffer object.\n")
+
+struct hmm_bo_device;
+
+/*
+ * buffer object type.
+ *
+ * HMM_BO_PRIVATE:
+ * pages are allocated by driver itself.
+ * HMM_BO_SHARE:
+ * pages are allocated by other component. currently: video driver.
+ * HMM_BO_USER:
+ * pages are allocated in user space process.
+ *
+ */
+enum hmm_bo_type {
+ HMM_BO_PRIVATE,
+ HMM_BO_SHARE,
+ HMM_BO_USER,
+};
+
+#define HMM_BO_VM_ALLOCED 0x1
+#define HMM_BO_PAGE_ALLOCED 0x2
+#define HMM_BO_BINDED 0x4
+#define HMM_BO_MMAPED 0x8
+
+#ifdef HMM_BO_VMAP_SUPPORT
+#define HMM_BO_VMAPED 0x10
+#endif
+
+#define HMM_BO_ACTIVE 0x1000
+
+struct __page_block {
+ struct list_head list;
+ struct page *pages;
+ int order;
+};
+
+struct hmm_buffer_object {
+ struct hmm_bo_device *bdev;
+ struct list_head list;
+ struct kref kref;
+
+ /* mutex protecting this BO */
+ struct mutex mutex;
+
+ enum hmm_bo_type type;
+
+#ifdef HMM_BO_VMAP_SUPPORT
+ /*
+ * kernel virtual address got by vmap, used by kernel
+ * to access the buffer object's pages contiguously.
+ */
+ void *virt;
+#endif
+
+ struct list_head pgblocks;
+ struct page **pages; /* physical pages */
+ unsigned int pgnr; /* page number */
+ int from_highmem;
+#ifdef CI_VA_SHARE_BUFFER
+ /* for share buffer created by video driver */
+ unsigned int handle;
+#endif
+ int mmap_count;
+#ifdef USE_DRM_MM
+ /* protected by bdev->vm_lock */
+ struct drm_mm_node *vm_node;
+#else
+ struct hmm_vm_node *vm_node;
+#endif
+ int status;
+
+ /*
+ * release callback for releasing buffer object.
+ *
+ * usually set to the release function to release the
+ * upper level buffer object which has hmm_buffer_object
+ * embedded in. if the hmm_buffer_object is dynamically
+ * created by hmm_bo_create, release will set to kfree.
+ *
+ */
+ void (*release)(struct hmm_buffer_object *bo);
+};
+
+#define __list_to_hmm_bo(list_ptr) \
+ list_entry((list_ptr), struct hmm_buffer_object, list)
+
+/*
+ * use this function to initialize pre-allocated hmm_buffer_object.
+ *
+ * the hmm_buffer_object use reference count to manage its life cycle.
+ *
+ * bo->kref is inited to 1.
+ *
+ * use hmm_bo_ref/hmm_bo_unref increase/decrease the reference count,
+ * and hmm_bo_unref will free resource of buffer object (but not the
+ * buffer object itself as it can be both pre-allocated or dynamically
+ * allocated) when reference reaches 0.
+ *
+ * see detailed description of hmm_bo_ref/hmm_bo_unref below.
+ *
+ * as hmm_buffer_object may be used as an embedded object in an upper
+ * level object, a release callback must be provided. if it is
+ * embedded in upper level object, set release call back to release
+ * function of that object. if no upper level object, set release
+ * callback to NULL.
+ *
+ * ex:
+ * struct hmm_buffer_object bo;
+ * hmm_bo_init(bdev, &bo, pgnr, NULL);
+ *
+ * or
+ * struct my_buffer_object {
+ * struct hmm_buffer_object bo;
+ * ...
+ * };
+ *
+ * void my_buffer_release(struct hmm_buffer_object *bo)
+ * {
+ * struct my_buffer_object *my_bo =
+ * container_of(bo, struct my_buffer_object, bo);
+ *
+ * ... // release resource in my_buffer_object
+ *
+ * kfree(my_bo);
+ * }
+ *
+ * struct my_buffer_object *my_bo =
+ * kmalloc(sizeof(*my_bo), GFP_KERNEL);
+ *
+ * hmm_bo_init(bdev, &my_bo->bo, pgnr, my_buffer_release);
+ * ...
+ *
+ * hmm_bo_unref(&my_bo->bo);
+ */
+int hmm_bo_init(struct hmm_bo_device *bdev,
+ struct hmm_buffer_object *bo,
+ unsigned int pgnr,
+ void (*release)(struct hmm_buffer_object *));
+
+/*
+ * use these functions to dynamically alloc hmm_buffer_object.
+ *
+ * hmm_bo_init will called for that allocated buffer object, and
+ * the release callback is set to kfree.
+ *
+ * ex:
+ * hmm_buffer_object *bo = hmm_bo_create(bdev, pgnr);
+ * ...
+ * hmm_bo_unref(bo);
+ */
+struct hmm_buffer_object *hmm_bo_create(struct hmm_bo_device *bdev,
+ int pgnr);
+
+/*
+ * increse buffer object reference.
+ */
+void hmm_bo_ref(struct hmm_buffer_object *bo);
+
+/*
+ * decrese buffer object reference. if reference reaches 0,
+ * release function of the buffer object will be called.
+ *
+ * this call is also used to release hmm_buffer_object or its
+ * upper level object with it embedded in. you need to call
+ * this function when it is no longer used.
+ *
+ * Note:
+ *
+ * user dont need to care about internal resource release of
+ * the buffer object in the release callback, it will be
+ * handled internally.
+ *
+ * this call will only release internal resource of the buffer
+ * object but will not free the buffer object itself, as the
+ * buffer object can be both pre-allocated statically or
+ * dynamically allocated. so user need to deal with the release
+ * of the buffer object itself manually. below example shows
+ * the normal case of using the buffer object.
+ *
+ * struct hmm_buffer_object *bo = hmm_bo_create(bdev, pgnr);
+ * ......
+ * hmm_bo_unref(bo);
+ *
+ * or:
+ *
+ * struct hmm_buffer_object bo;
+ *
+ * hmm_bo_init(bdev, &bo, pgnr, NULL);
+ * ...
+ * hmm_bo_unref(&bo);
+ */
+void hmm_bo_unref(struct hmm_buffer_object *bo);
+
+
+/*
+ * put buffer object to unactivated status, meaning put it into
+ * bo->bdev->free_bo_list, but not destroy it.
+ *
+ * this can be used to instead of hmm_bo_destroy if there are
+ * lots of petential hmm_bo_init/hmm_bo_destroy operations with
+ * the same buffer object size. using this with hmm_bo_device_get_bo
+ * can improve performace as lots of memory allocation/free are
+ * avoided..
+ */
+void hmm_bo_unactivate(struct hmm_buffer_object *bo);
+int hmm_bo_activated(struct hmm_buffer_object *bo);
+
+/*
+ * allocate/free virtual address space for the bo.
+ */
+int hmm_bo_alloc_vm(struct hmm_buffer_object *bo);
+void hmm_bo_free_vm(struct hmm_buffer_object *bo);
+int hmm_bo_vm_allocated(struct hmm_buffer_object *bo);
+
+/*
+ * allocate/free physical pages for the bo. will try to alloc mem
+ * from highmem if from_highmem is set, and type indicate that the
+ * pages will be allocated by using video driver (for share buffer)
+ * or by ISP driver itself.
+ */
+int hmm_bo_alloc_pages(struct hmm_buffer_object *bo,
+ enum hmm_bo_type type, int from_highmem,
+ unsigned int userptr);
+void hmm_bo_free_pages(struct hmm_buffer_object *bo);
+int hmm_bo_page_allocated(struct hmm_buffer_object *bo);
+
+/*
+ * get physical page info of the bo.
+ */
+int hmm_bo_get_page_info(struct hmm_buffer_object *bo,
+ struct page ***pages, int *pgnr);
+
+/*
+ * bind/unbind the physical pages to a virtual address space.
+ */
+int hmm_bo_bind(struct hmm_buffer_object *bo);
+void hmm_bo_unbind(struct hmm_buffer_object *bo);
+int hmm_bo_binded(struct hmm_buffer_object *bo);
+
+#ifdef HMM_BO_VMAP_SUPPORT
+/*
+ * map/unmap buffer object's physical pages to contiguous kernel
+ * virtual address. the mapped virtual address is kept in bo->virt.
+ *
+ * return non-zero if failed.
+ */
+int hmm_bo_vmap(struct hmm_buffer_object *bo);
+void hmm_bo_vunmap(struct hmm_buffer_object *bo);
+int hmm_bo_vmapped(struct hmm_buffer_object *bo);
+#else
+/*
+ * vmap buffer object's pages to contiguous kernel virtual address.
+ * user needs to call vunmap manually to unmap it.
+ */
+void *hmm_bo_vmap(struct hmm_buffer_object *bo);
+#endif
+
+/*
+ * mmap the bo's physical pages to specific vma.
+ *
+ * vma's address space size must be the same as bo's size,
+ * otherwise it will return -EINVAL.
+ *
+ * vma->vm_flags will be set to (VM_RESERVED | VM_IO).
+ */
+int hmm_bo_mmap(struct vm_area_struct *vma,
+ struct hmm_buffer_object *bo);
+
+#endif
diff --git a/drivers/media/video/mfld_ci/mfldisp/include/hmm/hmm_bo_dev.h b/drivers/media/video/mfld_ci/mfldisp/include/hmm/hmm_bo_dev.h
new file mode 100644
index 0000000..dbd5056
--- /dev/null
+++ b/drivers/media/video/mfld_ci/mfldisp/include/hmm/hmm_bo_dev.h
@@ -0,0 +1,124 @@
+/*
+ * Support for Medifield PNW Camera Imaging ISP subsystem.
+ *
+ * Copyright (c) 2010 Intel Corporation. All Rights Reserved.
+ *
+ * Copyright (c) 2010 Silicon Hive www.siliconhive.com.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#ifndef __HMM_BO_DEV_H__
+#define __HMM_BO_DEV_H__
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+
+#include "mmu/isp_mmu.h"
+#include "hmm/hmm_common.h"
+
+#ifdef USE_DRM_MM
+#include <drm/drm_mm.h>
+#else
+#include "hmm/hmm_vm.h"
+#endif
+
+#define __check_bodev_null_return(bdev, exp) \
+ __check_null_return(bdev, exp, \
+ "NULL hmm_bo_device.\n")
+
+#define HMM_BO_DEVICE_INITED 0x1
+
+struct hmm_buffer_object;
+
+struct hmm_bo_device {
+ /* isp_mmu provides lock itself */
+ struct isp_mmu mmu;
+
+#ifdef USE_DRM_MM
+ /* drm_mm does not support lock itself */
+ struct drm_mm vaddr_space;
+ rwlock_t vm_lock;
+#else
+ /* hmm_vm provides lock itself */
+ struct hmm_vm vaddr_space;
+#endif
+
+ struct list_head free_bo_list;
+ struct mutex fblist_mutex;
+ struct list_head active_bo_list;
+ struct mutex ablist_mutex;
+
+ int flag;
+};
+
+int hmm_bo_device_init(struct hmm_bo_device *bdev,
+ struct isp_mmu_driver *mmu_driver,
+ unsigned int vaddr_start, unsigned int size);
+
+/*
+ * clean up all hmm_bo_device related things.
+ */
+void hmm_bo_device_exit(struct hmm_bo_device *bdev);
+
+/*
+ * whether the bo device is inited or not.
+ */
+int hmm_bo_device_inited(struct hmm_bo_device *bdev);
+
+/*
+ * find the buffer object with virtual address vaddr.
+ * return NULL if no such buffer object found.
+ */
+struct hmm_buffer_object *hmm_bo_device_search_start(
+ struct hmm_bo_device *bdev, unsigned int vaddr);
+
+/*
+ * find the buffer object with virtual address vaddr.
+ * return NULL if no such buffer object found.
+ */
+struct hmm_buffer_object *hmm_bo_device_search_in_range(
+ struct hmm_bo_device *bdev, unsigned int vaddr);
+
+/*
+ * find a buffer object with pgnr pages from free_bo_list and
+ * activate it (remove from free_bo_list and add to
+ * active_bo_list)
+ *
+ * return NULL if no such buffer object found.
+ */
+struct hmm_buffer_object *hmm_bo_device_get_bo(
+ struct hmm_bo_device *bdev, unsigned int pgnr);
+
+/*
+ * destroy all buffer objects in the free_bo_list.
+ */
+void hmm_bo_device_destroy_free_bo_list(struct hmm_bo_device *bdev);
+/*
+ * destroy buffer object with start virtual address vaddr.
+ */
+void hmm_bo_device_destroy_free_bo_addr(struct hmm_bo_device *bdev,
+ unsigned int vaddr);
+/*
+ * destroy all buffer objects with pgnr pages.
+ */
+void hmm_bo_device_destroy_free_bo_size(struct hmm_bo_device *bdev,
+ unsigned int pgnr);
+
+#endif
diff --git a/drivers/media/video/mfld_ci/mfldisp/include/hmm/hmm_common.h b/drivers/media/video/mfld_ci/mfldisp/include/hmm/hmm_common.h
new file mode 100644
index 0000000..8a1368f
--- /dev/null
+++ b/drivers/media/video/mfld_ci/mfldisp/include/hmm/hmm_common.h
@@ -0,0 +1,121 @@
+/*
+ * Support for Medifield PNW Camera Imaging ISP subsystem.
+ *
+ * Copyright (c) 2010 Intel Corporation. All Rights Reserved.
+ *
+ * Copyright (c) 2010 Silicon Hive www.siliconhive.com.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#ifndef __HMM_BO_COMMON_H__
+#define __HMM_BO_COMMON_H__
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+/*
+ * print and debug micros.
+ */
+#define HMM_BO_NAME "HMM"
+
+#define __hmm_printk(level, fmt, arg ...) \
+ do { \
+ printk(level "%s: " fmt, HMM_BO_NAME, ## arg); \
+ udelay(10000); \
+ } while (0)
+
+#define __hmm_printk_detail(level, fmt, arg ...) \
+ do { \
+ printk(level "%s: %s: %d: " fmt, HMM_BO_NAME, \
+ __func__, __LINE__, ## arg); \
+ udelay(10000); \
+ } while (0)
+
+#define __hmm_info(fmt, arg ...) \
+ __hmm_printk(KERN_INFO, fmt, ## arg)
+
+#define __hmm_warn(fmt, arg ...) \
+ __hmm_printk_detail(KERN_WARNING, fmt, ## arg)
+
+#define __hmm_err(fmt, arg ...) \
+ __hmm_printk_detail(KERN_ERR, fmt, ## arg)
+
+extern int hmm_dbg;
+#define __hmm_dbg(level, fmt, arg...) \
+ do {\
+ if ((hmm_dbg) >= (level)) \
+ __hmm_printk_detail(KERN_DEBUG, fmt, ## arg);\
+ } while (0)
+
+/*
+ * some common use micros
+ */
+#define __var_equal(var1, var2, fmt, arg ...) \
+ do { \
+ if ((var1) == (var2))\
+ __hmm_err(fmt, ## arg); \
+ } while (0)
+
+#define __var_equal_return(var1, var2, exp, fmt, arg ...) \
+ do { \
+ if ((var1) == (var2)) { \
+ __hmm_err(fmt, ## arg); \
+ return exp;\
+ } \
+ } while (0)
+
+#define __var_equal_goto(var1, var2, label, fmt, arg ...) \
+ do { \
+ if ((var1) == (var2)) { \
+ __hmm_err(fmt, ## arg); \
+ goto label;\
+ } \
+ } while (0)
+
+
+#define __var_not_equal(var1, var2, fmt, arg ...) \
+ do { \
+ if ((var1) != (var2))\
+ __hmm_err(fmt, ## arg); \
+ } while (0)
+
+#define __var_not_equal_return(var1, var2, exp, fmt, arg ...) \
+ do { \
+ if ((var1) != (var2)) { \
+ __hmm_err(fmt, ## arg); \
+ return exp;\
+ } \
+ } while (0)
+
+#define __var_not_equal_goto(var1, var2, label, fmt, arg ...) \
+ do { \
+ if ((var1) != (var2)) { \
+ __hmm_err(fmt, ## arg); \
+ goto label;\
+ } \
+ } while (0)
+
+#define __check_null(ptr, fmt, arg...) \
+ __var_equal(ptr, NULL, fmt, ## arg)
+
+#define __check_null_return(ptr, exp, fmt, arg ...) \
+ __var_equal_return(ptr, NULL, exp, fmt, ## arg)
+
+#define __check_null_goto(ptr, label, fmt, arg...) \
+ __var_equal_goto(ptr, NULL, label, fmt, ## arg)
+
+#endif
diff --git a/drivers/media/video/mfld_ci/mfldisp/include/hmm/hmm_vm.h b/drivers/media/video/mfld_ci/mfldisp/include/hmm/hmm_vm.h
new file mode 100644
index 0000000..24e3310
--- /dev/null
+++ b/drivers/media/video/mfld_ci/mfldisp/include/hmm/hmm_vm.h
@@ -0,0 +1,67 @@
+/*
+ * Support for Medifield PNW Camera Imaging ISP subsystem.
+ *
+ * Copyright (c) 2010 Intel Corporation. All Rights Reserved.
+ *
+ * Copyright (c) 2010 Silicon Hive www.siliconhive.com.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#ifndef __HMM_VM_H__
+#define __HMM_VM_H__
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+
+struct hmm_vm {
+ unsigned int start;
+ unsigned int pgnr;
+ unsigned int size;
+ struct list_head vm_node_list;
+ spinlock_t lock;
+};
+
+struct hmm_vm_node {
+ struct list_head list;
+ unsigned int start;
+ unsigned int pgnr;
+ unsigned int size;
+ struct hmm_vm *vm;
+};
+
+#define __hmm_vm_node(list_ptr) \
+ list_entry((list_ptr), struct hmm_vm_node, list)
+
+int hmm_vm_init(struct hmm_vm *vm, unsigned int start,
+ unsigned int size);
+
+void hmm_vm_clean(struct hmm_vm *vm);
+
+struct hmm_vm_node *hmm_vm_alloc_node(struct hmm_vm *vm,
+ unsigned int pgnr);
+
+void hmm_vm_free_node(struct hmm_vm_node *node);
+
+struct hmm_vm_node *hmm_vm_find_node_start(struct hmm_vm *vm,
+ unsigned int addr);
+
+struct hmm_vm_node *hmm_vm_find_node_in_range(struct hmm_vm *vm,
+ unsigned int addr);
+
+#endif
--
1.6.2.5
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0005-ISP-driver-The-low-level-memory-management-of-ISP.patch
Type: application/octet-stream
Size: 82603 bytes
Desc: 0005-ISP-driver-The-low-level-memory-management-of-ISP.patch
URL: <http://lists.meego.com/pipermail/meego-kernel/attachments/20101202/64cf85a2/attachment-0001.obj>
More information about the MeeGo-kernel
mailing list