diff options
Diffstat (limited to 'drivers/media/video/mx2_camera.c')
-rw-r--r-- | drivers/media/video/mx2_camera.c | 1214 |
1 files changed, 668 insertions, 546 deletions
diff --git a/drivers/media/video/mx2_camera.c b/drivers/media/video/mx2_camera.c index 04aab0c538a..18afaeeadb7 100644 --- a/drivers/media/video/mx2_camera.c +++ b/drivers/media/video/mx2_camera.c @@ -3,6 +3,7 @@ * * Copyright (C) 2008, Sascha Hauer, Pengutronix * Copyright (C) 2010, Baruch Siach, Orex Computed Radiography + * Copyright (C) 2012, Javier Martin, Vista Silicon S.L. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,6 +19,7 @@ #include <linux/dma-mapping.h> #include <linux/errno.h> #include <linux/fs.h> +#include <linux/gcd.h> #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/mm.h> @@ -30,17 +32,14 @@ #include <media/v4l2-common.h> #include <media/v4l2-dev.h> -#include <media/videobuf-core.h> -#include <media/videobuf-dma-contig.h> +#include <media/videobuf2-core.h> +#include <media/videobuf2-dma-contig.h> #include <media/soc_camera.h> #include <media/soc_mediabus.h> #include <linux/videodev2.h> #include <mach/mx2_cam.h> -#ifdef CONFIG_MACH_MX27 -#include <mach/dma-mx1-mx2.h> -#endif #include <mach/hardware.h> #include <asm/dma.h> @@ -206,10 +205,23 @@ #define PRP_INTR_LBOVF (1 << 7) #define PRP_INTR_CH2OVF (1 << 8) -#define mx27_camera_emma(pcdev) (cpu_is_mx27() && pcdev->use_emma) +/* Resizing registers */ +#define PRP_RZ_VALID_TBL_LEN(x) ((x) << 24) +#define PRP_RZ_VALID_BILINEAR (1 << 31) #define MAX_VIDEO_MEM 16 +#define RESIZE_NUM_MIN 1 +#define RESIZE_NUM_MAX 20 +#define BC_COEF 3 +#define SZ_COEF (1 << BC_COEF) + +#define RESIZE_DIR_H 0 +#define RESIZE_DIR_V 1 + +#define RESIZE_ALGO_BILINEAR 0 +#define RESIZE_ALGO_AVERAGING 1 + struct mx2_prp_cfg { int channel; u32 in_fmt; @@ -219,6 +231,13 @@ struct mx2_prp_cfg { u32 irq_flags; }; +/* prp resizing parameters */ +struct emma_prp_resize { + int algo; /* type of algorithm used */ + int len; /* number of coefficients */ + unsigned char s[RESIZE_NUM_MAX]; /* table of coefficients */ +}; + /* prp configuration for a client-host fmt pair */ struct mx2_fmt_cfg { enum v4l2_mbus_pixelcode in_fmt; @@ -226,6 +245,26 @@ struct mx2_fmt_cfg { struct mx2_prp_cfg cfg; }; +enum mx2_buffer_state { + MX2_STATE_QUEUED, + MX2_STATE_ACTIVE, + MX2_STATE_DONE, +}; + +struct mx2_buf_internal { + struct list_head queue; + int bufnum; + bool discard; +}; + +/* buffer for one video frame */ +struct mx2_buffer { + /* common v4l buffer stuff -- must be first */ + struct vb2_buffer vb; + enum mx2_buffer_state state; + struct mx2_buf_internal internal; +}; + struct mx2_camera_dev { struct device *dev; struct soc_camera_host soc_host; @@ -242,6 +281,7 @@ struct mx2_camera_dev { struct list_head capture; struct list_head active_bufs; + struct list_head discard; spinlock_t lock; @@ -250,26 +290,23 @@ struct mx2_camera_dev { struct mx2_buffer *fb1_active; struct mx2_buffer *fb2_active; - int use_emma; - u32 csicr1; + struct mx2_buf_internal buf_discard[2]; void *discard_buffer; dma_addr_t discard_buffer_dma; size_t discard_size; struct mx2_fmt_cfg *emma_prp; + struct emma_prp_resize resizing[2]; + unsigned int s_width, s_height; u32 frame_count; + struct vb2_alloc_ctx *alloc_ctx; }; -/* buffer for one video frame */ -struct mx2_buffer { - /* common v4l buffer stuff -- must be first */ - struct videobuf_buffer vb; - - enum v4l2_mbus_pixelcode code; - - int bufnum; -}; +static struct mx2_buffer *mx2_ibuf_to_buf(struct mx2_buf_internal *int_buf) +{ + return container_of(int_buf, struct mx2_buffer, internal); +} static struct mx2_fmt_cfg mx27_emma_prp_table[] = { /* @@ -324,13 +361,36 @@ static struct mx2_fmt_cfg *mx27_emma_prp_get_format( return &mx27_emma_prp_table[0]; }; +static void mx27_update_emma_buf(struct mx2_camera_dev *pcdev, + unsigned long phys, int bufnum) +{ + struct mx2_fmt_cfg *prp = pcdev->emma_prp; + + if (prp->cfg.channel == 1) { + writel(phys, pcdev->base_emma + + PRP_DEST_RGB1_PTR + 4 * bufnum); + } else { + writel(phys, pcdev->base_emma + + PRP_DEST_Y_PTR - 0x14 * bufnum); + if (prp->out_fmt == V4L2_PIX_FMT_YUV420) { + u32 imgsize = pcdev->icd->user_height * + pcdev->icd->user_width; + + writel(phys + imgsize, pcdev->base_emma + + PRP_DEST_CB_PTR - 0x14 * bufnum); + writel(phys + ((5 * imgsize) / 4), pcdev->base_emma + + PRP_DEST_CR_PTR - 0x14 * bufnum); + } + } +} + static void mx2_camera_deactivate(struct mx2_camera_dev *pcdev) { unsigned long flags; clk_disable(pcdev->clk_csi); writel(0, pcdev->base_csi + CSICR1); - if (mx27_camera_emma(pcdev)) { + if (cpu_is_mx27()) { writel(0, pcdev->base_emma + PRP_CNTL); } else if (cpu_is_mx25()) { spin_lock_irqsave(&pcdev->lock, flags); @@ -362,7 +422,7 @@ static int mx2_camera_add_device(struct soc_camera_device *icd) csicr1 = CSICR1_MCLKEN; - if (mx27_camera_emma(pcdev)) { + if (cpu_is_mx27()) { csicr1 |= CSICR1_PRP_IF_EN | CSICR1_FCC | CSICR1_RXFF_LEVEL(0); } else if (cpu_is_mx27()) @@ -392,56 +452,13 @@ static void mx2_camera_remove_device(struct soc_camera_device *icd) mx2_camera_deactivate(pcdev); - if (pcdev->discard_buffer) { - dma_free_coherent(ici->v4l2_dev.dev, pcdev->discard_size, - pcdev->discard_buffer, - pcdev->discard_buffer_dma); - pcdev->discard_buffer = NULL; - } - pcdev->icd = NULL; } -#ifdef CONFIG_MACH_MX27 -static void mx27_camera_dma_enable(struct mx2_camera_dev *pcdev) -{ - u32 tmp; - - imx_dma_enable(pcdev->dma); - - tmp = readl(pcdev->base_csi + CSICR1); - tmp |= CSICR1_RF_OR_INTEN; - writel(tmp, pcdev->base_csi + CSICR1); -} - -static irqreturn_t mx27_camera_irq(int irq_csi, void *data) -{ - struct mx2_camera_dev *pcdev = data; - u32 status = readl(pcdev->base_csi + CSISR); - - if (status & CSISR_SOF_INT && pcdev->active) { - u32 tmp; - - tmp = readl(pcdev->base_csi + CSICR1); - writel(tmp | CSICR1_CLR_RXFIFO, pcdev->base_csi + CSICR1); - mx27_camera_dma_enable(pcdev); - } - - writel(CSISR_SOF_INT | CSISR_RFF_OR_INT, pcdev->base_csi + CSISR); - - return IRQ_HANDLED; -} -#else -static irqreturn_t mx27_camera_irq(int irq_csi, void *data) -{ - return IRQ_NONE; -} -#endif /* CONFIG_MACH_MX27 */ - static void mx25_camera_frame_done(struct mx2_camera_dev *pcdev, int fb, int state) { - struct videobuf_buffer *vb; + struct vb2_buffer *vb; struct mx2_buffer *buf; struct mx2_buffer **fb_active = fb == 1 ? &pcdev->fb1_active : &pcdev->fb2_active; @@ -454,25 +471,24 @@ static void mx25_camera_frame_done(struct mx2_camera_dev *pcdev, int fb, goto out; vb = &(*fb_active)->vb; - dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, - vb, vb->baddr, vb->bsize); + dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__, + vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); - vb->state = state; - do_gettimeofday(&vb->ts); - vb->field_count++; - - wake_up(&vb->done); + do_gettimeofday(&vb->v4l2_buf.timestamp); + vb->v4l2_buf.sequence++; + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); if (list_empty(&pcdev->capture)) { buf = NULL; writel(0, pcdev->base_csi + fb_reg); } else { - buf = list_entry(pcdev->capture.next, struct mx2_buffer, - vb.queue); + buf = list_first_entry(&pcdev->capture, struct mx2_buffer, + internal.queue); vb = &buf->vb; - list_del(&vb->queue); - vb->state = VIDEOBUF_ACTIVE; - writel(videobuf_to_dma_contig(vb), pcdev->base_csi + fb_reg); + list_del(&buf->internal.queue); + buf->state = MX2_STATE_ACTIVE; + writel(vb2_dma_contig_plane_dma_addr(vb, 0), + pcdev->base_csi + fb_reg); } *fb_active = buf; @@ -487,9 +503,9 @@ static irqreturn_t mx25_camera_irq(int irq_csi, void *data) u32 status = readl(pcdev->base_csi + CSISR); if (status & CSISR_DMA_TSF_FB1_INT) - mx25_camera_frame_done(pcdev, 1, VIDEOBUF_DONE); + mx25_camera_frame_done(pcdev, 1, MX2_STATE_DONE); else if (status & CSISR_DMA_TSF_FB2_INT) - mx25_camera_frame_done(pcdev, 2, VIDEOBUF_DONE); + mx25_camera_frame_done(pcdev, 2, MX2_STATE_DONE); /* FIXME: handle CSISR_RFF_OR_INT */ @@ -501,59 +517,50 @@ static irqreturn_t mx25_camera_irq(int irq_csi, void *data) /* * Videobuf operations */ -static int mx2_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, - unsigned int *size) +static int mx2_videobuf_setup(struct vb2_queue *vq, + const struct v4l2_format *fmt, + unsigned int *count, unsigned int *num_planes, + unsigned int sizes[], void *alloc_ctxs[]) { - struct soc_camera_device *icd = vq->priv_data; + struct soc_camera_device *icd = soc_camera_from_vb2q(vq); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct mx2_camera_dev *pcdev = ici->priv; int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, icd->current_fmt->host_fmt); - dev_dbg(icd->parent, "count=%d, size=%d\n", *count, *size); + dev_dbg(icd->parent, "count=%d, size=%d\n", *count, sizes[0]); + + /* TODO: support for VIDIOC_CREATE_BUFS not ready */ + if (fmt != NULL) + return -ENOTTY; if (bytes_per_line < 0) return bytes_per_line; - *size = bytes_per_line * icd->user_height; + alloc_ctxs[0] = pcdev->alloc_ctx; + + sizes[0] = bytes_per_line * icd->user_height; if (0 == *count) *count = 32; - if (*size * *count > MAX_VIDEO_MEM * 1024 * 1024) - *count = (MAX_VIDEO_MEM * 1024 * 1024) / *size; + if (!*num_planes && + sizes[0] * *count > MAX_VIDEO_MEM * 1024 * 1024) + *count = (MAX_VIDEO_MEM * 1024 * 1024) / sizes[0]; - return 0; -} + *num_planes = 1; -static void free_buffer(struct videobuf_queue *vq, struct mx2_buffer *buf) -{ - struct soc_camera_device *icd = vq->priv_data; - struct videobuf_buffer *vb = &buf->vb; - - dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, - vb, vb->baddr, vb->bsize); - - /* - * This waits until this buffer is out of danger, i.e., until it is no - * longer in state VIDEOBUF_QUEUED or VIDEOBUF_ACTIVE - */ - videobuf_waiton(vq, vb, 0, 0); - - videobuf_dma_contig_free(vq, vb); - dev_dbg(icd->parent, "%s freed\n", __func__); - - vb->state = VIDEOBUF_NEEDS_INIT; + return 0; } -static int mx2_videobuf_prepare(struct videobuf_queue *vq, - struct videobuf_buffer *vb, enum v4l2_field field) +static int mx2_videobuf_prepare(struct vb2_buffer *vb) { - struct soc_camera_device *icd = vq->priv_data; - struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb); + struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, icd->current_fmt->host_fmt); int ret = 0; - dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, - vb, vb->baddr, vb->bsize); + dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, + vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); if (bytes_per_line < 0) return bytes_per_line; @@ -563,99 +570,58 @@ static int mx2_videobuf_prepare(struct videobuf_queue *vq, * This can be useful if you want to see if we actually fill * the buffer with something */ - memset((void *)vb->baddr, 0xaa, vb->bsize); + memset((void *)vb2_plane_vaddr(vb, 0), + 0xaa, vb2_get_plane_payload(vb, 0)); #endif - if (buf->code != icd->current_fmt->code || - vb->width != icd->user_width || - vb->height != icd->user_height || - vb->field != field) { - buf->code = icd->current_fmt->code; - vb->width = icd->user_width; - vb->height = icd->user_height; - vb->field = field; - vb->state = VIDEOBUF_NEEDS_INIT; - } - - vb->size = bytes_per_line * vb->height; - if (vb->baddr && vb->bsize < vb->size) { + vb2_set_plane_payload(vb, 0, bytes_per_line * icd->user_height); + if (vb2_plane_vaddr(vb, 0) && + vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) { ret = -EINVAL; goto out; } - if (vb->state == VIDEOBUF_NEEDS_INIT) { - ret = videobuf_iolock(vq, vb, NULL); - if (ret) - goto fail; - - vb->state = VIDEOBUF_PREPARED; - } - return 0; -fail: - free_buffer(vq, buf); out: return ret; } -static void mx2_videobuf_queue(struct videobuf_queue *vq, - struct videobuf_buffer *vb) +static void mx2_videobuf_queue(struct vb2_buffer *vb) { - struct soc_camera_device *icd = vq->priv_data; + struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx2_camera_dev *pcdev = ici->priv; struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb); unsigned long flags; - dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, - vb, vb->baddr, vb->bsize); + dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, + vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); spin_lock_irqsave(&pcdev->lock, flags); - vb->state = VIDEOBUF_QUEUED; - list_add_tail(&vb->queue, &pcdev->capture); + buf->state = MX2_STATE_QUEUED; + list_add_tail(&buf->internal.queue, &pcdev->capture); - if (mx27_camera_emma(pcdev)) { - goto out; -#ifdef CONFIG_MACH_MX27 - } else if (cpu_is_mx27()) { - int ret; - - if (pcdev->active == NULL) { - ret = imx_dma_setup_single(pcdev->dma, - videobuf_to_dma_contig(vb), vb->size, - (u32)pcdev->base_dma + 0x10, - DMA_MODE_READ); - if (ret) { - vb->state = VIDEOBUF_ERROR; - wake_up(&vb->done); - goto out; - } - - vb->state = VIDEOBUF_ACTIVE; - pcdev->active = buf; - } -#endif - } else { /* cpu_is_mx25() */ + if (cpu_is_mx25()) { u32 csicr3, dma_inten = 0; if (pcdev->fb1_active == NULL) { - writel(videobuf_to_dma_contig(vb), + writel(vb2_dma_contig_plane_dma_addr(vb, 0), pcdev->base_csi + CSIDMASA_FB1); pcdev->fb1_active = buf; dma_inten = CSICR1_FB1_DMA_INTEN; } else if (pcdev->fb2_active == NULL) { - writel(videobuf_to_dma_contig(vb), + writel(vb2_dma_contig_plane_dma_addr(vb, 0), pcdev->base_csi + CSIDMASA_FB2); pcdev->fb2_active = buf; dma_inten = CSICR1_FB2_DMA_INTEN; } if (dma_inten) { - list_del(&vb->queue); - vb->state = VIDEOBUF_ACTIVE; + list_del(&buf->internal.queue); + buf->state = MX2_STATE_ACTIVE; csicr3 = readl(pcdev->base_csi + CSICR3); @@ -674,36 +640,31 @@ static void mx2_videobuf_queue(struct videobuf_queue *vq, } } -out: spin_unlock_irqrestore(&pcdev->lock, flags); } -static void mx2_videobuf_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) +static void mx2_videobuf_release(struct vb2_buffer *vb) { - struct soc_camera_device *icd = vq->priv_data; + struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx2_camera_dev *pcdev = ici->priv; struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb); unsigned long flags; #ifdef DEBUG - dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, - vb, vb->baddr, vb->bsize); + dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, + vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); - switch (vb->state) { - case VIDEOBUF_ACTIVE: + switch (buf->state) { + case MX2_STATE_ACTIVE: dev_info(icd->parent, "%s (active)\n", __func__); break; - case VIDEOBUF_QUEUED: + case MX2_STATE_QUEUED: dev_info(icd->parent, "%s (queued)\n", __func__); break; - case VIDEOBUF_PREPARED: - dev_info(icd->parent, "%s (prepared)\n", __func__); - break; default: dev_info(icd->parent, "%s (unknown) %d\n", __func__, - vb->state); + buf->state); break; } #endif @@ -717,11 +678,9 @@ static void mx2_videobuf_release(struct videobuf_queue *vq, * state. This requires a specific handling for each of the these DMA * types. */ + spin_lock_irqsave(&pcdev->lock, flags); - if (vb->state == VIDEOBUF_QUEUED) { - list_del(&vb->queue); - vb->state = VIDEOBUF_ERROR; - } else if (cpu_is_mx25() && vb->state == VIDEOBUF_ACTIVE) { + if (cpu_is_mx25() && buf->state == MX2_STATE_ACTIVE) { if (pcdev->fb1_active == buf) { pcdev->csicr1 &= ~CSICR1_FB1_DMA_INTEN; writel(0, pcdev->base_csi + CSIDMASA_FB1); @@ -732,75 +691,178 @@ static void mx2_videobuf_release(struct videobuf_queue *vq, pcdev->fb2_active = NULL; } writel(pcdev->csicr1, pcdev->base_csi + CSICR1); - vb->state = VIDEOBUF_ERROR; } spin_unlock_irqrestore(&pcdev->lock, flags); - - free_buffer(vq, buf); } -static struct videobuf_queue_ops mx2_videobuf_ops = { - .buf_setup = mx2_videobuf_setup, - .buf_prepare = mx2_videobuf_prepare, - .buf_queue = mx2_videobuf_queue, - .buf_release = mx2_videobuf_release, -}; - -static void mx2_camera_init_videobuf(struct videobuf_queue *q, - struct soc_camera_device *icd) +static void mx27_camera_emma_buf_init(struct soc_camera_device *icd, + int bytesperline) { - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct soc_camera_host *ici = + to_soc_camera_host(icd->parent); struct mx2_camera_dev *pcdev = ici->priv; + struct mx2_fmt_cfg *prp = pcdev->emma_prp; - videobuf_queue_dma_contig_init(q, &mx2_videobuf_ops, pcdev->dev, - &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_NONE, sizeof(struct mx2_buffer), - icd, &icd->video_lock); -} + writel((pcdev->s_width << 16) | pcdev->s_height, + pcdev->base_emma + PRP_SRC_FRAME_SIZE); + writel(prp->cfg.src_pixel, + pcdev->base_emma + PRP_SRC_PIXEL_FORMAT_CNTL); + if (prp->cfg.channel == 1) { + writel((icd->user_width << 16) | icd->user_height, + pcdev->base_emma + PRP_CH1_OUT_IMAGE_SIZE); + writel(bytesperline, + pcdev->base_emma + PRP_DEST_CH1_LINE_STRIDE); + writel(prp->cfg.ch1_pixel, + pcdev->base_emma + PRP_CH1_PIXEL_FORMAT_CNTL); + } else { /* channel 2 */ + writel((icd->user_width << 16) | icd->user_height, + pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE); + } -#define MX2_BUS_FLAGS (V4L2_MBUS_MASTER | \ - V4L2_MBUS_VSYNC_ACTIVE_HIGH | \ - V4L2_MBUS_VSYNC_ACTIVE_LOW | \ - V4L2_MBUS_HSYNC_ACTIVE_HIGH | \ - V4L2_MBUS_HSYNC_ACTIVE_LOW | \ - V4L2_MBUS_PCLK_SAMPLE_RISING | \ - V4L2_MBUS_PCLK_SAMPLE_FALLING | \ - V4L2_MBUS_DATA_ACTIVE_HIGH | \ - V4L2_MBUS_DATA_ACTIVE_LOW) + /* Enable interrupts */ + writel(prp->cfg.irq_flags, pcdev->base_emma + PRP_INTR_CNTL); +} -static int mx27_camera_emma_prp_reset(struct mx2_camera_dev *pcdev) +static void mx2_prp_resize_commit(struct mx2_camera_dev *pcdev) { - u32 cntl; - int count = 0; + int dir; - cntl = readl(pcdev->base_emma + PRP_CNTL); - writel(PRP_CNTL_SWRST, pcdev->base_emma + PRP_CNTL); - while (count++ < 100) { - if (!(readl(pcdev->base_emma + PRP_CNTL) & PRP_CNTL_SWRST)) - return 0; - barrier(); - udelay(1); - } + for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) { + unsigned char *s = pcdev->resizing[dir].s; + int len = pcdev->resizing[dir].len; + unsigned int coeff[2] = {0, 0}; + unsigned int valid = 0; + int i; - return -ETIMEDOUT; + if (len == 0) + continue; + + for (i = RESIZE_NUM_MAX - 1; i >= 0; i--) { + int j; + + j = i > 9 ? 1 : 0; + coeff[j] = (coeff[j] << BC_COEF) | + (s[i] & (SZ_COEF - 1)); + + if (i == 5 || i == 15) + coeff[j] <<= 1; + + valid = (valid << 1) | (s[i] >> BC_COEF); + } + + valid |= PRP_RZ_VALID_TBL_LEN(len); + + if (pcdev->resizing[dir].algo == RESIZE_ALGO_BILINEAR) + valid |= PRP_RZ_VALID_BILINEAR; + + if (pcdev->emma_prp->cfg.channel == 1) { + if (dir == RESIZE_DIR_H) { + writel(coeff[0], pcdev->base_emma + + PRP_CH1_RZ_HORI_COEF1); + writel(coeff[1], pcdev->base_emma + + PRP_CH1_RZ_HORI_COEF2); + writel(valid, pcdev->base_emma + + PRP_CH1_RZ_HORI_VALID); + } else { + writel(coeff[0], pcdev->base_emma + + PRP_CH1_RZ_VERT_COEF1); + writel(coeff[1], pcdev->base_emma + + PRP_CH1_RZ_VERT_COEF2); + writel(valid, pcdev->base_emma + + PRP_CH1_RZ_VERT_VALID); + } + } else { + if (dir == RESIZE_DIR_H) { + writel(coeff[0], pcdev->base_emma + + PRP_CH2_RZ_HORI_COEF1); + writel(coeff[1], pcdev->base_emma + + PRP_CH2_RZ_HORI_COEF2); + writel(valid, pcdev->base_emma + + PRP_CH2_RZ_HORI_VALID); + } else { + writel(coeff[0], pcdev->base_emma + + PRP_CH2_RZ_VERT_COEF1); + writel(coeff[1], pcdev->base_emma + + PRP_CH2_RZ_VERT_COEF2); + writel(valid, pcdev->base_emma + + PRP_CH2_RZ_VERT_VALID); + } + } + } } -static void mx27_camera_emma_buf_init(struct soc_camera_device *icd, - int bytesperline) +static int mx2_start_streaming(struct vb2_queue *q, unsigned int count) { + struct soc_camera_device *icd = soc_camera_from_vb2q(q); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx2_camera_dev *pcdev = ici->priv; struct mx2_fmt_cfg *prp = pcdev->emma_prp; - u32 imgsize = pcdev->icd->user_height * pcdev->icd->user_width; + struct vb2_buffer *vb; + struct mx2_buffer *buf; + unsigned long phys; + int bytesperline; - if (prp->cfg.channel == 1) { - writel(pcdev->discard_buffer_dma, - pcdev->base_emma + PRP_DEST_RGB1_PTR); - writel(pcdev->discard_buffer_dma, - pcdev->base_emma + PRP_DEST_RGB2_PTR); + if (cpu_is_mx27()) { + unsigned long flags; + if (count < 2) + return -EINVAL; + + spin_lock_irqsave(&pcdev->lock, flags); + + buf = list_first_entry(&pcdev->capture, struct mx2_buffer, + internal.queue); + buf->internal.bufnum = 0; + vb = &buf->vb; + buf->state = MX2_STATE_ACTIVE; + + phys = vb2_dma_contig_plane_dma_addr(vb, 0); + mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum); + list_move_tail(pcdev->capture.next, &pcdev->active_bufs); + + buf = list_first_entry(&pcdev->capture, struct mx2_buffer, + internal.queue); + buf->internal.bufnum = 1; + vb = &buf->vb; + buf->state = MX2_STATE_ACTIVE; - writel(PRP_CNTL_CH1EN | + phys = vb2_dma_contig_plane_dma_addr(vb, 0); + mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum); + list_move_tail(pcdev->capture.next, &pcdev->active_bufs); + + bytesperline = soc_mbus_bytes_per_line(icd->user_width, + icd->current_fmt->host_fmt); + if (bytesperline < 0) + return bytesperline; + + /* + * I didn't manage to properly enable/disable the prp + * on a per frame basis during running transfers, + * thus we allocate a buffer here and use it to + * discard frames when no buffer is available. + * Feel free to work on this ;) + */ + pcdev->discard_size = icd->user_height * bytesperline; + pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev, + pcdev->discard_size, &pcdev->discard_buffer_dma, + GFP_KERNEL); + if (!pcdev->discard_buffer) + return -ENOMEM; + + pcdev->buf_discard[0].discard = true; + list_add_tail(&pcdev->buf_discard[0].queue, + &pcdev->discard); + + pcdev->buf_discard[1].discard = true; + list_add_tail(&pcdev->buf_discard[1].queue, + &pcdev->discard); + + mx2_prp_resize_commit(pcdev); + + mx27_camera_emma_buf_init(icd, bytesperline); + + if (prp->cfg.channel == 1) { + writel(PRP_CNTL_CH1EN | PRP_CNTL_CSIEN | prp->cfg.in_fmt | prp->cfg.out_fmt | @@ -809,56 +871,107 @@ static void mx27_camera_emma_buf_init(struct soc_camera_device *icd, PRP_CNTL_CH1_TSKIP(0) | PRP_CNTL_IN_TSKIP(0), pcdev->base_emma + PRP_CNTL); + } else { + writel(PRP_CNTL_CH2EN | + PRP_CNTL_CSIEN | + prp->cfg.in_fmt | + prp->cfg.out_fmt | + PRP_CNTL_CH2_LEN | + PRP_CNTL_CH2_TSKIP(0) | + PRP_CNTL_IN_TSKIP(0), + pcdev->base_emma + PRP_CNTL); + } + spin_unlock_irqrestore(&pcdev->lock, flags); + } - writel((icd->user_width << 16) | icd->user_height, - pcdev->base_emma + PRP_SRC_FRAME_SIZE); - writel((icd->user_width << 16) | icd->user_height, - pcdev->base_emma + PRP_CH1_OUT_IMAGE_SIZE); - writel(bytesperline, - pcdev->base_emma + PRP_DEST_CH1_LINE_STRIDE); - writel(prp->cfg.src_pixel, - pcdev->base_emma + PRP_SRC_PIXEL_FORMAT_CNTL); - writel(prp->cfg.ch1_pixel, - pcdev->base_emma + PRP_CH1_PIXEL_FORMAT_CNTL); - } else { /* channel 2 */ - writel(pcdev->discard_buffer_dma, - pcdev->base_emma + PRP_DEST_Y_PTR); - writel(pcdev->discard_buffer_dma, - pcdev->base_emma + PRP_SOURCE_Y_PTR); - - if (prp->cfg.out_fmt == PRP_CNTL_CH2_OUT_YUV420) { - writel(pcdev->discard_buffer_dma + imgsize, - pcdev->base_emma + PRP_DEST_CB_PTR); - writel(pcdev->discard_buffer_dma + ((5 * imgsize) / 4), - pcdev->base_emma + PRP_DEST_CR_PTR); - writel(pcdev->discard_buffer_dma + imgsize, - pcdev->base_emma + PRP_SOURCE_CB_PTR); - writel(pcdev->discard_buffer_dma + ((5 * imgsize) / 4), - pcdev->base_emma + PRP_SOURCE_CR_PTR); + return 0; +} + +static int mx2_stop_streaming(struct vb2_queue *q) +{ + struct soc_camera_device *icd = soc_camera_from_vb2q(q); + struct soc_camera_host *ici = + to_soc_camera_host(icd->parent); + struct mx2_camera_dev *pcdev = ici->priv; + struct mx2_fmt_cfg *prp = pcdev->emma_prp; + unsigned long flags; + void *b; + u32 cntl; + + if (cpu_is_mx27()) { + spin_lock_irqsave(&pcdev->lock, flags); + + cntl = readl(pcdev->base_emma + PRP_CNTL); + if (prp->cfg.channel == 1) { + writel(cntl & ~PRP_CNTL_CH1EN, + pcdev->base_emma + PRP_CNTL); + } else { + writel(cntl & ~PRP_CNTL_CH2EN, + pcdev->base_emma + PRP_CNTL); } + INIT_LIST_HEAD(&pcdev->capture); + INIT_LIST_HEAD(&pcdev->active_bufs); + INIT_LIST_HEAD(&pcdev->discard); - writel(PRP_CNTL_CH2EN | - PRP_CNTL_CSIEN | - prp->cfg.in_fmt | - prp->cfg.out_fmt | - PRP_CNTL_CH2_LEN | - PRP_CNTL_CH2_TSKIP(0) | - PRP_CNTL_IN_TSKIP(0), - pcdev->base_emma + PRP_CNTL); + b = pcdev->discard_buffer; + pcdev->discard_buffer = NULL; - writel((icd->user_width << 16) | icd->user_height, - pcdev->base_emma + PRP_SRC_FRAME_SIZE); + spin_unlock_irqrestore(&pcdev->lock, flags); - writel((icd->user_width << 16) | icd->user_height, - pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE); + dma_free_coherent(ici->v4l2_dev.dev, + pcdev->discard_size, b, pcdev->discard_buffer_dma); + } - writel(prp->cfg.src_pixel, - pcdev->base_emma + PRP_SRC_PIXEL_FORMAT_CNTL); + return 0; +} + +static struct vb2_ops mx2_videobuf_ops = { + .queue_setup = mx2_videobuf_setup, + .buf_prepare = mx2_videobuf_prepare, + .buf_queue = mx2_videobuf_queue, + .buf_cleanup = mx2_videobuf_release, + .start_streaming = mx2_start_streaming, + .stop_streaming = mx2_stop_streaming, +}; + +static int mx2_camera_init_videobuf(struct vb2_queue *q, + struct soc_camera_device *icd) +{ + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_USERPTR; + q->drv_priv = icd; + q->ops = &mx2_videobuf_ops; + q->mem_ops = &vb2_dma_contig_memops; + q->buf_struct_size = sizeof(struct mx2_buffer); + + return vb2_queue_init(q); +} +#define MX2_BUS_FLAGS (V4L2_MBUS_MASTER | \ + V4L2_MBUS_VSYNC_ACTIVE_HIGH | \ + V4L2_MBUS_VSYNC_ACTIVE_LOW | \ + V4L2_MBUS_HSYNC_ACTIVE_HIGH | \ + V4L2_MBUS_HSYNC_ACTIVE_LOW | \ + V4L2_MBUS_PCLK_SAMPLE_RISING | \ + V4L2_MBUS_PCLK_SAMPLE_FALLING | \ + V4L2_MBUS_DATA_ACTIVE_HIGH | \ + V4L2_MBUS_DATA_ACTIVE_LOW) + +static int mx27_camera_emma_prp_reset(struct mx2_camera_dev *pcdev) +{ + u32 cntl; + int count = 0; + + cntl = readl(pcdev->base_emma + PRP_CNTL); + writel(PRP_CNTL_SWRST, pcdev->base_emma + PRP_CNTL); + while (count++ < 100) { + if (!(readl(pcdev->base_emma + PRP_CNTL) & PRP_CNTL_SWRST)) + return 0; + barrier(); + udelay(1); } - /* Enable interrupts */ - writel(prp->cfg.irq_flags, pcdev->base_emma + PRP_INTR_CNTL); + return -ETIMEDOUT; } static int mx2_camera_set_bus_param(struct soc_camera_device *icd) @@ -939,31 +1052,10 @@ static int mx2_camera_set_bus_param(struct soc_camera_device *icd) if (bytesperline < 0) return bytesperline; - if (mx27_camera_emma(pcdev)) { + if (cpu_is_mx27()) { ret = mx27_camera_emma_prp_reset(pcdev); if (ret) return ret; - - if (pcdev->discard_buffer) - dma_free_coherent(ici->v4l2_dev.dev, - pcdev->discard_size, pcdev->discard_buffer, - pcdev->discard_buffer_dma); - - /* - * I didn't manage to properly enable/disable the prp - * on a per frame basis during running transfers, - * thus we allocate a buffer here and use it to - * discard frames when no buffer is available. - * Feel free to work on this ;) - */ - pcdev->discard_size = icd->user_height * bytesperline; - pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev, - pcdev->discard_size, &pcdev->discard_buffer_dma, - GFP_KERNEL); - if (!pcdev->discard_buffer) - return -ENOMEM; - - mx27_camera_emma_buf_init(icd, bytesperline); } else if (cpu_is_mx25()) { writel((bytesperline * icd->user_height) >> 2, pcdev->base_csi + CSIRXCNT); @@ -1052,6 +1144,123 @@ static int mx2_camera_get_formats(struct soc_camera_device *icd, return formats; } +static int mx2_emmaprp_resize(struct mx2_camera_dev *pcdev, + struct v4l2_mbus_framefmt *mf_in, + struct v4l2_pix_format *pix_out, bool apply) +{ + int num, den; + unsigned long m; + int i, dir; + + for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) { + struct emma_prp_resize tmprsz; + unsigned char *s = tmprsz.s; + int len = 0; + int in, out; + + if (dir == RESIZE_DIR_H) { + in = mf_in->width; + out = pix_out->width; + } else { + in = mf_in->height; + out = pix_out->height; + } + + if (in < out) + return -EINVAL; + else if (in == out) + continue; + + /* Calculate ratio */ + m = gcd(in, out); + num = in / m; + den = out / m; + if (num > RESIZE_NUM_MAX) + return -EINVAL; + + if ((num >= 2 * den) && (den == 1) && + (num < 9) && (!(num & 0x01))) { + int sum = 0; + int j; + + /* Average scaling for >= 2:1 ratios */ + /* Support can be added for num >=9 and odd values */ + + tmprsz.algo = RESIZE_ALGO_AVERAGING; + len = num; + + for (i = 0; i < (len / 2); i++) + s[i] = 8; + + do { + for (i = 0; i < (len / 2); i++) { + s[i] = s[i] >> 1; + sum = 0; + for (j = 0; j < (len / 2); j++) + sum += s[j]; + if (sum == 4) + break; + } + } while (sum != 4); + + for (i = (len / 2); i < len; i++) + s[i] = s[len - i - 1]; + + s[len - 1] |= SZ_COEF; + } else { + /* bilinear scaling for < 2:1 ratios */ + int v; /* overflow counter */ + int coeff, nxt; /* table output */ + int in_pos_inc = 2 * den; + int out_pos = num; + int out_pos_inc = 2 * num; + int init_carry = num - den; + int carry = init_carry; + + tmprsz.algo = RESIZE_ALGO_BILINEAR; + v = den + in_pos_inc; + do { + coeff = v - out_pos; + out_pos += out_pos_inc; + carry += out_pos_inc; + for (nxt = 0; v < out_pos; nxt++) { + v += in_pos_inc; + carry -= in_pos_inc; + } + + if (len > RESIZE_NUM_MAX) + return -EINVAL; + + coeff = ((coeff << BC_COEF) + + (in_pos_inc >> 1)) / in_pos_inc; + + if (coeff >= (SZ_COEF - 1)) + coeff--; + + coeff |= SZ_COEF; + s[len] = (unsigned char)coeff; + len++; + + for (i = 1; i < nxt; i++) { + if (len >= RESIZE_NUM_MAX) + return -EINVAL; + s[len] = 0; + len++; + } + } while (carry != init_carry); + } + tmprsz.len = len; + if (dir == RESIZE_DIR_H) + mf_in->width = pix_out->width; + else + mf_in->height = pix_out->height; + + if (apply) + memcpy(&pcdev->resizing[dir], &tmprsz, sizeof(tmprsz)); + } + return 0; +} + static int mx2_camera_set_fmt(struct soc_camera_device *icd, struct v4l2_format *f) { @@ -1063,6 +1272,9 @@ static int mx2_camera_set_fmt(struct soc_camera_device *icd, struct v4l2_mbus_framefmt mf; int ret; + dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n", + __func__, pix->width, pix->height); + xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); if (!xlate) { dev_warn(icd->parent, "Format %x not found\n", @@ -1080,6 +1292,22 @@ static int mx2_camera_set_fmt(struct soc_camera_device *icd, if (ret < 0 && ret != -ENOIOCTLCMD) return ret; + /* Store width and height returned by the sensor for resizing */ + pcdev->s_width = mf.width; + pcdev->s_height = mf.height; + dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n", + __func__, pcdev->s_width, pcdev->s_height); + + pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code, + xlate->host_fmt->fourcc); + + memset(pcdev->resizing, 0, sizeof(pcdev->resizing)); + if ((mf.width != pix->width || mf.height != pix->height) && + pcdev->emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) { + if (mx2_emmaprp_resize(pcdev, &mf, pix, true) < 0) + dev_dbg(icd->parent, "%s: can't resize\n", __func__); + } + if (mf.code != xlate->code) return -EINVAL; @@ -1089,9 +1317,8 @@ static int mx2_camera_set_fmt(struct soc_camera_device *icd, pix->colorspace = mf.colorspace; icd->current_fmt = xlate; - if (mx27_camera_emma(pcdev)) - pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code, - xlate->host_fmt->fourcc); + dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n", + __func__, pix->width, pix->height); return 0; } @@ -1104,9 +1331,14 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, struct v4l2_pix_format *pix = &f->fmt.pix; struct v4l2_mbus_framefmt mf; __u32 pixfmt = pix->pixelformat; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct mx2_camera_dev *pcdev = ici->priv; unsigned int width_limit; int ret; + dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n", + __func__, pix->width, pix->height); + xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); if (pixfmt && !xlate) { dev_warn(icd->parent, "Format %x not found\n", pixfmt); @@ -1156,6 +1388,20 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, if (ret < 0) return ret; + dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n", + __func__, pcdev->s_width, pcdev->s_height); + + /* If the sensor does not support image size try PrP resizing */ + pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code, + xlate->host_fmt->fourcc); + + memset(pcdev->resizing, 0, sizeof(pcdev->resizing)); + if ((mf.width != pix->width || mf.height != pix->height) && + pcdev->emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) { + if (mx2_emmaprp_resize(pcdev, &mf, pix, false) < 0) + dev_dbg(icd->parent, "%s: can't resize\n", __func__); + } + if (mf.field == V4L2_FIELD_ANY) mf.field = V4L2_FIELD_NONE; /* @@ -1174,6 +1420,9 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, pix->field = mf.field; pix->colorspace = mf.colorspace; + dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n", + __func__, pix->width, pix->height); + return 0; } @@ -1187,136 +1436,11 @@ static int mx2_camera_querycap(struct soc_camera_host *ici, return 0; } -static int mx2_camera_reqbufs(struct soc_camera_device *icd, - struct v4l2_requestbuffers *p) -{ - int i; - - for (i = 0; i < p->count; i++) { - struct mx2_buffer *buf = container_of(icd->vb_vidq.bufs[i], - struct mx2_buffer, vb); - INIT_LIST_HEAD(&buf->vb.queue); - } - - return 0; -} - -#ifdef CONFIG_MACH_MX27 -static void mx27_camera_frame_done(struct mx2_camera_dev *pcdev, int state) -{ - struct videobuf_buffer *vb; - struct mx2_buffer *buf; - unsigned long flags; - int ret; - - spin_lock_irqsave(&pcdev->lock, flags); - - if (!pcdev->active) { - dev_err(pcdev->dev, "%s called with no active buffer!\n", - __func__); - goto out; - } - - vb = &pcdev->active->vb; - buf = container_of(vb, struct mx2_buffer, vb); - WARN_ON(list_empty(&vb->queue)); - dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, - vb, vb->baddr, vb->bsize); - - /* _init is used to debug races, see comment in pxa_camera_reqbufs() */ - list_del_init(&vb->queue); - vb->state = state; - do_gettimeofday(&vb->ts); - vb->field_count++; - - wake_up(&vb->done); - - if (list_empty(&pcdev->capture)) { - pcdev->active = NULL; - goto out; - } - - pcdev->active = list_entry(pcdev->capture.next, - struct mx2_buffer, vb.queue); - - vb = &pcdev->active->vb; - vb->state = VIDEOBUF_ACTIVE; - - ret = imx_dma_setup_single(pcdev->dma, videobuf_to_dma_contig(vb), - vb->size, (u32)pcdev->base_dma + 0x10, DMA_MODE_READ); - - if (ret) { - vb->state = VIDEOBUF_ERROR; - pcdev->active = NULL; - wake_up(&vb->done); - } - -out: - spin_unlock_irqrestore(&pcdev->lock, flags); -} - -static void mx27_camera_dma_err_callback(int channel, void *data, int err) -{ - struct mx2_camera_dev *pcdev = data; - - mx27_camera_frame_done(pcdev, VIDEOBUF_ERROR); -} - -static void mx27_camera_dma_callback(int channel, void *data) -{ - struct mx2_camera_dev *pcdev = data; - - mx27_camera_frame_done(pcdev, VIDEOBUF_DONE); -} - -#define DMA_REQ_CSI_RX 31 /* FIXME: Add this to a resource */ - -static int __devinit mx27_camera_dma_init(struct platform_device *pdev, - struct mx2_camera_dev *pcdev) -{ - int err; - - pcdev->dma = imx_dma_request_by_prio("CSI RX DMA", DMA_PRIO_HIGH); - if (pcdev->dma < 0) { - dev_err(&pdev->dev, "%s failed to request DMA channel\n", - __func__); - return pcdev->dma; - } - - err = imx_dma_setup_handlers(pcdev->dma, mx27_camera_dma_callback, - mx27_camera_dma_err_callback, pcdev); - if (err) { - dev_err(&pdev->dev, "%s failed to set DMA callback\n", - __func__); - goto err_out; - } - - err = imx_dma_config_channel(pcdev->dma, - IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_FIFO, - IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR, - DMA_REQ_CSI_RX, 1); - if (err) { - dev_err(&pdev->dev, "%s failed to config DMA channel\n", - __func__); - goto err_out; - } - - imx_dma_config_burstlen(pcdev->dma, 64); - - return 0; - -err_out: - imx_dma_free(pcdev->dma); - - return err; -} -#endif /* CONFIG_MACH_MX27 */ - static unsigned int mx2_camera_poll(struct file *file, poll_table *pt) { struct soc_camera_device *icd = file->private_data; - return videobuf_poll_stream(file, &icd->vb_vidq, pt); + return vb2_poll(&icd->vb2_vidq, file, pt); } static struct soc_camera_host_ops mx2_soc_camera_host_ops = { @@ -1327,144 +1451,148 @@ static struct soc_camera_host_ops mx2_soc_camera_host_ops = { .set_crop = mx2_camera_set_crop, .get_formats = mx2_camera_get_formats, .try_fmt = mx2_camera_try_fmt, - .init_videobuf = mx2_camera_init_videobuf, - .reqbufs = mx2_camera_reqbufs, + .init_videobuf2 = mx2_camera_init_videobuf, .poll = mx2_camera_poll, .querycap = mx2_camera_querycap, .set_bus_param = mx2_camera_set_bus_param, }; static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev, - int bufnum, int state) + int bufnum, bool err) { - u32 imgsize = pcdev->icd->user_height * pcdev->icd->user_width; +#ifdef DEBUG struct mx2_fmt_cfg *prp = pcdev->emma_prp; +#endif + struct mx2_buf_internal *ibuf; struct mx2_buffer *buf; - struct videobuf_buffer *vb; + struct vb2_buffer *vb; unsigned long phys; - if (!list_empty(&pcdev->active_bufs)) { - buf = list_entry(pcdev->active_bufs.next, - struct mx2_buffer, vb.queue); + ibuf = list_first_entry(&pcdev->active_bufs, struct mx2_buf_internal, + queue); + + BUG_ON(ibuf->bufnum != bufnum); - BUG_ON(buf->bufnum != bufnum); + if (ibuf->discard) { + /* + * Discard buffer must not be returned to user space. + * Just return it to the discard queue. + */ + list_move_tail(pcdev->active_bufs.next, &pcdev->discard); + } else { + buf = mx2_ibuf_to_buf(ibuf); vb = &buf->vb; #ifdef DEBUG - phys = videobuf_to_dma_contig(vb); + phys = vb2_dma_contig_plane_dma_addr(vb, 0); if (prp->cfg.channel == 1) { if (readl(pcdev->base_emma + PRP_DEST_RGB1_PTR + 4 * bufnum) != phys) { - dev_err(pcdev->dev, "%p != %p\n", phys, - readl(pcdev->base_emma + - PRP_DEST_RGB1_PTR + - 4 * bufnum)); + dev_err(pcdev->dev, "%lx != %x\n", phys, + readl(pcdev->base_emma + + PRP_DEST_RGB1_PTR + 4 * bufnum)); } } else { if (readl(pcdev->base_emma + PRP_DEST_Y_PTR - 0x14 * bufnum) != phys) { - dev_err(pcdev->dev, "%p != %p\n", phys, - readl(pcdev->base_emma + - PRP_DEST_Y_PTR - - 0x14 * bufnum)); + dev_err(pcdev->dev, "%lx != %x\n", phys, + readl(pcdev->base_emma + + PRP_DEST_Y_PTR - 0x14 * bufnum)); } } #endif - dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, - vb->baddr, vb->bsize); + dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__, vb, + vb2_plane_vaddr(vb, 0), + vb2_get_plane_payload(vb, 0)); - list_del(&vb->queue); - vb->state = state; - do_gettimeofday(&vb->ts); - vb->field_count = pcdev->frame_count * 2; - pcdev->frame_count++; - - wake_up(&vb->done); + list_del_init(&buf->internal.queue); + do_gettimeofday(&vb->v4l2_buf.timestamp); + vb->v4l2_buf.sequence = pcdev->frame_count; + if (err) + vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); + else + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); } + pcdev->frame_count++; + if (list_empty(&pcdev->capture)) { - if (prp->cfg.channel == 1) { - writel(pcdev->discard_buffer_dma, pcdev->base_emma + - PRP_DEST_RGB1_PTR + 4 * bufnum); - } else { - writel(pcdev->discard_buffer_dma, pcdev->base_emma + - PRP_DEST_Y_PTR - - 0x14 * bufnum); - if (prp->out_fmt == V4L2_PIX_FMT_YUV420) { - writel(pcdev->discard_buffer_dma + imgsize, - pcdev->base_emma + PRP_DEST_CB_PTR - - 0x14 * bufnum); - writel(pcdev->discard_buffer_dma + - ((5 * imgsize) / 4), pcdev->base_emma + - PRP_DEST_CR_PTR - 0x14 * bufnum); - } + if (list_empty(&pcdev->discard)) { + dev_warn(pcdev->dev, "%s: trying to access empty discard list\n", + __func__); + return; } + + ibuf = list_first_entry(&pcdev->discard, + struct mx2_buf_internal, queue); + ibuf->bufnum = bufnum; + + list_move_tail(pcdev->discard.next, &pcdev->active_bufs); + mx27_update_emma_buf(pcdev, pcdev->discard_buffer_dma, bufnum); return; } - buf = list_entry(pcdev->capture.next, - struct mx2_buffer, vb.queue); + buf = list_first_entry(&pcdev->capture, struct mx2_buffer, + internal.queue); - buf->bufnum = !bufnum; + buf->internal.bufnum = bufnum; list_move_tail(pcdev->capture.next, &pcdev->active_bufs); vb = &buf->vb; - vb->state = VIDEOBUF_ACTIVE; + buf->state = MX2_STATE_ACTIVE; - phys = videobuf_to_dma_contig(vb); - if (prp->cfg.channel == 1) { - writel(phys, pcdev->base_emma + PRP_DEST_RGB1_PTR + 4 * bufnum); - } else { - writel(phys, pcdev->base_emma + - PRP_DEST_Y_PTR - 0x14 * bufnum); - if (prp->cfg.out_fmt == PRP_CNTL_CH2_OUT_YUV420) { - writel(phys + imgsize, pcdev->base_emma + - PRP_DEST_CB_PTR - 0x14 * bufnum); - writel(phys + ((5 * imgsize) / 4), pcdev->base_emma + - PRP_DEST_CR_PTR - 0x14 * bufnum); - } - } + phys = vb2_dma_contig_plane_dma_addr(vb, 0); + mx27_update_emma_buf(pcdev, phys, bufnum); } static irqreturn_t mx27_camera_emma_irq(int irq_emma, void *data) { struct mx2_camera_dev *pcdev = data; unsigned int status = readl(pcdev->base_emma + PRP_INTRSTATUS); - struct mx2_buffer *buf; + struct mx2_buf_internal *ibuf; + + spin_lock(&pcdev->lock); + + if (list_empty(&pcdev->active_bufs)) { + dev_warn(pcdev->dev, "%s: called while active list is empty\n", + __func__); + + if (!status) { + spin_unlock(&pcdev->lock); + return IRQ_NONE; + } + } if (status & (1 << 7)) { /* overflow */ - u32 cntl; - /* - * We only disable channel 1 here since this is the only - * enabled channel - * - * FIXME: the correct DMA overflow handling should be resetting - * the buffer, returning an error frame, and continuing with - * the next one. - */ - cntl = readl(pcdev->base_emma + PRP_CNTL); + u32 cntl = readl(pcdev->base_emma + PRP_CNTL); writel(cntl & ~(PRP_CNTL_CH1EN | PRP_CNTL_CH2EN), pcdev->base_emma + PRP_CNTL); writel(cntl, pcdev->base_emma + PRP_CNTL); - } - if ((((status & (3 << 5)) == (3 << 5)) || - ((status & (3 << 3)) == (3 << 3))) - && !list_empty(&pcdev->active_bufs)) { + + ibuf = list_first_entry(&pcdev->active_bufs, + struct mx2_buf_internal, queue); + mx27_camera_frame_done_emma(pcdev, + ibuf->bufnum, true); + + status &= ~(1 << 7); + } else if (((status & (3 << 5)) == (3 << 5)) || + ((status & (3 << 3)) == (3 << 3))) { /* * Both buffers have triggered, process the one we're expecting * to first */ - buf = list_entry(pcdev->active_bufs.next, - struct mx2_buffer, vb.queue); - mx27_camera_frame_done_emma(pcdev, buf->bufnum, VIDEOBUF_DONE); - status &= ~(1 << (6 - buf->bufnum)); /* mark processed */ + ibuf = list_first_entry(&pcdev->active_bufs, + struct mx2_buf_internal, queue); + mx27_camera_frame_done_emma(pcdev, ibuf->bufnum, false); + status &= ~(1 << (6 - ibuf->bufnum)); /* mark processed */ + } else if ((status & (1 << 6)) || (status & (1 << 4))) { + mx27_camera_frame_done_emma(pcdev, 0, false); + } else if ((status & (1 << 5)) || (status & (1 << 3))) { + mx27_camera_frame_done_emma(pcdev, 1, false); } - if ((status & (1 << 6)) || (status & (1 << 4))) - mx27_camera_frame_done_emma(pcdev, 0, VIDEOBUF_DONE); - if ((status & (1 << 5)) || (status & (1 << 3))) - mx27_camera_frame_done_emma(pcdev, 1, VIDEOBUF_DONE); + spin_unlock(&pcdev->lock); writel(status, pcdev->base_emma + PRP_INTRSTATUS); return IRQ_HANDLED; @@ -1527,8 +1655,6 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev) struct resource *res_csi, *res_emma; void __iomem *base_csi; int irq_csi, irq_emma; - irq_handler_t mx2_cam_irq_handler = cpu_is_mx25() ? mx25_camera_irq - : mx27_camera_irq; int err = 0; dev_dbg(&pdev->dev, "initialising\n"); @@ -1550,22 +1676,11 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev) pcdev->clk_csi = clk_get(&pdev->dev, NULL); if (IS_ERR(pcdev->clk_csi)) { + dev_err(&pdev->dev, "Could not get csi clock\n"); err = PTR_ERR(pcdev->clk_csi); goto exit_kfree; } - dev_dbg(&pdev->dev, "Camera clock frequency: %ld\n", - clk_get_rate(pcdev->clk_csi)); - - /* Initialize DMA */ -#ifdef CONFIG_MACH_MX27 - if (cpu_is_mx27()) { - err = mx27_camera_dma_init(pdev, pcdev); - if (err) - goto exit_clk_put; - } -#endif /* CONFIG_MACH_MX27 */ - pcdev->res_csi = res_csi; pcdev->pdata = pdev->dev.platform_data; if (pcdev->pdata) { @@ -1585,6 +1700,7 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev) INIT_LIST_HEAD(&pcdev->capture); INIT_LIST_HEAD(&pcdev->active_bufs); + INIT_LIST_HEAD(&pcdev->discard); spin_lock_init(&pcdev->lock); /* @@ -1606,11 +1722,13 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev) pcdev->base_dma = res_csi->start; pcdev->dev = &pdev->dev; - err = request_irq(pcdev->irq_csi, mx2_cam_irq_handler, 0, - MX2_CAM_DRV_NAME, pcdev); - if (err) { - dev_err(pcdev->dev, "Camera interrupt register failed \n"); - goto exit_iounmap; + if (cpu_is_mx25()) { + err = request_irq(pcdev->irq_csi, mx25_camera_irq, 0, + MX2_CAM_DRV_NAME, pcdev); + if (err) { + dev_err(pcdev->dev, "Camera interrupt register failed \n"); + goto exit_iounmap; + } } if (cpu_is_mx27()) { @@ -1618,14 +1736,15 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev) res_emma = platform_get_resource(pdev, IORESOURCE_MEM, 1); irq_emma = platform_get_irq(pdev, 1); - if (res_emma && irq_emma >= 0) { - dev_info(&pdev->dev, "Using EMMA\n"); - pcdev->use_emma = 1; - pcdev->res_emma = res_emma; - pcdev->irq_emma = irq_emma; - if (mx27_camera_emma_init(pcdev)) - goto exit_free_irq; + if (!res_emma || !irq_emma) { + dev_err(&pdev->dev, "no EMMA resources\n"); + goto exit_free_irq; } + + pcdev->res_emma = res_emma; + pcdev->irq_emma = irq_emma; + if (mx27_camera_emma_init(pcdev)) + goto exit_free_irq; } pcdev->soc_host.drv_name = MX2_CAM_DRV_NAME, @@ -1633,6 +1752,12 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev) pcdev->soc_host.priv = pcdev; pcdev->soc_host.v4l2_dev.dev = &pdev->dev; pcdev->soc_host.nr = pdev->id; + + pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); + if (IS_ERR(pcdev->alloc_ctx)) { + err = PTR_ERR(pcdev->alloc_ctx); + goto eallocctx; + } err = soc_camera_host_register(&pcdev->soc_host); if (err) goto exit_free_emma; @@ -1643,26 +1768,24 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev) return 0; exit_free_emma: - if (mx27_camera_emma(pcdev)) { + vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); +eallocctx: + if (cpu_is_mx27()) { free_irq(pcdev->irq_emma, pcdev); clk_disable(pcdev->clk_emma); clk_put(pcdev->clk_emma); iounmap(pcdev->base_emma); - release_mem_region(res_emma->start, resource_size(res_emma)); + release_mem_region(pcdev->res_emma->start, resource_size(pcdev->res_emma)); } exit_free_irq: - free_irq(pcdev->irq_csi, pcdev); + if (cpu_is_mx25()) + free_irq(pcdev->irq_csi, pcdev); exit_iounmap: iounmap(base_csi); exit_release: release_mem_region(res_csi->start, resource_size(res_csi)); exit_dma_free: -#ifdef CONFIG_MACH_MX27 - if (cpu_is_mx27()) - imx_dma_free(pcdev->dma); -exit_clk_put: clk_put(pcdev->clk_csi); -#endif /* CONFIG_MACH_MX27 */ exit_kfree: kfree(pcdev); exit: @@ -1677,19 +1800,18 @@ static int __devexit mx2_camera_remove(struct platform_device *pdev) struct resource *res; clk_put(pcdev->clk_csi); -#ifdef CONFIG_MACH_MX27 + if (cpu_is_mx25()) + free_irq(pcdev->irq_csi, pcdev); if (cpu_is_mx27()) - imx_dma_free(pcdev->dma); -#endif /* CONFIG_MACH_MX27 */ - free_irq(pcdev->irq_csi, pcdev); - if (mx27_camera_emma(pcdev)) free_irq(pcdev->irq_emma, pcdev); soc_camera_host_unregister(&pcdev->soc_host); + vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); + iounmap(pcdev->base_csi); - if (mx27_camera_emma(pcdev)) { + if (cpu_is_mx27()) { clk_disable(pcdev->clk_emma); clk_put(pcdev->clk_emma); iounmap(pcdev->base_emma); |