summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWojciech Bieganski <wbieganski@antmicro.com>2014-03-06 12:32:34 +0100
committerMarcel Ziswiler <marcel.ziswiler@toradex.com>2014-03-13 16:04:08 +0100
commitf195d3bc9cfb38159f8a3a299222396e0005aa91 (patch)
tree71f8b85c1ea05b6aaab2bfda1456c2d2a913f569
parent996cd0be0d0327eeb90f95b57d24b7a0eb551cee (diff)
-rw-r--r--drivers/media/video/tegra_v4l2_camera.c165
1 files changed, 108 insertions, 57 deletions
diff --git a/drivers/media/video/tegra_v4l2_camera.c b/drivers/media/video/tegra_v4l2_camera.c
index 14a74cd6b7cc..054415b15c73 100644
--- a/drivers/media/video/tegra_v4l2_camera.c
+++ b/drivers/media/video/tegra_v4l2_camera.c
@@ -21,6 +21,8 @@
#include <linux/pm_runtime.h>
#include <linux/nvhost.h>
+#include <linux/kthread.h>
+
#include <mach/iomap.h>
#include <media/soc_camera.h>
@@ -263,7 +265,6 @@ MODULE_PARM_DESC(internal_sync, "enable internal vsync and hsync decoded " \
struct tegra_buffer {
struct vb2_buffer vb; /* v4l buffer must be first */
struct list_head queue;
-
/*
* Various buffer addresses shadowed so we don't have to recalculate
* per frame. These are calculated during videobuf_prepare.
@@ -275,6 +276,9 @@ struct tegra_buffer {
dma_addr_t start_addr_u;
dma_addr_t start_addr_v;
void* virtual_addr;
+
+ dma_addr_t internal_phys_addr;
+ void* internal_virtual_addr;
};
struct tegra_camera_dev {
@@ -303,6 +307,9 @@ struct tegra_camera_dev {
u32 syncpt_vi;
u32 syncpt_csi;
+ /* private buffer for non-interlaced frame */
+ struct vb2_dc_buf *internal_vbuf;
+
/* Debug */
int num_frames;
};
@@ -352,6 +359,11 @@ static const struct soc_mbus_pixelfmt tegra_camera_formats[] = {
},
};
+struct thread_args {
+ struct tegra_camera_dev *pcdev;
+ struct tegra_buffer *tb;
+};
+
static struct tegra_buffer *to_tegra_vb(struct vb2_buffer *vb)
{
return container_of(vb, struct tegra_buffer, vb);
@@ -550,6 +562,7 @@ static void tegra_camera_capture_setup_vip(struct tegra_camera_dev *pcdev,
{
struct soc_camera_device *icd = pcdev->icd;
+
TC_VI_REG_WT(pcdev, TEGRA_VI_VI_CORE_CONTROL, 0x00000000);
TC_VI_REG_WT(pcdev, TEGRA_VI_VI_INPUT_CONTROL,
@@ -588,6 +601,18 @@ static void tegra_camera_capture_setup_vip(struct tegra_camera_dev *pcdev,
// TC_VI_REG_WT(pcdev, TEGRA_VI_CAMERA_CONTROL, 0x00000004);
}
+struct vb2_dc_buf {
+ struct vb2_dc_conf *conf;
+ void *vaddr;
+ dma_addr_t paddr;
+ unsigned long size;
+ struct vm_area_struct *vma;
+ atomic_t refcount;
+ //struct vb2_vmarea_handler handler;
+
+ struct nvmap_handle_ref *nvmap_ref;
+};
+
static void tegra_camera_capture_setup(struct tegra_camera_dev *pcdev)
{
struct soc_camera_device *icd = pcdev->icd;
@@ -603,6 +628,15 @@ static void tegra_camera_capture_setup(struct tegra_camera_dev *pcdev)
icd->current_fmt->host_fmt);
int frame_count = 1;
+ // prepare internal_vbuf in case of later interlacing
+ if(IS_INTERLACED)
+ {
+ if(output_fourcc == V4L2_PIX_FMT_YUV420 || output_fourcc == V4L2_PIX_FMT_YVU420)
+ pcdev->internal_vbuf = vb2_dma_nvmap_memops.alloc(pcdev->alloc_ctx, ((icd->user_height * icd->user_width) + (icd->user_height * icd->user_width)/2 ));
+ else
+ pcdev->internal_vbuf = vb2_dma_nvmap_memops.alloc(pcdev->alloc_ctx, (icd->user_height * bytes_per_line));
+ }
+
switch (input_code) {
case V4L2_MBUS_FMT_UYVY8_2X8:
yuv_input_format = 0x2;
@@ -696,45 +730,47 @@ static void tegra_camera_capture_setup(struct tegra_camera_dev *pcdev)
/* First output memory enabled */
TC_VI_REG_WT(pcdev, TEGRA_VI_VI_ENABLE, 0x00000000);
+}
+void interlace_and_copy(void* dst, void* src, int width, int height)
+{
+ int l;
+
+ for(l = 0; l <= (height-1); l++)
+ {
+ if(l < (height/2))
+ memcpy(dst + (width * (2 * l)), (void*) ((unsigned int)(src) + (width * l)), width);
+ else
+ memcpy(dst + (width * (2 * (l-(height/2)) + 1) ), (void*) ((unsigned int)(src) + (width * l)), width);
+ }
}
-//
-// make_interlaced
-//
-// make one interlaced frame out of two frames in the buffer @ addr
-// this function must be issued for Y, U and V buffers.
-//
-static void make_interlaced(uint8_t * addr, int width, int height) {
- uint8_t starting_line_buf[width];
- bool lines[height];
- int i;
- int destination_line, starting_line;
- for (i = 1; i < height-1; i++) lines[i] = false;
- lines[0] = true;
- lines[height-1] = true;
+int make_interlaced(void* arg)
+{
+ struct thread_args *ta = arg;
+ struct soc_camera_device *icd = ta->pcdev->icd;
+ void *src = ta->tb->internal_virtual_addr;
+ void *dst = ta->tb->virtual_addr;
+ int bytes_per_line;
- #define real_line(i) ( (i % 2) ? ((i / 2) + (height/2)) : (i / 2) )
- while (1) {
- starting_line = -1;
- for (i = 1; i < ((height)-1); i++) if (!lines[i]) {
- starting_line = i;
- break;
- }
- if (starting_line == -1) break; // all is done
-
- memcpy(starting_line_buf, (void*) ((unsigned int)(addr) + (width*starting_line)), width);
- destination_line = starting_line;
- while (1) {
- lines[destination_line] = true;
- if (real_line(destination_line) == starting_line) {
- memcpy(addr + width * destination_line, starting_line_buf, width);
- break;
- }
- memcpy(addr + (width * destination_line), (void*) ((unsigned int)(addr) + (width * real_line(destination_line))), width);
- destination_line = real_line(destination_line);
- }
+ if(icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_YUV420 || icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_YVU420 )
+ {
+ interlace_and_copy(dst, src, icd->user_width, icd->user_height); // Y
+ interlace_and_copy(dst + (icd->user_width * icd->user_height),
+ src + (icd->user_width * icd->user_height),
+ icd->user_width,
+ icd->user_height/4); // U
+ interlace_and_copy(dst + (icd->user_width * icd->user_height) + (icd->user_width * icd->user_height)/4,
+ src + (icd->user_width * icd->user_height) + (icd->user_width * icd->user_height)/4,
+ icd->user_width,
+ icd->user_height/4); // V
}
+ else
+ {
+ bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, icd->current_fmt->host_fmt);
+ interlace_and_copy(dst, src, bytes_per_line, icd->user_height); // Y, U, V
+ }
+ do_exit(0);
}
static int tegra_camera_capture_start(struct tegra_camera_dev *pcdev,
@@ -743,8 +779,8 @@ static int tegra_camera_capture_start(struct tegra_camera_dev *pcdev,
struct soc_camera_device *icd = pcdev->icd;
int port = pcdev->pdata->port;
int err;
- int bytes_per_line;
- uint8_t *src;
+ struct task_struct *interlace_task;
+ struct thread_args ta;
switch (icd->current_fmt->host_fmt->fourcc) {
case V4L2_PIX_FMT_YUV420:
@@ -786,12 +822,19 @@ static int tegra_camera_capture_start(struct tegra_camera_dev *pcdev,
TC_VI_REG_WT(pcdev, TEGRA_VI_CAMERA_CONTROL,
0x00000005); // start & stop
+ // start the interlacing task now
+ if(IS_INTERLACED)
+ {
+ ta.tb = buf;
+ ta.pcdev = pcdev;
+ interlace_task = kthread_run(make_interlaced, &ta, "interlacing thread");
+ }
+
/*
* Only wait on CSI frame end syncpt if we're using CSI. Otherwise,
* wait on VIP VSYNC syncpt.
*/
-
- pcdev->syncpt_vi++;
+ pcdev->syncpt_vi++;
pcdev->syncpt_csi++;
if (tegra_camera_port_is_csi(port))
@@ -809,10 +852,10 @@ static int tegra_camera_capture_start(struct tegra_camera_dev *pcdev,
if (IS_INTERLACED) {
- TC_VI_REG_WT(pcdev, TEGRA_VI_CAMERA_CONTROL,
+ TC_VI_REG_WT(pcdev, TEGRA_VI_CAMERA_CONTROL,
0x00000005); // start & stop
- pcdev->syncpt_vi++;
+ pcdev->syncpt_vi++;
pcdev->syncpt_csi++;
err = nvhost_syncpt_wait_timeout_ext(pcdev->ndev,
@@ -820,18 +863,6 @@ static int tegra_camera_capture_start(struct tegra_camera_dev *pcdev,
pcdev->syncpt_vi,
TEGRA_SYNCPT_VI_WAIT_TIMEOUT,
NULL);
-
- src = (uint8_t*)(buf->virtual_addr);
- if(icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_YUV420 || icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_YVU420 ) {
- make_interlaced(src, icd->user_width, icd->user_height); // Y
- make_interlaced(src + icd->user_width * icd->user_height, icd->user_width, icd->user_height/4); // U
- make_interlaced(src + icd->user_width * icd->user_height + (icd->user_width * icd->user_height)/4, icd->user_width, icd->user_height/4); // V
- }
- else {
- bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
- icd->current_fmt->host_fmt);
- make_interlaced(src, bytes_per_line, icd->user_height); // Y
- }
}
if (!err)
@@ -872,7 +903,6 @@ static int tegra_camera_capture_start(struct tegra_camera_dev *pcdev,
"VIP_INPUT_STATUS = 0x%08x\n",
vip_input_status);
}
-
return err;
}
@@ -999,7 +1029,11 @@ static int tegra_camera_capture_frame(struct tegra_camera_dev *pcdev)
vb->v4l2_buf.field = pcdev->field;
vb->v4l2_buf.sequence = pcdev->sequence++;
- vb2_buffer_done(vb, (err != 0) ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
+ if(IS_INTERLACED && pcdev->num_frames==0)
+ // if we're dealing with interlaced frames, tell V4L to remove the frame from the queue
+ vb2_buffer_done(vb, VB2_BUF_STATE_DEQUEUED);
+ else
+ vb2_buffer_done(vb, (err != 0) ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
pcdev->num_frames++;
@@ -1083,8 +1117,17 @@ static void tegra_camera_init_buffer(struct tegra_camera_dev *pcdev,
int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
icd->current_fmt->host_fmt);
- buf->buffer_addr = vb2_dma_nvmap_plane_paddr(&buf->vb, 0); // physical addr
- buf->virtual_addr = vb2_plane_vaddr(&buf->vb, 0); // save virtual addr for later interlace handling
+
+ if(IS_INTERLACED)
+ {
+ buf->internal_phys_addr = pcdev->internal_vbuf->paddr; // physical addr of internal buffer
+ buf->internal_virtual_addr = pcdev->internal_vbuf->vaddr; // virtual address of internal buffer
+ buf->buffer_addr = buf->internal_phys_addr; // internal buffer -> buffer for decoding
+ buf->virtual_addr = vb2_plane_vaddr(&buf->vb, 0); // save virtual addr for later interlace handling
+ }
+ else
+ buf->buffer_addr = vb2_dma_nvmap_plane_paddr(&buf->vb, 0); // physical addr
+
switch (icd->current_fmt->host_fmt->fourcc) {
case V4L2_PIX_FMT_UYVY:
case V4L2_PIX_FMT_VYUY:
@@ -1273,6 +1316,12 @@ static void tegra_camera_videobuf_release(struct vb2_buffer *vb)
dev_dbg(icd->parent, "In tegra_camera_videobuf_release()\n");
+ if(IS_INTERLACED && pcdev->internal_vbuf!=NULL)
+ {
+ vb2_dma_nvmap_memops.put(pcdev->internal_vbuf);
+ pcdev->internal_vbuf = NULL;
+ }
+
mutex_lock(&pcdev->work_mutex);
spin_lock_irq(&pcdev->videobuf_queue_lock);
@@ -1351,6 +1400,7 @@ static int tegra_camera_init_videobuf(struct vb2_queue *q,
q->drv_priv = icd;
q->ops = &tegra_camera_videobuf_ops;
q->mem_ops = &vb2_dma_nvmap_memops;
+ //q->mem_ops = &am_vb2_dma_nvmap_memops;
q->buf_struct_size = sizeof(struct tegra_buffer);
dev_dbg(icd->parent, "Finished tegra_camera_init_videobuf()\n");
@@ -1633,6 +1683,7 @@ static struct soc_camera_host_ops tegra_soc_camera_host_ops = {
.querycap = tegra_camera_querycap,
};
+
static int __devinit tegra_camera_probe(struct nvhost_device *ndev,
struct nvhost_device_id *id_table)
{