[Meego-kernel] [MFLD Camera - PATCH v6 6/9] Medfield Camera Image ISP driver memory management.

Wang, Wen W wen.w.wang at intel.com
Thu Dec 23 06:39:09 PST 2010


>From 62e903e745ba1cc635fe15f1ff4e79728199eef0 Mon Sep 17 00:00:00 2001
From: Wen Wang <wen.w.wang at intel.com>
Date: Fri, 24 Dec 2010 02:14:10 +0800
Subject: [PATCH] MFLD ISP memory management

This patch includes memory and MMU management for Medfield camera image
ISP driver. It provides functions to control MMU block in ISP and memory
allocation/free/access from ISP point of view.

Signed-off-by: Wen Wang <wen.w.wang at intel.com>
Signed-off-by: Xiaolin Zhang <xiaolin.zhang at intel.com>
---
 drivers/media/video/atomisp/hmm/hmm.c              |  375 ++++++
 drivers/media/video/atomisp/hmm/hmm_bo.c           | 1190 ++++++++++++++++++++
 drivers/media/video/atomisp/hmm/hmm_bo_dev.c       |  295 +++++
 drivers/media/video/atomisp/hmm/hmm_vm.c           |  210 ++++
 drivers/media/video/atomisp/include/hmm/hmm.h      |   71 ++
 drivers/media/video/atomisp/include/hmm/hmm_bo.h   |  313 +++++
 .../media/video/atomisp/include/hmm/hmm_bo_dev.h   |  124 ++
 .../media/video/atomisp/include/hmm/hmm_common.h   |   97 ++
 drivers/media/video/atomisp/include/hmm/hmm_vm.h   |   67 ++
 drivers/media/video/atomisp/include/mmu/isp_mmu.h  |  168 +++
 drivers/media/video/atomisp/include/mmu/sh_mmu.h   |   74 ++
 drivers/media/video/atomisp/mmu/isp_mmu.c          |  515 +++++++++
 12 files changed, 3499 insertions(+), 0 deletions(-)
 create mode 100644 drivers/media/video/atomisp/hmm/hmm.c
 create mode 100644 drivers/media/video/atomisp/hmm/hmm_bo.c
 create mode 100644 drivers/media/video/atomisp/hmm/hmm_bo_dev.c
 create mode 100644 drivers/media/video/atomisp/hmm/hmm_vm.c
 create mode 100644 drivers/media/video/atomisp/include/hmm/hmm.h
 create mode 100644 drivers/media/video/atomisp/include/hmm/hmm_bo.h
 create mode 100644 drivers/media/video/atomisp/include/hmm/hmm_bo_dev.h
 create mode 100644 drivers/media/video/atomisp/include/hmm/hmm_common.h
 create mode 100644 drivers/media/video/atomisp/include/hmm/hmm_vm.h
 create mode 100644 drivers/media/video/atomisp/include/mmu/isp_mmu.h
 create mode 100644 drivers/media/video/atomisp/include/mmu/sh_mmu.h
 create mode 100644 drivers/media/video/atomisp/mmu/isp_mmu.c

diff --git a/drivers/media/video/atomisp/hmm/hmm.c b/drivers/media/video/atomisp/hmm/hmm.c
new file mode 100644
index 0000000..7f4d0de
--- /dev/null
+++ b/drivers/media/video/atomisp/hmm/hmm.c
@@ -0,0 +1,375 @@
+/*
+ * 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.
+ *
+ */
+/*
+ * This file contains entry functions for memory management of ISP driver
+ */
+#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;
+static 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)
+               v4l2_printk(KERN_ERR, &isp_dev,
+                           "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;
+
+       /*Get page number from size*/
+       pgnr = size_to_pgnr_ceil(bytes);
+
+       /*Buffer object structure init*/
+       bo = hmm_bo_create(&bo_device, pgnr);
+       if (!bo) {
+               v4l2_printk(KERN_ERR, &isp_dev, "hmm_bo_create failed.\n");
+               goto create_bo_err;
+       }
+
+       /*Allocate virtual address in ISP virtual space*/
+       ret = hmm_bo_alloc_vm(bo);
+       if (ret) {
+               v4l2_printk(KERN_ERR, &isp_dev,
+                           "hmm_bo_alloc_vm failed.\n");
+               goto alloc_vm_err;
+       }
+
+       /*Allocate pages for memory*/
+       ret = hmm_bo_alloc_pages(bo, type, from_highmem, userptr);
+       if (ret) {
+               v4l2_printk(KERN_ERR, &isp_dev,
+                           "hmm_bo_alloc_pages failed.\n");
+               goto alloc_page_err;
+       }
+
+       /*Combind the virtual address and pages togather*/
+       ret = hmm_bo_bind(bo);
+       if (ret) {
+               v4l2_printk(KERN_ERR, &isp_dev, "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) {
+               v4l2_printk(KERN_ERR, &isp_dev,
+                           "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);
+}
+
+static inline int hmm_check_bo(struct hmm_buffer_object *bo, unsigned int ptr)
+{
+       if (!bo) {
+               v4l2_printk(KERN_ERR, &isp_dev,
+                           "can not find buffer object contains "
+                           "address 0x%x\n", ptr);
+               return -EINVAL;
+       }
+
+       if (!hmm_bo_page_allocated(bo)) {
+               v4l2_printk(KERN_ERR, &isp_dev,
+                           "buffer object has no page allocated.\n");
+               return -EINVAL;
+       }
+
+       if (!hmm_bo_vm_allocated(bo)) {
+               v4l2_printk(KERN_ERR, &isp_dev,
+                           "buffer object has no virtual address"
+                           " space allocated.\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/*Read function in ISP memory management*/
+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;
+       int ret;
+
+       ptr = (unsigned int)virt;
+
+       bo = hmm_bo_device_search_in_range(&bo_device, ptr);
+       ret = hmm_check_bo(bo, ptr);
+       if (ret)
+               return ret;
+
+       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) {
+                       v4l2_printk(KERN_ERR, &isp_dev,
+                                   "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;
+}
+
+/*Write function in ISP memory management*/
+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;
+       int ret;
+
+       ptr = (unsigned int)virt;
+
+       bo = hmm_bo_device_search_in_range(&bo_device, ptr);
+       ret = hmm_check_bo(bo, ptr);
+       if (ret)
+               return ret;
+
+       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) {
+                       v4l2_printk(KERN_ERR, &isp_dev,
+                                   "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;
+}
+
+/*memset function in ISP memory management*/
+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;
+       int ret;
+
+       ptr = (unsigned int)virt;
+
+       bo = hmm_bo_device_search_in_range(&bo_device, ptr);
+       ret = hmm_check_bo(bo, ptr);
+       if (ret)
+               return ret;
+
+       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) {
+                       v4l2_printk(KERN_ERR, &isp_dev,
+                                   "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;
+}
+
+/*Virtual address to physical address convert*/
+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) {
+               v4l2_printk(KERN_ERR, &isp_dev,
+                           "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) {
+               v4l2_printk(KERN_ERR, &isp_dev,
+                           "can not find buffer object start with "
+                           "address 0x%x\n", (unsigned int)virt);
+               return -EINVAL;
+       }
+
+       return hmm_bo_mmap(vma, bo);
+}
+
+/*Map ISP virtual address into IA virtual address*/
+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) {
+               v4l2_printk(KERN_ERR, &isp_dev,
+                           "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/atomisp/hmm/hmm_bo.c b/drivers/media/video/atomisp/hmm/hmm_bo.c
new file mode 100644
index 0000000..f1f2935
--- /dev/null
+++ b/drivers/media/video/atomisp/hmm/hmm_bo.c
@@ -0,0 +1,1190 @@
+/*
+ * 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.
+ *
+ */
+/*
+ * This file contains functions for buffer object structure management
+ */
+#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;
+
+       while (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) {
+               v4l2_printk(KERN_ERR, &isp_dev, "out of memory.\n");
+               return NULL;
+       }
+
+       ret = hmm_bo_init(bdev, bo, pgnr, free_bo_internal);
+       if (ret) {
+               v4l2_printk(KERN_ERR, &isp_dev, "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) {
+               v4l2_printk(KERN_WARNING, &isp_dev,
+                           "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) {
+               v4l2_printk(KERN_ERR, &isp_dev,
+                           "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)
+               v4l2_printk(KERN_WARNING, &isp_dev,
+                           "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) {
+               v4l2_printk(KERN_ERR, &isp_dev,
+                            "destroy bo which is MMAPED, do nothing\n");
+               goto err;
+       }
+#ifdef HMM_BO_VMAP_SUPPORT
+       if (bo->status & HMM_BO_VMAPED) {
+               v4l2_printk(KERN_WARNING, &isp_dev,
+                            "the bo is still vmapped, unmap it first...\n");
+               hmm_bo_vunmap(bo);
+       }
+#endif
+
+       if (bo->status & HMM_BO_BINDED) {
+               v4l2_printk(KERN_WARNING, &isp_dev,
+                            "the bo is still binded, unbind it first...\n");
+               hmm_bo_unbind(bo);
+       }
+       if (bo->status & HMM_BO_PAGE_ALLOCED) {
+               v4l2_printk(KERN_WARNING, &isp_dev,
+                            "the pages is not freed, free pages first\n");
+               hmm_bo_free_pages(bo);
+       }
+       if (bo->status & HMM_BO_VM_ALLOCED) {
+               v4l2_printk(KERN_WARNING, &isp_dev,
+                            "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:
+       v4l2_printk(KERN_ERR, &isp_dev,
+                       "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)) {
+               v4l2_printk(KERN_ERR, &isp_dev,
+                               "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);
+       v4l2_printk(KERN_ERR, &isp_dev,
+                       "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);
+       v4l2_printk(KERN_ERR, &isp_dev,
+                       "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) {
+               v4l2_printk(KERN_ERR, &isp_dev,
+                               "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
+
+/*Allocate pages which will be used only by ISP*/
+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) {
+                               v4l2_printk(KERN_ERR, &isp_dev,
+                                            "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)
+                       v4l2_printk(KERN_ERR, &isp_dev,
+                                       "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)
+                       v4l2_printk(KERN_ERR, &isp_dev,
+                                       "set page to WB err...\n");
+
+               __free_pages(pgblk->pages, pgblk->order);
+               kfree(pgblk);
+       }
+
+       kfree(bo->pages);
+}
+
+/*
+ * Convert user space virtual address into page list and update into pages
+ * tables
+ */
+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)) {
+               v4l2_printk(KERN_ERR, &isp_dev, "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) {
+               v4l2_printk(KERN_ERR, &isp_dev,
+                               "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);
+       }
+       /* fixe me: need to set pages to uncacheable */
+
+       for (i = 0; i < bo->pgnr; i++) {
+#ifdef CONFIG_ARCH_USES_PG_UNCACHED
+               SetPageUncached(bo->pages[i]);
+#else
+               ret = set_pages_uc(bo->pages[i], 1);
+               if (ret) {
+                       v4l2_printk(KERN_ERR, &isp_dev,
+                                       "set page to uncacheable failed %d.\n",
+                                       i);
+                       goto cleanup;
+               }
+#endif
+       }
+
+       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;
+
+       for (i = 0; i < bo->pgnr; i++) {
+#ifdef CONFIG_ARCH_USES_PG_UNCACHED
+               ClearPageUncached(bo->pages[i]);
+#else
+               if (set_pages_wb(bo->pages[i], 1))
+                       v4l2_printk(KERN_ERR, &isp_dev,
+                                    "set page to WB again failed.\n");
+#endif
+       }
+
+       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 {
+               v4l2_printk(KERN_ERR, &isp_dev, "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);
+       v4l2_printk(KERN_ERR, &isp_dev, "alloc pages err...\n");
+       return ret;
+status_err:
+       mutex_unlock(&bo->mutex);
+       v4l2_printk(KERN_ERR, &isp_dev,
+                       "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
+               v4l2_printk(KERN_ERR, &isp_dev, "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);
+       v4l2_printk(KERN_ERR, &isp_dev,
+                       "buffer object not page allocated yet.\n");
+
+#ifdef HMM_BO_VMAP_SUPPORT
+status_err1:
+       mutex_unlock(&bo->mutex);
+       v4l2_printk(KERN_ERR, &isp_dev,
+                    "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:
+       v4l2_printk(KERN_ERR, &isp_dev,
+                       "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);
+       v4l2_printk(KERN_ERR, &isp_dev,
+                       "setup MMU address mapping failed.\n");
+       return ret;
+
+status_err2:
+       mutex_unlock(&bo->mutex);
+       v4l2_printk(KERN_ERR, &isp_dev, "buffer object already binded.\n");
+       return -EINVAL;
+status_err1:
+       mutex_unlock(&bo->mutex);
+       v4l2_printk(KERN_ERR, &isp_dev,
+                    "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);
+       v4l2_printk(KERN_ERR, &isp_dev,
+                    "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);
+       v4l2_printk(KERN_ERR, &isp_dev, "buffer object vmap failed.\n");
+       return -ENOMEM;
+status_err2:
+       mutex_unlock(&bo->mutex);
+       v4l2_printk(KERN_ERR, &isp_dev, "buffer object already vmapped.\n");
+       return -EINVAL;
+status_err1:
+       mutex_unlock(&bo->mutex);
+       v4l2_printk(KERN_ERR, &isp_dev,
+                       "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);
+       v4l2_printk(KERN_ERR, &isp_dev, "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) {
+               v4l2_printk(KERN_WARNING, &isp_dev,
+                            "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)) {
+                       v4l2_printk(KERN_WARNING, &isp_dev,
+                                       "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:
+       v4l2_printk(KERN_ERR, &isp_dev,
+                       "buffer page not allocated yet.\n");
+       return -EINVAL;
+}
diff --git a/drivers/media/video/atomisp/hmm/hmm_bo_dev.c b/drivers/media/video/atomisp/hmm/hmm_bo_dev.c
new file mode 100644
index 0000000..5fed475
--- /dev/null
+++ b/drivers/media/video/atomisp/hmm/hmm_bo_dev.c
@@ -0,0 +1,295 @@
+/*
+ * 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) {
+               v4l2_printk(KERN_ERR, &isp_dev, "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) {
+               v4l2_printk(KERN_ERR, &isp_dev, "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) {
+               v4l2_printk(KERN_ERR, &isp_dev, "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))
+               v4l2_printk(KERN_WARNING, &isp_dev,
+                            "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))
+               v4l2_printk(KERN_WARNING, &isp_dev,
+                               "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/atomisp/hmm/hmm_vm.c b/drivers/media/video/atomisp/hmm/hmm_vm.c
new file mode 100644
index 0000000..336b1cb
--- /dev/null
+++ b/drivers/media/video/atomisp/hmm/hmm_vm.c
@@ -0,0 +1,210 @@
+/*
+ * 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.
+ *
+ */
+/*
+ * This file contains function for ISP virtual address management in ISP driver
+ */
+#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) {
+               v4l2_printk(KERN_ERR, &isp_dev, "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) {
+               v4l2_printk(KERN_INFO, &isp_dev,
+                           "no enough virtual address space.\n");
+               goto failed;
+       }
+
+found:
+
+       spin_unlock(&vm->lock);
+       node = alloc_hmm_vm_node(addr, pgnr, vm);
+       if (!node)
+               goto failed;
+
+       spin_lock(&vm->lock);
+       list_add(&node->list, pos);
+
+       spin_unlock(&vm->lock);
+
+       return node;
+failed:
+       v4l2_printk(KERN_ERR, &isp_dev, "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/atomisp/include/hmm/hmm.h b/drivers/media/video/atomisp/include/hmm/hmm.h
new file mode 100644
index 0000000..05c5e35
--- /dev/null
+++ b/drivers/media/video/atomisp/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/atomisp/include/hmm/hmm_bo.h b/drivers/media/video/atomisp/include/hmm/hmm_bo.h
new file mode 100644
index 0000000..30b4bf7
--- /dev/null
+++ b/drivers/media/video/atomisp/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/atomisp/include/hmm/hmm_bo_dev.h b/drivers/media/video/atomisp/include/hmm/hmm_bo_dev.h
new file mode 100644
index 0000000..9762143
--- /dev/null
+++ b/drivers/media/video/atomisp/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/atomisp/include/hmm/hmm_common.h b/drivers/media/video/atomisp/include/hmm/hmm_common.h
new file mode 100644
index 0000000..2358979
--- /dev/null
+++ b/drivers/media/video/atomisp/include/hmm/hmm_common.h
@@ -0,0 +1,97 @@
+/*
+ * 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>
+#include <media/v4l2-device.h>
+#include <media/v4l2-common.h>
+
+#define        HMM_BO_NAME     "HMM"
+
+/*
+ * some common use micros
+ */
+#define        var_equal(var1, var2, fmt, arg ...)     \
+       do { \
+               if ((var1) == (var2))\
+                       v4l2_printk(KERN_ERR, &isp_dev, \
+                       fmt, ## arg); \
+       } while (0)
+
+#define        var_equal_return(var1, var2, exp, fmt, arg ...) \
+       do { \
+               if ((var1) == (var2)) { \
+                       v4l2_printk(KERN_ERR, &isp_dev, \
+                       fmt, ## arg); \
+                       return exp;\
+               } \
+       } while (0)
+
+#define        var_equal_goto(var1, var2, label, fmt, arg ...) \
+       do { \
+               if ((var1) == (var2)) { \
+                       v4l2_printk(KERN_ERR, &isp_dev, \
+                       fmt, ## arg); \
+                       goto label;\
+               } \
+       } while (0)
+
+
+#define        var_not_equal(var1, var2, fmt, arg ...) \
+       do { \
+               if ((var1) != (var2))\
+                       v4l2_printk(KERN_ERR, &isp_dev, \
+                       fmt, ## arg); \
+       } while (0)
+
+#define        var_not_equal_return(var1, var2, exp, fmt, arg ...)     \
+       do { \
+               if ((var1) != (var2)) { \
+                       v4l2_printk(KERN_ERR, &isp_dev, \
+                       fmt, ## arg); \
+                       return exp;\
+               } \
+       } while (0)
+
+#define        var_not_equal_goto(var1, var2, label, fmt, arg ...)     \
+       do { \
+               if ((var1) != (var2)) { \
+                       v4l2_printk(KERN_ERR, &isp_dev, \
+                       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/atomisp/include/hmm/hmm_vm.h b/drivers/media/video/atomisp/include/hmm/hmm_vm.h
new file mode 100644
index 0000000..260cb90
--- /dev/null
+++ b/drivers/media/video/atomisp/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
diff --git a/drivers/media/video/atomisp/include/mmu/isp_mmu.h b/drivers/media/video/atomisp/include/mmu/isp_mmu.h
new file mode 100644
index 0000000..a01c19b
--- /dev/null
+++ b/drivers/media/video/atomisp/include/mmu/isp_mmu.h
@@ -0,0 +1,168 @@
+/*
+ * 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.
+ *
+ */
+/*
+ * ISP MMU driver for classic two-level page tables
+ */
+#ifndef        __ISP_MMU_H__
+#define        __ISP_MMU_H__
+
+#include <linux/mutex.h>
+
+/*
+ * do not change these values, the page size for ISP must be the
+ * same as kernel's page size.
+ */
+#define        ISP_PAGE_OFFSET         12
+#define        ISP_PAGE_SIZE           (1U << ISP_PAGE_OFFSET)
+#define        ISP_PAGE_MASK           (~(ISP_PAGE_SIZE - 1))
+
+#define        ISP_L1PT_OFFSET         22
+#define        ISP_L1PT_MASK           (~((1U << ISP_L1PT_OFFSET) - 1))
+
+#define        ISP_L2PT_OFFSET         12
+#define        ISP_L2PT_MASK           (~(ISP_L1PT_MASK|(~(ISP_PAGE_MASK))))
+
+#define        ISP_L1PT_PTES           1024
+#define        ISP_L2PT_PTES           1024
+
+#define        ISP_PTR_TO_L1_IDX(x)    ((((x) & ISP_L1PT_MASK)) \
+                                       >> ISP_L1PT_OFFSET)
+
+#define        ISP_PTR_TO_L2_IDX(x)    ((((x) & ISP_L2PT_MASK)) \
+                                       >> ISP_L2PT_OFFSET)
+
+#define        ISP_PAGE_ALIGN(x)       (((x) + (ISP_PAGE_SIZE-1)) \
+                                       & ISP_PAGE_MASK)
+
+#define        ISP_PT_TO_VIRT(l1_idx, l2_idx, offset) do {\
+               ((l1_idx) << ISP_L1PT_OFFSET) | \
+               ((l2_idx) << ISP_L2PT_OFFSET) | \
+               (offset)\
+} while (0)
+
+#define        pgnr_to_size(pgnr)      ((pgnr) << ISP_PAGE_OFFSET)
+#define        size_to_pgnr_ceil(size) (((size) + (1 << ISP_PAGE_OFFSET) - 1)\
+                                               >> ISP_PAGE_OFFSET)
+#define        size_to_pgnr_bottom(size)       ((size) >> ISP_PAGE_OFFSET)
+
+struct isp_mmu;
+
+struct isp_mmu_driver {
+       /*
+        * const value
+        *
+        * @name:
+        *      driver name
+        * @pte_valid_mask:
+        *      should be 1 bit valid data, meaning the value should
+        *      be power of 2.
+        */
+       char *name;
+       unsigned int pte_valid_mask;
+
+       /*
+        * set page directory base address (physical address).
+        *
+        * must be provided.
+        */
+       int (*set_pd_base) (struct isp_mmu *mmu,
+                       unsigned int pd_base);
+       /*
+        * callback to flush tlb.
+        *
+        * tlb_flush_range will at least flush TLBs containing
+        * address mapping from addr to addr + size.
+        *
+        * tlb_flush_all will flush all TLBs.
+        *
+        * tlb_flush_all is must be provided. if tlb_flush_range is
+        * not valid, it will set to tlb_flush_all by default.
+        */
+       void (*tlb_flush_range) (struct isp_mmu *mmu,
+                                unsigned int addr, unsigned int size);
+       void (*tlb_flush_all) (struct isp_mmu *mmu);
+
+};
+
+struct isp_mmu {
+       struct isp_mmu_driver *driver;
+       unsigned int l1_pte;
+
+       struct mutex pt_mutex;
+};
+
+/* flags for PDE and PTE */
+#define        ISP_PTE_VALID_MASK(mmu) \
+       ((mmu)->driver->pte_valid_mask)
+
+#define        ISP_PTE_VALID(mmu, pte) \
+       ((pte) & ISP_PTE_VALID_MASK(mmu))
+
+#define        UNVALID_PHYS    0xffffffff
+
+#define        NULL_PAGE       (UNVALID_PHYS & ISP_PAGE_MASK)
+#define        NULL_PTE        NULL_PAGE
+
+#define        PAGE_VALID(page)        ((page) != NULL_PAGE)
+
+/*
+ * init mmu with specific mmu driver.
+ */
+int isp_mmu_init(struct isp_mmu *mmu, struct isp_mmu_driver *driver);
+/*
+ * cleanup all mmu related things.
+ */
+void isp_mmu_exit(struct isp_mmu *mmu);
+
+/*
+ * setup/remove address mapping for pgnr continous physical pages
+ * and isp_virt.
+ *
+ * map/unmap is mutex lock protected, and caller does not have
+ * to do lock/unlock operation.
+ *
+ * map/unmap will not flush tlb, and caller needs to deal with
+ * this itself.
+ */
+int isp_mmu_map(struct isp_mmu *mmu, unsigned int isp_virt,
+               unsigned int phys, unsigned int pgnr);
+
+void isp_mmu_unmap(struct isp_mmu *mmu, unsigned int isp_virt,
+                  unsigned int pgnr);
+
+static inline void isp_mmu_flush_tlb_all(struct isp_mmu *mmu)
+{
+       if (mmu->driver && mmu->driver->tlb_flush_all)
+               mmu->driver->tlb_flush_all(mmu);
+}
+
+#define isp_mmu_flush_tlb isp_mmu_flush_tlb_all
+
+static inline void isp_mmu_flush_tlb_range(struct isp_mmu *mmu,
+               unsigned int start, unsigned int size)
+{
+       if (mmu->driver && mmu->driver->tlb_flush_range)
+               mmu->driver->tlb_flush_range(mmu, start, size);
+}
+
+#endif /* ISP_MMU_H_ */
diff --git a/drivers/media/video/atomisp/include/mmu/sh_mmu.h b/drivers/media/video/atomisp/include/mmu/sh_mmu.h
new file mode 100644
index 0000000..d05aa3b
--- /dev/null
+++ b/drivers/media/video/atomisp/include/mmu/sh_mmu.h
@@ -0,0 +1,74 @@
+/*
+ * 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        SH_MMU_H_
+#define        SH_MMU_H_
+
+#include <css/sh_css.h>
+#include "mmu/isp_mmu.h"
+
+
+/*
+ * include SH header file here
+ */
+
+/*
+ * set page directory base address (physical address).
+ *
+ * must be provided.
+ */
+static int sh_set_pd_base(struct isp_mmu *mmu,
+               unsigned int phys)
+{
+       sh_css_mmu_set_page_table_base_address((void *)phys);
+       return 0;
+}
+
+/*
+ * callback to flush tlb.
+ *
+ * tlb_flush_range will at least flush TLBs containing
+ * address mapping from addr to addr + size.
+ *
+ * tlb_flush_all will flush all TLBs.
+ *
+ * tlb_flush_all is must be provided. if tlb_flush_range is
+ * not valid, it will set to tlb_flush_all by default.
+ */
+static void sh_tlb_flush(struct isp_mmu *mmu)
+{
+       sh_css_mmu_invalidate_cache();
+}
+
+static struct isp_mmu_driver sh_mmu_driver = {
+       .name = "Silicon Hive ISP3000 MMU",
+       .pte_valid_mask = 0x1,
+       .set_pd_base = sh_set_pd_base,
+       .tlb_flush_all = sh_tlb_flush,
+};
+
+#define        ISP_VM_START    0x0
+#define        ISP_VM_SIZE     (1 << 30)       /* 1G address space */
+#define        ISP_PTR_NULL    NULL
+
+#endif /* SH_MMU_H_ */
+
diff --git a/drivers/media/video/atomisp/mmu/isp_mmu.c b/drivers/media/video/atomisp/mmu/isp_mmu.c
new file mode 100644
index 0000000..3c892d6
--- /dev/null
+++ b/drivers/media/video/atomisp/mmu/isp_mmu.c
@@ -0,0 +1,515 @@
+/*
+ * 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.
+ *
+ */
+/*
+ * ISP MMU management wrap code
+ */
+#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/list.h>
+#include <linux/io.h>
+#include <asm/cacheflush.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+
+#include "mmu/isp_mmu.h"
+#include "mfldisp_internal.h"
+
+static unsigned int mfld_isp_get_pte(unsigned int pt, unsigned int idx)
+{
+       unsigned int pt_virt = (unsigned int)phys_to_virt(pt);
+       return *(((unsigned int *) pt_virt) + idx);
+}
+
+static void mfld_isp_set_pte(unsigned int pt,
+                            unsigned int idx, unsigned int pte)
+{
+       unsigned int pt_virt = (unsigned int)phys_to_virt(pt);
+       (*(((unsigned int *) pt_virt) + idx)) = pte;
+}
+
+static void *isp_pt_phys_to_virt(unsigned int phys)
+{
+       return phys_to_virt(phys);
+}
+
+static unsigned int isp_pte_to_pgaddr(unsigned int pte)
+{
+       return (unsigned int)(pte & ISP_PAGE_MASK);
+}
+
+static unsigned int isp_pgaddr_to_pte_valid(struct isp_mmu *mmu,
+                                          unsigned int phys)
+{
+       return (unsigned int) (phys | ISP_PTE_VALID_MASK(mmu));
+}
+
+/*
+ * allocate a uncacheable page table.
+ * return physical address.
+ */
+static unsigned int alloc_page_table(void)
+{
+       int i;
+       unsigned int page;
+
+       void *virt = (void *)__get_free_page(GFP_KERNEL);
+       if (!virt)
+               return NULL_PAGE;
+
+       /*
+        * we need a uncacheable page table.
+        */
+#ifdef CONFIG_X86
+       set_memory_uc((unsigned int)virt, 1);
+#endif
+
+       page = virt_to_phys(virt);
+
+       for (i = 0; i < 1024; i++) {
+               /* NEED CHECK */
+               mfld_isp_set_pte(page, i, NULL_PAGE);
+       }
+
+       return page;
+}
+
+static void free_page_table(unsigned int page)
+{
+       unsigned int virt;
+       page &= ISP_PAGE_MASK;
+       /*
+        * reset the page to write back before free
+        */
+#ifdef CONFIG_X86
+       virt = (unsigned int)phys_to_virt(page);
+       set_memory_wb(virt, 1);
+#endif
+       free_page((unsigned long)phys_to_virt(page));
+}
+
+static void mmu_remap_error(struct isp_mmu *mmu,
+                             unsigned int l1_pt, unsigned int l1_idx,
+                             unsigned int l2_pt, unsigned int l2_idx,
+                             unsigned int isp_virt, unsigned int old_phys,
+                             unsigned int new_phys)
+{
+       v4l2_printk(KERN_ERR, &isp_dev, "address remap:\n\n"
+                    "\tL1 PT: virt = 0x%x, phys = 0x%x, "
+                    "idx = %d\n"
+                    "\tL2 PT: virt = 0x%x, phys = 0x%x, "
+                    "idx = %d\n"
+                    "\told: isp_virt = 0x%x, phys = 0x%x\n"
+                    "\tnew: isp_virt = 0x%x, phys = 0x%x\n",
+                    (unsigned int)isp_pt_phys_to_virt(l1_pt),
+                    (unsigned int)(l1_pt), l1_idx,
+                    (unsigned int)isp_pt_phys_to_virt(l2_pt),
+                    (unsigned int)(l2_pt), l2_idx, (unsigned int)isp_virt,
+                    (unsigned int)old_phys, (unsigned int)isp_virt,
+                    (unsigned int)new_phys);
+}
+
+static void mmu_unmap_l2_pte_error(struct isp_mmu *mmu,
+                                    unsigned int l1_pt, unsigned int l1_idx,
+                                    unsigned int l2_pt, unsigned int l2_idx,
+                                    unsigned int isp_virt, unsigned int pte)
+{
+       v4l2_printk(KERN_ERR, &isp_dev, "unmap unvalid L2 pte:\n\n"
+                    "\tL1 PT: virt = 0x%x, phys = 0x%x, "
+                    "idx = %d\n"
+                    "\tL2 PT: virt = 0x%x, phys = 0x%x, "
+                    "idx = %d\n"
+                    "\tisp_virt = 0x%x, pte(page phys) = 0x%x\n",
+                    (unsigned int)isp_pt_phys_to_virt(l1_pt),
+                    (unsigned int)(l1_pt), l1_idx,
+                    (unsigned int)isp_pt_phys_to_virt(l2_pt),
+                    (unsigned int)(l2_pt), l2_idx, (unsigned int)isp_virt,
+                    (unsigned int)pte);
+}
+
+static void mmu_unmap_l1_pte_error(struct isp_mmu *mmu,
+                                    unsigned int l1_pt, unsigned int l1_idx,
+                                    unsigned int isp_virt, unsigned int pte)
+{
+       v4l2_printk(KERN_ERR, &isp_dev, "unmap unvalid L1 pte (L2 PT):\n\n"
+                    "\tL1 PT: virt = 0x%x, phys = 0x%x, "
+                    "idx = %d\n"
+                    "\tisp_virt = 0x%x, l1_pte(L2 PT) = 0x%x\n",
+                    (unsigned int)isp_pt_phys_to_virt(l1_pt),
+                    (unsigned int)(l1_pt), l1_idx, (unsigned int)isp_virt,
+                    (unsigned int)pte);
+}
+
+static void mmu_unmap_l1_pt_error(struct isp_mmu *mmu, unsigned int pte)
+{
+       v4l2_printk(KERN_ERR, &isp_dev, "unmap unvalid L1PT:\n\n"
+                    "L1PT = 0x%x\n", (unsigned int)pte);
+}
+
+/*
+ * Update L2 page table according to isp virtual address and page physical
+ * address
+ */
+static int mmu_l2_map(struct isp_mmu *mmu, unsigned int l1_pt,
+                       unsigned int l1_idx, unsigned int l2_pt,
+                       unsigned int start, unsigned int end, unsigned int phys)
+{
+       unsigned int ptr;
+       unsigned int idx;
+       unsigned int pte;
+
+       l2_pt &= ISP_PAGE_MASK;
+
+       start = start & ISP_PAGE_MASK;
+       end = ISP_PAGE_ALIGN(end);
+       phys &= ISP_PAGE_MASK;
+
+       ptr = start;
+       do {
+               idx = ISP_PTR_TO_L2_IDX(ptr);
+
+               pte = mfld_isp_get_pte(l2_pt, idx);
+
+               if (ISP_PTE_VALID(mmu, pte))
+                       mmu_remap_error(mmu, l1_pt, l1_idx,
+                                         l2_pt, idx, ptr, pte, phys);
+
+               pte = isp_pgaddr_to_pte_valid(mmu, phys);
+
+               mfld_isp_set_pte(l2_pt, idx, pte);
+               ptr += (1U << ISP_L2PT_OFFSET);
+               phys += (1U << ISP_L2PT_OFFSET);
+       } while (ptr < end && idx < ISP_L2PT_PTES - 1);
+
+       return 0;
+}
+
+/*
+ * Update L1 page table according to isp virtual address and page physical
+ * address
+ */
+static int mmu_l1_map(struct isp_mmu *mmu, unsigned int l1_pt,
+                       unsigned int start, unsigned int end, unsigned int phys)
+{
+       unsigned int l2_pt, ptr, l1_aligned;
+       unsigned int idx;
+       unsigned int l2_pte;
+       int ret;
+
+       l1_pt &= ISP_PAGE_MASK;
+
+       start = start & ISP_PAGE_MASK;
+       end = ISP_PAGE_ALIGN(end);
+       phys &= ISP_PAGE_MASK;
+
+       ptr = start;
+       do {
+               idx = ISP_PTR_TO_L1_IDX(ptr);
+
+               l2_pte = mfld_isp_get_pte(l1_pt, idx);
+
+               if (!ISP_PTE_VALID(mmu, l2_pte)) {
+                       l2_pt = alloc_page_table();
+                       if (l2_pt == NULL_PAGE) {
+                               v4l2_printk(KERN_ERR, &isp_dev,
+                                            "alloc page table fail.\n");
+                               return -ENOMEM;
+                       }
+
+                       l2_pte = isp_pgaddr_to_pte_valid(mmu, l2_pt);
+
+                       mfld_isp_set_pte(l1_pt, idx, l2_pte);
+               }
+
+               l2_pt = isp_pte_to_pgaddr(l2_pte);
+
+               l1_aligned = (ptr & ISP_PAGE_MASK) + (1U << ISP_L1PT_OFFSET);
+
+               if (l1_aligned < end) {
+                       ret = mmu_l2_map(mmu, l1_pt, idx,
+                                          l2_pt, ptr, l1_aligned, phys);
+                       phys += (l1_aligned - ptr);
+                       ptr = l1_aligned;
+               } else {
+                       ret = mmu_l2_map(mmu, l1_pt, idx,
+                                          l2_pt, ptr, end, phys);
+                       phys += (end - ptr);
+                       ptr = end;
+               }
+
+               if (ret) {
+                       v4l2_printk(KERN_ERR, &isp_dev,
+                                   "setup mapping in L2PT fail.\n");
+                       return ret;
+               }
+       } while (ptr < end && idx < ISP_L1PT_PTES - 1);
+
+       return 0;
+}
+
+/*
+ * Update page table according to isp virtual address and page physical
+ * address
+ */
+static int mmu_map(struct isp_mmu *mmu, unsigned int isp_virt,
+                    unsigned int phys, unsigned int pgnr)
+{
+       unsigned int start, end;
+       unsigned int l1_pt;
+       int ret;
+
+       if (!ISP_PTE_VALID(mmu, mmu->l1_pte)) {
+               /*
+                * allocate 1 new page for L1 page table
+                */
+               l1_pt = alloc_page_table();
+               if (l1_pt == NULL_PAGE) {
+                       v4l2_printk(KERN_ERR, &isp_dev,
+                                   "alloc page table fail.\n");
+                       return -ENOMEM;
+               }
+
+               /*
+                * setup L1 page table physical addr to MMU
+                */
+               ret = mmu->driver->set_pd_base(mmu, l1_pt);
+               if (ret) {
+                       v4l2_printk(KERN_ERR, &isp_dev,
+                                    "set page directory base address "
+                                    "fail.\n");
+                       return ret;
+               }
+
+               mmu->l1_pte = isp_pgaddr_to_pte_valid(mmu, l1_pt);
+
+               isp_mmu_flush_tlb(mmu);
+       }
+
+       l1_pt = isp_pte_to_pgaddr(mmu->l1_pte);
+
+       start = (isp_virt) & ISP_PAGE_MASK;
+       end = start + (pgnr << ISP_PAGE_OFFSET);
+       phys &= ISP_PAGE_MASK;
+
+       ret = mmu_l1_map(mmu, l1_pt, start, end, phys);
+
+       if (ret)
+               v4l2_printk(KERN_ERR, &isp_dev,
+                           "setup mapping in L1PT fail.\n");
+
+       return ret;
+}
+
+/*
+ * Free L2 page table according to isp virtual address and page physical
+ * address
+ */
+static void mmu_l2_unmap(struct isp_mmu *mmu, unsigned int l1_pt,
+                          unsigned int l1_idx, unsigned int l2_pt,
+                          unsigned int start, unsigned int end)
+{
+
+       unsigned int ptr;
+       unsigned int idx;
+       unsigned int pte;
+
+       l2_pt &= ISP_PAGE_MASK;
+
+       start = start & ISP_PAGE_MASK;
+       end = ISP_PAGE_ALIGN(end);
+
+       ptr = start;
+       do {
+               idx = ISP_PTR_TO_L2_IDX(ptr);
+
+               pte = mfld_isp_get_pte(l2_pt, idx);
+
+               if (!ISP_PTE_VALID(mmu, pte))
+                       mmu_unmap_l2_pte_error(mmu, l1_pt, l1_idx,
+                                                l2_pt, idx, ptr, pte);
+
+               mfld_isp_set_pte(l2_pt, idx, NULL_PTE);
+
+               ptr += (1U << ISP_L2PT_OFFSET);
+       } while (ptr < end && idx < ISP_L2PT_PTES - 1);
+}
+
+/*
+ * Free L1 page table according to isp virtual address and page physical
+ * address
+ */
+static void mmu_l1_unmap(struct isp_mmu *mmu, unsigned int l1_pt,
+                          unsigned int start, unsigned int end)
+{
+       unsigned int l2_pt, ptr, l1_aligned;
+       unsigned int idx;
+       unsigned int l2_pte;
+
+       l1_pt &= ISP_PAGE_MASK;
+
+       start = start & ISP_PAGE_MASK;
+       end = ISP_PAGE_ALIGN(end);
+
+       ptr = start;
+       do {
+               idx = ISP_PTR_TO_L1_IDX(ptr);
+
+               l2_pte = mfld_isp_get_pte(l1_pt, idx);
+
+               if (!ISP_PTE_VALID(mmu, l2_pte)) {
+                       mmu_unmap_l1_pte_error(mmu, l1_pt, idx, ptr, l2_pte);
+                       continue;
+               }
+
+               l2_pt = isp_pte_to_pgaddr(l2_pte);
+
+               l1_aligned = (ptr & ISP_PAGE_MASK) + (1U << ISP_L1PT_OFFSET);
+
+               if (l1_aligned < end) {
+                       mmu_l2_unmap(mmu, l1_pt, idx, l2_pt, ptr, l1_aligned);
+                       ptr = l1_aligned;
+               } else {
+                       mmu_l2_unmap(mmu, l1_pt, idx, l2_pt, ptr, end);
+                       ptr = end;
+               }
+               /*
+                * use the same L2 page next time, so we dont
+                * need to invalidate and free this PT.
+                */
+               /*      mfld_isp_set_pte(l1_pt, idx, NULL_PTE); */
+       } while (ptr < end && idx < ISP_L1PT_PTES - 1);
+}
+
+/*
+ * Free page table according to isp virtual address and page physical
+ * address
+ */
+static void mmu_unmap(struct isp_mmu *mmu, unsigned int isp_virt,
+                       unsigned int pgnr)
+{
+       unsigned int start, end;
+       unsigned int l1_pt;
+
+       if (!ISP_PTE_VALID(mmu, mmu->l1_pte)) {
+               mmu_unmap_l1_pt_error(mmu, mmu->l1_pte);
+               return;
+       }
+
+       l1_pt = isp_pte_to_pgaddr(mmu->l1_pte);
+
+       start = (isp_virt) & ISP_PAGE_MASK;
+       end = start + (pgnr << ISP_PAGE_OFFSET);
+
+       mmu_l1_unmap(mmu, l1_pt, start, end);
+}
+
+int isp_mmu_map(struct isp_mmu *mmu, unsigned int isp_virt,
+               unsigned int phys, unsigned int pgnr)
+{
+       return mmu_map(mmu, isp_virt, phys, pgnr);
+}
+
+void isp_mmu_unmap(struct isp_mmu *mmu, unsigned int isp_virt,
+                  unsigned int pgnr)
+{
+       mmu_unmap(mmu, isp_virt, pgnr);
+}
+
+static void isp_mmu_flush_tlb_range_default(struct isp_mmu *mmu,
+                                             unsigned int start,
+                                             unsigned int size)
+{
+       isp_mmu_flush_tlb(mmu);
+}
+
+/*MMU init for internal structure*/
+int isp_mmu_init(struct isp_mmu *mmu, struct isp_mmu_driver *driver)
+{
+       if (!mmu)               /* error */
+               return -EINVAL;
+       if (!driver)            /* error */
+               return -EINVAL;
+
+       if (!driver->name)
+               v4l2_printk(KERN_WARNING, &isp_dev,
+                           "NULL name for MMU driver...\n");
+
+       mmu->driver = driver;
+
+       if (!driver->set_pd_base || !driver->tlb_flush_all) {
+               v4l2_printk(KERN_ERR, &isp_dev,
+                           "set_pd_base or tlb_flush_all operation "
+                            "not provided.\n");
+               return -EINVAL;
+       }
+
+       if (!driver->tlb_flush_range)
+               driver->tlb_flush_range = isp_mmu_flush_tlb_range_default;
+
+       if (!driver->pte_valid_mask)
+               driver->pte_valid_mask = 0x1;
+
+       mmu->l1_pte = NULL_PTE;
+
+       mutex_init(&mmu->pt_mutex);
+
+       isp_mmu_flush_tlb(mmu);
+
+       return 0;
+}
+
+/*Free L1 and L2 page table*/
+void isp_mmu_exit(struct isp_mmu *mmu)
+{
+       unsigned int idx;
+       unsigned int pte;
+       unsigned int l1_pt, l2_pt;
+
+       if (!mmu)
+               return;
+
+       if (!ISP_PTE_VALID(mmu, mmu->l1_pte)) {
+               v4l2_printk(KERN_WARNING, &isp_dev,
+                           "invalid L1PT: pte = 0x%x\n",
+                           (unsigned int)mmu->l1_pte);
+               return;
+       }
+
+       l1_pt = isp_pte_to_pgaddr(mmu->l1_pte);
+
+       for (idx = 0; idx < ISP_L1PT_PTES; idx++) {
+               pte = mfld_isp_get_pte(l1_pt, idx);
+
+               if (ISP_PTE_VALID(mmu, pte)) {
+                       l2_pt = isp_pte_to_pgaddr(pte);
+
+                       free_page_table(l2_pt);
+               }
+       }
+
+       free_page_table(l1_pt);
+}
--
1.5.4.3
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0001-Medfiled-Camera-Image-ISP-driver-memory-management.patch
Type: application/octet-stream
Size: 90006 bytes
Desc: 0001-Medfiled-Camera-Image-ISP-driver-memory-management.patch
URL: <http://lists.meego.com/pipermail/meego-kernel/attachments/20101223/53a59683/attachment-0001.obj>


More information about the MeeGo-kernel mailing list