aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/samsung/fimc/fimc_dev.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video/samsung/fimc/fimc_dev.c')
-rw-r--r--drivers/media/video/samsung/fimc/fimc_dev.c334
1 files changed, 318 insertions, 16 deletions
diff --git a/drivers/media/video/samsung/fimc/fimc_dev.c b/drivers/media/video/samsung/fimc/fimc_dev.c
index a0a91cd..1ed79dd 100644
--- a/drivers/media/video/samsung/fimc/fimc_dev.c
+++ b/drivers/media/video/samsung/fimc/fimc_dev.c
@@ -32,9 +32,12 @@
#include <linux/videodev2_exynos_camera.h>
#include <linux/delay.h>
#include <linux/cma.h>
+#include <linux/dma-mapping.h>
#include <plat/fimc.h>
#include <plat/clock.h>
#include <mach/regs-pmu.h>
+#include <linux/cpufreq.h>
+#include <mach/cpufreq.h>
#include "fimc.h"
@@ -711,20 +714,32 @@ static struct fimc_control *fimc_register_controller(struct platform_device *pde
/* In Midas project, FIMC2 reserve memory is used by ION driver. */
if (id != 2) {
#endif
- sprintf(ctrl->cma_name, "%s%d", FIMC_CMA_NAME, ctrl->id);
- err = cma_info(&mem_info, ctrl->dev, 0);
- fimc_info1("%s : [cma_info] start_addr : 0x%x, end_addr : 0x%x, "
- "total_size : 0x%x, free_size : 0x%x\n",
- __func__, mem_info.lower_bound, mem_info.upper_bound,
- mem_info.total_size, mem_info.free_size);
- if (err) {
- fimc_err("%s: get cma info failed\n", __func__);
- ctrl->mem.size = 0;
+#ifdef CONFIG_USE_FIMC_CMA
+ if (id == 1) {
+ ctrl->mem.size =
+ CONFIG_VIDEO_SAMSUNG_MEMSIZE_FIMC1 * SZ_1K;
ctrl->mem.base = 0;
- } else {
- ctrl->mem.size = mem_info.total_size;
- ctrl->mem.base = (dma_addr_t)cma_alloc
- (ctrl->dev, ctrl->cma_name, (size_t)ctrl->mem.size, 0);
+ } else
+#endif
+ {
+ sprintf(ctrl->cma_name, "%s%d",
+ FIMC_CMA_NAME, ctrl->id);
+ err = cma_info(&mem_info, ctrl->dev, 0);
+ fimc_info1("%s : [cma_info] start_addr : 0x%x, "
+ " end_addr : 0x%x, total_size : 0x%x, "
+ "free_size : 0x%x\n", __func__,
+ mem_info.lower_bound, mem_info.upper_bound,
+ mem_info.total_size, mem_info.free_size);
+ if (err) {
+ fimc_err("%s: get cma info failed\n", __func__);
+ ctrl->mem.size = 0;
+ ctrl->mem.base = 0;
+ } else {
+ ctrl->mem.size = mem_info.total_size;
+ ctrl->mem.base = (dma_addr_t)cma_alloc
+ (ctrl->dev, ctrl->cma_name,
+ (size_t)ctrl->mem.size, 0);
+ }
}
#ifdef CONFIG_ION_EXYNOS
}
@@ -813,7 +828,7 @@ static struct fimc_control *fimc_register_controller(struct platform_device *pde
clk_put(fimc_src_clk);
return NULL;
}
- clk_set_rate(sclk_fimc_lclk, FIMC_CLK_RATE);
+ clk_set_rate(sclk_fimc_lclk, fimc_clk_rate());
clk_put(sclk_fimc_lclk);
clk_put(fimc_src_clk);
@@ -1021,6 +1036,255 @@ static int fimc_mmap(struct file *filp, struct vm_area_struct *vma)
return ret;
}
+#ifdef CONFIG_SLP_DMABUF
+/**
+ * _fimc_dmabuf_put() - release memory associated with
+ * a DMABUF shared buffer
+ */
+static void _fimc_dmabuf_put(struct vb2_buffer *vb)
+{
+ unsigned int plane;
+
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ void *mem_priv = vb->planes[plane].mem_priv;
+
+ if (mem_priv) {
+ dma_buf_detach(vb->planes[plane].dbuf,
+ vb->planes[plane].mem_priv);
+ dma_buf_put(vb->planes[plane].dbuf);
+ vb->planes[plane].dbuf = NULL;
+ vb->planes[plane].mem_priv = NULL;
+ }
+ }
+}
+
+void _fimc_queue_free(struct fimc_control *ctrl, enum v4l2_buf_type type)
+{
+ unsigned int buffer;
+ struct vb2_buffer *vb;
+
+ for (buffer = 0; buffer < VIDEO_MAX_FRAME; ++buffer) {
+ if (V4L2_TYPE_IS_OUTPUT(type))
+ vb = ctrl->out_bufs[buffer];
+ else
+ vb = ctrl->cap_bufs[buffer];
+
+ if (!vb)
+ continue;
+ _fimc_dmabuf_put(vb);
+ kfree(vb);
+ vb = NULL;
+ }
+}
+
+int fimc_queue_alloc(struct fimc_control *ctrl, enum v4l2_buf_type type,
+ enum v4l2_memory memory, unsigned int num_buffers,
+ unsigned int num_planes)
+{
+ unsigned int buffer;
+ struct vb2_buffer *vb;
+
+ for (buffer = 0; buffer < num_buffers; ++buffer) {
+ vb = kzalloc(sizeof(struct vb2_buffer), GFP_KERNEL);
+ if (!vb) {
+ fimc_err("%s: Memory alloc for buffer struct failed\n",
+ __func__);
+ break;
+ }
+
+ if (V4L2_TYPE_IS_MULTIPLANAR(type))
+ vb->v4l2_buf.length = num_planes;
+
+ vb->num_planes = num_planes;
+ vb->v4l2_buf.index = buffer;
+ vb->v4l2_buf.type = type;
+ vb->v4l2_buf.memory = memory;
+
+ if (V4L2_TYPE_IS_OUTPUT(type))
+ ctrl->out_bufs[buffer] = vb;
+ else
+ ctrl->cap_bufs[buffer] = vb;
+ }
+
+ for (buffer = num_buffers; buffer < VIDEO_MAX_FRAME; ++buffer) {
+ ctrl->out_bufs[buffer] = NULL;
+ ctrl->cap_bufs[buffer] = NULL;
+ }
+
+ return buffer;
+}
+
+static inline int _verify_planes_array(struct vb2_buffer *vb,
+ const struct v4l2_buffer *b)
+{
+ if (NULL == b->m.planes) {
+ printk(KERN_ERR "%s: Multi-planar buffer passwd but planes"
+ " array not provided\n", __func__);
+ return -EINVAL;
+ }
+ if (b->length < vb->num_planes || b->length > VIDEO_MAX_PLANES) {
+ printk(KERN_ERR "%s: Incorrect planes array length, "
+ "expected %d, got %d\n", __func__,
+ vb->num_planes, b->length);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int _fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b,
+ struct v4l2_plane *v4l2_planes)
+{
+ int plane;
+ int ret;
+
+ memcpy(b, &vb->v4l2_buf, offsetof(struct v4l2_buffer, m));
+ b->input = vb->v4l2_buf.input;
+ b->reserved = vb->v4l2_buf.reserved;
+
+ if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) {
+ ret = _verify_planes_array(vb, b);
+ if (ret)
+ return ret;
+
+ memcpy(b->m.planes, vb->v4l2_planes,
+ b->length * sizeof(struct v4l2_plane));
+
+ for (plane = 0; plane < vb->num_planes; ++plane)
+ b->m.planes[plane].m.fd =
+ vb->v4l2_planes[plane].m.fd;
+ } else {
+ b->length = vb->v4l2_planes[0].length;
+ b->bytesused = vb->v4l2_planes[0].bytesused;
+ b->m.fd = vb->v4l2_planes[0].m.fd;
+ }
+
+ return ret;
+}
+
+static int _fill_vb2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b,
+ struct v4l2_plane *v4l2_planes)
+{
+ unsigned int plane;
+ int ret;
+
+ if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) {
+ ret = _verify_planes_array(vb, b);
+ if (ret)
+ return ret;
+
+ if (V4L2_TYPE_IS_OUTPUT(b->type)) {
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ v4l2_planes[plane].bytesused =
+ b->m.planes[plane].bytesused;
+ v4l2_planes[plane].data_offset =
+ b->m.planes[plane].data_offset;
+ }
+ }
+ for (plane = 0; plane < vb->num_planes; ++plane)
+ v4l2_planes[plane].m.fd =
+ b->m.planes[plane].m.fd;
+
+ } else {
+ if (V4L2_TYPE_IS_OUTPUT(b->type))
+ v4l2_planes[0].bytesused =
+ b->bytesused;
+ v4l2_planes[0].m.fd = b->m.fd;
+ }
+
+ vb->v4l2_buf.field = b->field;
+ vb->v4l2_buf.timestamp = b->timestamp;
+ vb->v4l2_buf.input = b->input;
+
+ return 0;
+}
+
+int _qbuf_dmabuf(struct fimc_control *ctrl, struct vb2_buffer *vb,
+ struct v4l2_buffer *b)
+{
+ struct v4l2_plane planes[VIDEO_MAX_PLANES];
+ unsigned int plane;
+ struct sg_table *sg;
+ struct dma_buf_attachment *dba;
+ int ret;
+
+ ret = _fill_vb2_buffer(vb, b, planes);
+ if (ret)
+ return ret;
+
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ struct dma_buf *dbuf = dma_buf_get(planes[plane].m.fd);
+
+ if (IS_ERR_OR_NULL(dbuf)) {
+ fimc_err("dmabuf get error!!! %x\n", plane);
+ ret = PTR_ERR(dbuf);
+ goto err;
+ }
+ planes[plane].length = dbuf->size;
+
+ /* Skip the plane if already verified */
+ if (dbuf == vb->planes[plane].dbuf) {
+ dma_buf_put(dbuf);
+ continue;
+ }
+
+ vb->planes[plane].mem_priv = NULL;
+
+ dba = dma_buf_attach(dbuf, ctrl->dev);
+ if (IS_ERR(dba)) {
+ fimc_err("failed to attach dmabuf\n");
+ dma_buf_put(dbuf);
+ ret = PTR_ERR(dba);
+ goto err;
+ }
+
+ sg = dma_buf_map_attachment(dba, DMA_BIDIRECTIONAL);
+ if (IS_ERR(sg)) {
+ fimc_err("qbuf: failed acquiring dmabuf "
+ "memory for plane\n");
+ ret = PTR_ERR(sg);
+ goto err;
+ }
+ dba->priv = sg;
+
+ vb->planes[plane].dbuf = dbuf;
+ vb->planes[plane].mem_priv = dba;
+
+ vb->v4l2_planes[plane] = planes[plane];
+ }
+
+ return 0;
+
+err:
+ _fimc_dmabuf_put(vb);
+
+ return ret;
+}
+
+int _dqbuf_dmabuf(struct fimc_control *ctrl, struct vb2_buffer *vb,
+ struct v4l2_buffer *b)
+{
+ struct v4l2_plane planes[VIDEO_MAX_PLANES];
+ struct sg_table *sg[VIDEO_MAX_PLANES];
+ struct dma_buf_attachment *dba[VIDEO_MAX_PLANES];
+ unsigned int plane;
+ int ret;
+
+ ret = _fill_v4l2_buffer(vb, b, planes);
+ if (ret)
+ return ret;
+
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ dba[plane] = vb->planes[plane].mem_priv;
+ sg[plane] = dba[plane]->priv;
+ dma_buf_unmap_attachment(dba[plane],
+ sg[plane], DMA_FROM_DEVICE);
+ }
+
+ return 0;
+}
+#endif
+
static u32 fimc_poll(struct file *filp, poll_table *wait)
{
struct fimc_prv_data *prv_data =
@@ -1226,6 +1490,19 @@ static int fimc_open(struct file *filp)
goto kzalloc_err;
}
+#ifdef CONFIG_USE_FIMC_CMA
+ if (ctrl->id == 1) {
+ ctrl->mem.cpu_addr = dma_alloc_coherent(ctrl->dev,
+ ctrl->mem.size, &(ctrl->mem.base), 0);
+ if (!ctrl->mem.cpu_addr) {
+ printk(KERN_INFO "FIMC%d: dma_alloc_coherent failed\n",
+ ctrl->id);
+ ret = -ENOMEM;
+ goto dma_alloc_err;
+ }
+ }
+#endif
+
if (in_use == 1) {
#if (!defined(CONFIG_EXYNOS_DEV_PD) || !defined(CONFIG_PM_RUNTIME))
if (pdata->clk_on)
@@ -1276,6 +1553,11 @@ static int fimc_open(struct file *filp)
return 0;
+#ifdef CONFIG_USE_FIMC_CMA
+dma_alloc_err:
+ kfree(prv_data);
+#endif
+
kzalloc_err:
atomic_dec(&ctrl->in_use);
@@ -1425,6 +1707,9 @@ static int fimc_release(struct file *filp)
} else {
ctrl->out->ctx_used[ctx_id] = false;
}
+#ifdef CONFIG_SLP_DMABUF
+ _fimc_queue_free(ctrl, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+#endif
}
if (ctrl->cap) {
@@ -1445,6 +1730,9 @@ static int fimc_release(struct file *filp)
}
kfree(ctrl->cap);
ctrl->cap = NULL;
+#ifdef CONFIG_SLP_DMABUF
+ _fimc_queue_free(ctrl, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+#endif
}
/*
@@ -1462,6 +1750,14 @@ static int fimc_release(struct file *filp)
ctrl->fb.is_enable = 0;
}
+#ifdef CONFIG_USE_FIMC_CMA
+ if (ctrl->id == 1) {
+ dma_free_coherent(ctrl->dev, ctrl->mem.size, ctrl->mem.cpu_addr,
+ ctrl->mem.base);
+ ctrl->mem.base = 0;
+ ctrl->mem.cpu_addr = NULL;
+ }
+#endif
fimc_warn("FIMC%d %d released.\n",
ctrl->id, atomic_read(&ctrl->in_use));
@@ -1840,7 +2136,7 @@ static int __devinit fimc_probe(struct platform_device *pdev)
ret = device_create_file(&(pdev->dev), &dev_attr_range_mode);
if (ret < 0) {
fimc_err("failed to add sysfs entries for range mode\n");
- goto err_global;
+ goto err_create_file;
}
printk(KERN_INFO "FIMC%d registered successfully\n", ctrl->id);
#if (defined(CONFIG_EXYNOS_DEV_PD) && defined(CONFIG_PM_RUNTIME))
@@ -1848,7 +2144,7 @@ static int __devinit fimc_probe(struct platform_device *pdev)
ctrl->fimc_irq_wq = create_workqueue(buf);
if (ctrl->fimc_irq_wq == NULL) {
fimc_err("failed to create_workqueue\n");
- goto err_global;
+ goto err_wq;
}
INIT_WORK(&ctrl->work_struct, s3c_fimc_irq_work);
@@ -1869,6 +2165,12 @@ static int __devinit fimc_probe(struct platform_device *pdev)
return 0;
+err_wq:
+ device_remove_file(&(pdev->dev), &dev_attr_range_mode);
+
+err_create_file:
+ device_remove_file(&(pdev->dev), &dev_attr_log_level);
+
err_global:
video_unregister_device(ctrl->vd);