aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/samsung/mfc5x/mfc_buf.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video/samsung/mfc5x/mfc_buf.c')
-rw-r--r--drivers/media/video/samsung/mfc5x/mfc_buf.c287
1 files changed, 285 insertions, 2 deletions
diff --git a/drivers/media/video/samsung/mfc5x/mfc_buf.c b/drivers/media/video/samsung/mfc5x/mfc_buf.c
index e0e243d..d841a37 100644
--- a/drivers/media/video/samsung/mfc5x/mfc_buf.c
+++ b/drivers/media/video/samsung/mfc5x/mfc_buf.c
@@ -16,6 +16,13 @@
#include <linux/mm.h>
#include <linux/err.h>
+#ifdef CONFIG_SLP
+#include <linux/cma.h>
+#ifdef CONFIG_SLP_DMABUF
+#include <linux/dma-buf.h>
+#endif
+#endif
+
#include "mfc.h"
#include "mfc_mem.h"
#include "mfc_buf.h"
@@ -30,6 +37,168 @@
#include "ump_kernel_interface_vcm.h"
#endif
+#ifdef CONFIG_SLP_DMABUF
+struct mfc_dmabuf_buf {
+ dma_addr_t dma_addr;
+ unsigned long size;
+ /* fd exported from this buf object. */
+ int export_fd;
+ /* dma buf exported from this buf object. */
+ struct dma_buf *export_dma_buf;
+ struct dma_buf_attachment *db_attach;
+ atomic_t refcount;
+};
+
+static void _mfc_dmabuf_put(struct mfc_dmabuf_buf *buf)
+{
+ if (atomic_dec_and_test(&buf->refcount)) {
+ /*
+ * In legacy driver, dmabuf functions don't control
+ * cma memory allocation and free.
+ * so, currently we comment cma_free function.
+ * cma_free(buf->dma_addr);
+ */
+ kfree(buf);
+ }
+}
+
+static int mfc_attach_dmabuf(struct dma_buf *dmabuf, struct device *dev,
+ struct dma_buf_attachment *attach)
+{
+ mfc_dbg("mfc_attach_dmabuf: called !\n");
+ return 0;
+}
+
+static void mfc_detach_dmabuf(struct dma_buf *dmabuf,
+ struct dma_buf_attachment *attach)
+{
+ mfc_dbg("mfc_detach_dmabuf: called !\n");
+ dma_buf_put(dmabuf);
+}
+
+static struct sg_table *
+ mfc_map_dmabuf(struct dma_buf_attachment *attach,
+ enum dma_data_direction direction)
+{
+ struct mfc_dmabuf_buf *buf;
+ struct sg_table *sgt;
+ int ret;
+ int val;
+
+ mfc_dbg("mfc_map_dmabuf: called !\n");
+ if (!attach->dmabuf->priv) {
+ mfc_err("mfc_map_dmabuf: failed : attach->dmabuf->priv is NULL\n");
+ return NULL;
+ }
+ buf = attach->dmabuf->priv;
+ sgt = kzalloc(sizeof(struct sg_table), GFP_KERNEL);
+ if (!sgt) {
+ mfc_err("mfc_map_dmabuf: failed to allocate sg table.\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ ret = sg_alloc_table(sgt, 1, GFP_KERNEL);
+ if (ret < 0) {
+ mfc_err("mfc_map_dmabuf: failed to allocate scatter list.\n");
+ kfree(sgt);
+ sgt = NULL;
+ return ERR_PTR(-ENOMEM);
+ }
+
+ sg_init_table(sgt->sgl, 1);
+ sg_dma_len(sgt->sgl) = buf->size;
+ sg_set_page(sgt->sgl, pfn_to_page(PFN_DOWN(buf->dma_addr)),
+ buf->size, 0);
+ sg_dma_address(sgt->sgl) = buf->dma_addr;
+
+ /*
+ * increase reference count of this buf object.
+ *
+ * Note:
+ * alloated physical memory region is being shared with others so
+ * this region shouldn't be released until all references of this
+ * region will be dropped by mfc_unmap_dmabuf().
+ */
+ val = atomic_inc_return(&buf->refcount);
+ mfc_dbg("mfc_map_dmabuf: refcount: %d\n", val);
+
+ return sgt;
+}
+
+static void mfc_unmap_dmabuf(struct dma_buf_attachment *attach,
+ struct sg_table *sgt, enum dma_data_direction direction)
+{
+ int val = 0;
+ struct mfc_dmabuf_buf *buf;
+
+ mfc_dbg("mfc_unmap_dmabuf: called !\n");
+ buf = attach->dmabuf->priv;
+
+ sg_free_table(sgt);
+ kfree(sgt);
+ sgt = NULL;
+
+ val = atomic_dec_return(&buf->refcount);
+ mfc_dbg("mfc_unmap_dmabuf: refcount: %d\n", val);
+}
+
+static void mfc_release_dmabuf(struct dma_buf *dmabuf)
+{
+ struct mfc_dmabuf_buf *buf;
+
+ mfc_dbg("mfc_release_dmabuf: called !\n");
+ if (!dmabuf->priv) {
+ mfc_dbg("mfc_release_dmabuf: failed: dmabuf->priv is NULL\n");
+ return;
+ }
+ buf = dmabuf->priv;
+ if (buf->export_dma_buf == dmabuf) {
+ mfc_dbg("mfc_release_dmabuf: called !\n");
+ buf->export_fd = -1;
+ buf->export_dma_buf = NULL;
+
+ _mfc_dmabuf_put(buf);
+ }
+}
+
+static void *mfc_kmap_atomic_dmabuf(struct dma_buf *dma_buf,
+ unsigned long page_num)
+{
+ return NULL;
+}
+
+static void mfc_kunmap_atomic_dmabuf(struct dma_buf *dma_buf,
+ unsigned long page_num,
+ void *addr)
+{
+
+}
+
+static void *mfc_kmap_dmabuf(struct dma_buf *dma_buf,
+ unsigned long page_num)
+{
+ return NULL;
+}
+
+static void mfc_kunmap_dmabuf(struct dma_buf *dma_buf,
+ unsigned long page_num, void *addr)
+{
+
+}
+
+static struct dma_buf_ops mfc_dmabuf_ops = {
+ .attach = mfc_attach_dmabuf,
+ .detach = mfc_detach_dmabuf,
+ .map_dma_buf = mfc_map_dmabuf,
+ .unmap_dma_buf = mfc_unmap_dmabuf,
+ .release = mfc_release_dmabuf,
+ .kmap = mfc_kmap_dmabuf,
+ .kmap_atomic = mfc_kmap_atomic_dmabuf,
+ .kunmap = mfc_kunmap_dmabuf,
+ .kunmap_atomic = mfc_kunmap_atomic_dmabuf,
+};
+#endif
+
#define PRINT_BUF
#undef DEBUG_ALLOC_FREE
@@ -122,7 +291,7 @@ static int mfc_put_free_buf(unsigned long addr, unsigned int size, int port)
/* 0x00: not merged, 0x01: prev merged, 0x02: next merged */
int merged = 0x00;
- if (!size)
+ if ((!size) || (port >= MFC_MAX_MEM_PORT_NUM))
return -EINVAL;
mfc_dbg("addr: 0x%08lx, size: %d, port: %d\n", addr, size, port);
@@ -376,6 +545,13 @@ void mfc_final_buf(void)
kfree(alloc);
}
#else
+#ifdef CONFIG_SLP
+ if (alloc->real) {
+ cma_free(alloc->real);
+ list_del(&alloc->list);
+ kfree(alloc);
+ }
+#else
if (mfc_put_free_buf(alloc->real,
alloc->size, port) < 0) {
@@ -385,6 +561,7 @@ void mfc_final_buf(void)
kfree(alloc);
}
#endif
+#endif
}
}
@@ -466,6 +643,15 @@ struct mfc_alloc_buffer *_mfc_alloc_buf(
#elif defined(CONFIG_S5P_VMEM)
int align_size = 0;
#endif
+#ifdef CONFIG_SLP
+ struct mfc_dev *dev = ctx->dev;
+ size_t available_size;
+ struct cma_info cma_infos;
+#ifdef CONFIG_SLP_DMABUF
+ struct mfc_dmabuf_buf *buf;
+ int flags = 0;
+#endif
+#endif
/*
unsigned long flags;
*/
@@ -486,8 +672,62 @@ struct mfc_alloc_buffer *_mfc_alloc_buf(
/*
spin_lock_irqsave(&lock, flags);
*/
+#ifdef CONFIG_SLP
+ if (cma_info(&cma_infos, dev->device, port ? "B" : "A")) {
+ mfc_info("failed to get CMA info of 'mfc'\n");
+ kfree(alloc);
+ return NULL;
+ }
+ available_size = cma_infos.free_size;
+ if (available_size > MAX_MEM_OFFSET) {
+ mfc_warn("<Warning> too large 'mfc' reserved memory, "
+ "size will be shrink (%d:%d)\n",
+ size >> 10, MAX_MEM_OFFSET >> 10);
+ size = MAX_MEM_OFFSET;
+ }
+ addr = cma_alloc(dev->device, port ? "B" : "A", size, align);
+ if (IS_ERR_VALUE(addr)) {
+ mfc_err("failed to get rsv. memory from CMA");
+ kfree(alloc);
+ return NULL;
+ }
+#ifdef CONFIG_SLP_DMABUF
+ buf = kzalloc(sizeof(struct mfc_dmabuf_buf), GFP_KERNEL);
+ if (!buf) {
+ mfc_err("failed to alloc mfc_dmabuf_buf");
+ kfree(alloc);
+ cma_free(addr);
+ return NULL;
+ }
+ buf->dma_addr = addr;
+ buf->size = size;
+
+ buf->export_dma_buf = dma_buf_export(buf, &mfc_dmabuf_ops,
+ buf->size, 0600);
+ if (!buf->export_dma_buf) {
+ mfc_err("fail to export dma_buf\n");
+ kfree(alloc);
+ cma_free(addr);
+ kfree(buf);
+ return NULL;
+ }
+ buf->export_fd = dma_buf_fd(buf->export_dma_buf, flags);
+ if (buf->export_fd < 0) {
+ mfc_err(" fail to get fd from dmabuf.\n");
+ kfree(alloc);
+ cma_free(addr);
+ kfree(buf);
+ dma_buf_put(buf->export_dma_buf);
+ return NULL;
+ }
+ alloc->dmabuf_fd = buf->export_fd;
+ atomic_inc(&buf->refcount);
+ mfc_dbg(" buf->export_fd = %d\n", buf->export_fd);
+#endif
+#else
addr = mfc_get_free_buf(size, align, port);
+#endif
mfc_dbg("mfc_get_free_buf: 0x%08lx\n", addr);
@@ -783,15 +1023,22 @@ int _mfc_free_buf(unsigned long real)
kfree(alloc);
}
#else
+#ifdef CONFIG_SLP
+ if (alloc->real) {
+ cma_free(alloc->real);
+ list_del(&alloc->list);
+ kfree(alloc);
+ }
+#else
if (mfc_put_free_buf(alloc->real,
alloc->size, port) < 0) {
-
mfc_err("failed to add free buffer\n");
} else {
list_del(&alloc->list);
kfree(alloc);
}
#endif
+#endif
break;
}
}
@@ -839,6 +1086,13 @@ void mfc_free_buf_type(int owner, int type)
alloc = list_entry(pos, struct mfc_alloc_buffer, list);
if ((alloc->owner == owner) && (alloc->type == type)) {
+#ifdef CONFIG_SLP
+ if (alloc->real) {
+ cma_free(alloc->real);
+ list_del(&alloc->list);
+ kfree(alloc);
+ }
+#else
if (mfc_put_free_buf(alloc->real,
alloc->size, port) < 0) {
@@ -847,6 +1101,7 @@ void mfc_free_buf_type(int owner, int type)
list_del(&alloc->list);
kfree(alloc);
}
+#endif
}
}
}
@@ -905,6 +1160,13 @@ void mfc_free_buf_inst(int owner)
kfree(alloc);
}
#else
+#ifdef CONFIG_SLP
+ if (alloc->real) {
+ cma_free(alloc->real);
+ list_del(&alloc->list);
+ kfree(alloc);
+ }
+#else
if (mfc_put_free_buf(alloc->real,
alloc->size, port) < 0) {
@@ -914,6 +1176,7 @@ void mfc_free_buf_inst(int owner)
kfree(alloc);
}
#endif
+#endif
}
}
}
@@ -1034,4 +1297,24 @@ void *mfc_get_buf_ump_handle(unsigned long real)
return NULL;
}
#endif
+#ifdef CONFIG_SLP_DMABUF
+int mfc_get_buf_dmabuf(unsigned long real)
+{
+ struct list_head *pos, *nxt;
+ int port;
+ struct mfc_alloc_buffer *alloc;
+
+ mfc_dbg("real: 0x%08lx\n", real);
+ for (port = 0; port < mfc_mem_count(); port++) {
+ list_for_each_safe(pos, nxt, &mfc_alloc_head[port]) {
+ alloc = list_entry(pos, struct mfc_alloc_buffer, list);
+
+ if (alloc->real == real)
+ return alloc->dmabuf_fd;
+ }
+ }
+
+ return -EINVAL;
+}
+#endif