[RFC 4/4] video: adf: add memblock helper

2013-08-28 Thread Greg Hackmann
Provides a dma-buf exporter for memblocks, mainly useful for ADF devices
to wrap their bootloader logos

Signed-off-by: Greg Hackmann 
---
 drivers/video/adf/Kconfig|   5 ++
 drivers/video/adf/Makefile   |   2 +
 drivers/video/adf/adf_memblock.c | 150 +++
 include/video/adf_memblock.h |  20 ++
 4 files changed, 177 insertions(+)
 create mode 100644 drivers/video/adf/adf_memblock.c
 create mode 100644 include/video/adf_memblock.h

diff --git a/drivers/video/adf/Kconfig b/drivers/video/adf/Kconfig
index 30b0611..ad0c0eb 100644
--- a/drivers/video/adf/Kconfig
+++ b/drivers/video/adf/Kconfig
@@ -8,3 +8,8 @@ menuconfig ADF_DISPLAY_CORE
depends on ADF
depends on DISPLAY_CORE
tristate "Helper for implementing ADF interface ops with Display Core 
devices"
+
+menuconfig ADF_MEMBLOCK
+   depends on ADF
+   depends on HAVE_MEMBLOCK
+   tristate "Helper for using memblocks as buffers in ADF drivers"
diff --git a/drivers/video/adf/Makefile b/drivers/video/adf/Makefile
index 30164ee..97f9c98 100644
--- a/drivers/video/adf/Makefile
+++ b/drivers/video/adf/Makefile
@@ -10,3 +10,5 @@ obj-$(CONFIG_ADF) += adf.o \
 obj-$(CONFIG_COMPAT) += adf_fops32.o
 
 obj-$(CONFIG_ADF_DISPLAY_CORE) += adf_display.o
+
+obj-$(CONFIG_ADF_MEMBLOCK) += adf_memblock.o
diff --git a/drivers/video/adf/adf_memblock.c b/drivers/video/adf/adf_memblock.c
new file mode 100644
index 000..a1b7ec6
--- /dev/null
+++ b/drivers/video/adf/adf_memblock.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include 
+#include 
+#include 
+#include 
+
+struct adf_memblock_pdata {
+   phys_addr_t base;
+};
+
+struct sg_table *adf_memblock_map(struct dma_buf_attachment *attach,
+   enum dma_data_direction direction)
+{
+   struct adf_memblock_pdata *pdata = attach->dmabuf->priv;
+   unsigned long pfn = (pdata->base >> PAGE_SHIFT);
+   struct page *page = pfn_to_page(pfn);
+   struct sg_table *table;
+   int ret;
+
+   table = kzalloc(sizeof(*table), GFP_KERNEL);
+   if (!table)
+   return ERR_PTR(-ENOMEM);
+
+   ret = sg_alloc_table(table, 1, GFP_KERNEL);
+   if (ret < 0)
+   goto err;
+
+   sg_set_page(table->sgl, page, attach->dmabuf->size, 0);
+   return table;
+
+err:
+   kfree(table);
+   return ERR_PTR(ret);
+}
+
+void adf_memblock_unmap(struct dma_buf_attachment *attach,
+   struct sg_table *table, enum dma_data_direction direction)
+{
+   sg_free_table(table);
+}
+
+static void __init_memblock adf_memblock_release(struct dma_buf *buf)
+{
+   struct adf_memblock_pdata *pdata = buf->priv;
+   int err = memblock_free(pdata->base, buf->size);
+
+   if (err < 0)
+   pr_warn("%s: freeing memblock failed: %d\n", __func__, err);
+   kfree(pdata);
+}
+
+static void *adf_memblock_do_kmap(struct dma_buf *buf, unsigned long pgoffset,
+   bool atomic)
+{
+   struct adf_memblock_pdata *pdata = buf->priv;
+   unsigned long pfn = (pdata->base >> PAGE_SHIFT) + pgoffset;
+   struct page *page = pfn_to_page(pfn);
+
+   if (atomic)
+   return kmap_atomic(page);
+   else
+   return kmap(page);
+}
+
+static void *adf_memblock_kmap_atomic(struct dma_buf *buf,
+   unsigned long pgoffset)
+{
+   return adf_memblock_do_kmap(buf, pgoffset, true);
+}
+
+static void adf_memblock_kunmap_atomic(struct dma_buf *buf,
+   unsigned long pgoffset, void *vaddr)
+{
+   kunmap_atomic(vaddr);
+}
+
+static void *adf_memblock_kmap(struct dma_buf *buf, unsigned long pgoffset)
+{
+   return adf_memblock_do_kmap(buf, pgoffset, false);
+}
+
+static void adf_memblock_kunmap(struct dma_buf *buf, unsigned long pgoffset,
+   void *vaddr)
+{
+   kunmap(vaddr);
+}
+
+static int adf_memblock_mmap(struct dma_buf *buf, struct vm_area_struct *vma)
+{
+   struct adf_memblock_pdata *pdata = buf->priv;
+   unsigned long pfn = pdata->base >> PAGE_SHIFT;
+
+   return remap_pfn_range(vma, vma->vm_start, pfn,
+   vma->vm_end - vma->vm_start, vma->vm_page_prot);
+}
+
+struct dma_buf_ops adf_memblock_ops = {
+   .map_dma_buf = adf_memblock_map,
+   .unmap_dma_buf = adf_memblock_unmap,
+   .release = adf_memblock_release,
+   .kmap_atomic = adf_memblock_kmap_atomic,
+   .kunmap_atomic = adf_memblock_kunmap_atomic,
+   .kmap = adf_memblock_kmap,
+   .kun

[RFC 4/4] video: adf: add memblock helper

2013-08-28 Thread Greg Hackmann
Provides a dma-buf exporter for memblocks, mainly useful for ADF devices
to wrap their bootloader logos

Signed-off-by: Greg Hackmann 
---
 drivers/video/adf/Kconfig|   5 ++
 drivers/video/adf/Makefile   |   2 +
 drivers/video/adf/adf_memblock.c | 150 +++
 include/video/adf_memblock.h |  20 ++
 4 files changed, 177 insertions(+)
 create mode 100644 drivers/video/adf/adf_memblock.c
 create mode 100644 include/video/adf_memblock.h

diff --git a/drivers/video/adf/Kconfig b/drivers/video/adf/Kconfig
index 30b0611..ad0c0eb 100644
--- a/drivers/video/adf/Kconfig
+++ b/drivers/video/adf/Kconfig
@@ -8,3 +8,8 @@ menuconfig ADF_DISPLAY_CORE
depends on ADF
depends on DISPLAY_CORE
tristate "Helper for implementing ADF interface ops with Display Core 
devices"
+
+menuconfig ADF_MEMBLOCK
+   depends on ADF
+   depends on HAVE_MEMBLOCK
+   tristate "Helper for using memblocks as buffers in ADF drivers"
diff --git a/drivers/video/adf/Makefile b/drivers/video/adf/Makefile
index 30164ee..97f9c98 100644
--- a/drivers/video/adf/Makefile
+++ b/drivers/video/adf/Makefile
@@ -10,3 +10,5 @@ obj-$(CONFIG_ADF) += adf.o \
 obj-$(CONFIG_COMPAT) += adf_fops32.o

 obj-$(CONFIG_ADF_DISPLAY_CORE) += adf_display.o
+
+obj-$(CONFIG_ADF_MEMBLOCK) += adf_memblock.o
diff --git a/drivers/video/adf/adf_memblock.c b/drivers/video/adf/adf_memblock.c
new file mode 100644
index 000..a1b7ec6
--- /dev/null
+++ b/drivers/video/adf/adf_memblock.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include 
+#include 
+#include 
+#include 
+
+struct adf_memblock_pdata {
+   phys_addr_t base;
+};
+
+struct sg_table *adf_memblock_map(struct dma_buf_attachment *attach,
+   enum dma_data_direction direction)
+{
+   struct adf_memblock_pdata *pdata = attach->dmabuf->priv;
+   unsigned long pfn = (pdata->base >> PAGE_SHIFT);
+   struct page *page = pfn_to_page(pfn);
+   struct sg_table *table;
+   int ret;
+
+   table = kzalloc(sizeof(*table), GFP_KERNEL);
+   if (!table)
+   return ERR_PTR(-ENOMEM);
+
+   ret = sg_alloc_table(table, 1, GFP_KERNEL);
+   if (ret < 0)
+   goto err;
+
+   sg_set_page(table->sgl, page, attach->dmabuf->size, 0);
+   return table;
+
+err:
+   kfree(table);
+   return ERR_PTR(ret);
+}
+
+void adf_memblock_unmap(struct dma_buf_attachment *attach,
+   struct sg_table *table, enum dma_data_direction direction)
+{
+   sg_free_table(table);
+}
+
+static void __init_memblock adf_memblock_release(struct dma_buf *buf)
+{
+   struct adf_memblock_pdata *pdata = buf->priv;
+   int err = memblock_free(pdata->base, buf->size);
+
+   if (err < 0)
+   pr_warn("%s: freeing memblock failed: %d\n", __func__, err);
+   kfree(pdata);
+}
+
+static void *adf_memblock_do_kmap(struct dma_buf *buf, unsigned long pgoffset,
+   bool atomic)
+{
+   struct adf_memblock_pdata *pdata = buf->priv;
+   unsigned long pfn = (pdata->base >> PAGE_SHIFT) + pgoffset;
+   struct page *page = pfn_to_page(pfn);
+
+   if (atomic)
+   return kmap_atomic(page);
+   else
+   return kmap(page);
+}
+
+static void *adf_memblock_kmap_atomic(struct dma_buf *buf,
+   unsigned long pgoffset)
+{
+   return adf_memblock_do_kmap(buf, pgoffset, true);
+}
+
+static void adf_memblock_kunmap_atomic(struct dma_buf *buf,
+   unsigned long pgoffset, void *vaddr)
+{
+   kunmap_atomic(vaddr);
+}
+
+static void *adf_memblock_kmap(struct dma_buf *buf, unsigned long pgoffset)
+{
+   return adf_memblock_do_kmap(buf, pgoffset, false);
+}
+
+static void adf_memblock_kunmap(struct dma_buf *buf, unsigned long pgoffset,
+   void *vaddr)
+{
+   kunmap(vaddr);
+}
+
+static int adf_memblock_mmap(struct dma_buf *buf, struct vm_area_struct *vma)
+{
+   struct adf_memblock_pdata *pdata = buf->priv;
+   unsigned long pfn = pdata->base >> PAGE_SHIFT;
+
+   return remap_pfn_range(vma, vma->vm_start, pfn,
+   vma->vm_end - vma->vm_start, vma->vm_page_prot);
+}
+
+struct dma_buf_ops adf_memblock_ops = {
+   .map_dma_buf = adf_memblock_map,
+   .unmap_dma_buf = adf_memblock_unmap,
+   .release = adf_memblock_release,
+   .kmap_atomic = adf_memblock_kmap_atomic,
+   .kunmap_atomic = adf_memblock_kunmap_atomic,
+   .kmap = adf_memblock_kmap,
+   .kunm