#include <media/v4l2-common.h>
#include <media/v4l2-dev.h>
+#include <media/videobuf-dma-sg.h>
#include <media/soc_camera.h>
#include <linux/videodev2.h>
enum pxa_camera_active_dma active_dma;
};
-struct pxa_framebuffer_queue {
- dma_addr_t sg_last_dma;
- struct pxa_dma_desc *sg_last_cpu;
-};
-
struct pxa_camera_dev {
struct device *dev;
/* PXA27x is only supposed to handle one camera on its Quick Capture
unsigned int irq;
void __iomem *base;
+ int channels;
unsigned int dma_chans[3];
struct pxacamera_platform_data *pdata;
spinlock_t lock;
struct pxa_buffer *active;
+ struct pxa_dma_desc *sg_tail[3];
};
static const char *pxa_cam_driver_description = "PXA_Camera";
unsigned int *size)
{
struct soc_camera_device *icd = vq->priv_data;
+ struct soc_camera_host *ici =
+ to_soc_camera_host(icd->dev.parent);
+ struct pxa_camera_dev *pcdev = ici->priv;
dev_dbg(&icd->dev, "count=%d, size=%d\n", *count, *size);
/* planar capture requires Y, U and V buffers to be page aligned */
- if (icd->current_fmt->fourcc == V4L2_PIX_FMT_YUV422P) {
+ if (pcdev->channels == 3) {
*size = PAGE_ALIGN(icd->width * icd->height); /* Y pages */
*size += PAGE_ALIGN(icd->width * icd->height / 2); /* U pages */
*size += PAGE_ALIGN(icd->width * icd->height / 2); /* V pages */
if (ret)
goto fail;
- if (buf->fmt->fourcc == V4L2_PIX_FMT_YUV422P) {
+ if (pcdev->channels == 3) {
/* FIXME the calculations should be more precise */
sglen_y = dma->sglen / 2;
sglen_u = sglen_v = dma->sglen / 4 + 1;
goto fail;
}
- if (buf->fmt->fourcc == V4L2_PIX_FMT_YUV422P) {
+ if (pcdev->channels == 3) {
/* init DMA for U channel */
ret = pxa_init_dma_channel(pcdev, buf, dma, 1, sglen_u,
sglen_y, 0x30, size_u);
buf->inwork = 0;
buf->active_dma = DMA_Y;
- if (buf->fmt->fourcc == V4L2_PIX_FMT_YUV422P)
+ if (pcdev->channels == 3)
buf->active_dma |= DMA_U | DMA_V;
return 0;
struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb);
struct pxa_buffer *active;
unsigned long flags;
+ int i;
dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
vb, vb->baddr, vb->bsize);
if (!active) {
CIFR |= CIFR_RESET_F;
- DDADR(pcdev->dma_chans[0]) = buf->dmas[0].sg_dma;
- DCSR(pcdev->dma_chans[0]) = DCSR_RUN;
-
- if (buf->fmt->fourcc == V4L2_PIX_FMT_YUV422P) {
- DDADR(pcdev->dma_chans[1]) = buf->dmas[1].sg_dma;
- DCSR(pcdev->dma_chans[1]) = DCSR_RUN;
- DDADR(pcdev->dma_chans[2]) = buf->dmas[2].sg_dma;
- DCSR(pcdev->dma_chans[2]) = DCSR_RUN;
+ for (i = 0; i < pcdev->channels; i++) {
+ DDADR(pcdev->dma_chans[i]) = buf->dmas[i].sg_dma;
+ DCSR(pcdev->dma_chans[i]) = DCSR_RUN;
+ pcdev->sg_tail[i] = buf->dmas[i].sg_cpu + buf->dmas[i].sglen - 1;
}
pcdev->active = buf;
} else {
struct pxa_cam_dma *buf_dma;
struct pxa_cam_dma *act_dma;
- int channels = 1;
int nents;
- int i;
-
- if (buf->fmt->fourcc == V4L2_PIX_FMT_YUV422P)
- channels = 3;
- for (i = 0; i < channels; i++) {
+ for (i = 0; i < pcdev->channels; i++) {
buf_dma = &buf->dmas[i];
act_dma = &active->dmas[i];
nents = buf_dma->sglen;
/* Add the descriptors we just initialized to
the currently running chain */
- act_dma->sg_cpu[act_dma->sglen - 1].ddadr =
- buf_dma->sg_dma;
+ pcdev->sg_tail[i]->ddadr = buf_dma->sg_dma;
+ pcdev->sg_tail[i] = buf_dma->sg_cpu + buf_dma->sglen - 1;
/* Setup a dummy descriptor with the DMA engines current
* state
DCSR(pcdev->dma_chans[i]) = DCSR_RUN;
}
-#ifdef DEBUG
- if (CISR & (CISR_IFO_0 | CISR_IFO_1 | CISR_IFO_2)) {
- dev_warn(pcdev->dev, "FIFO overrun\n");
- for (i = 0; i < channels; i++)
- DDADR(pcdev->dma_chans[i]) =
- pcdev->active->dmas[i].sg_dma;
-
- CICR0 &= ~CICR0_ENB;
- CIFR |= CIFR_RESET_F;
- for (i = 0; i < channels; i++)
- DCSR(pcdev->dma_chans[i]) = DCSR_RUN;
- CICR0 |= CICR0_ENB;
- }
-#endif
}
spin_unlock_irqrestore(&pcdev->lock, flags);
{
struct pxa_buffer *buf;
unsigned long flags;
- unsigned int status;
+ u32 status, camera_status, overrun;
struct videobuf_buffer *vb;
spin_lock_irqsave(&pcdev->lock, flags);
goto out;
}
+ camera_status = CISR;
+ overrun = CISR_IFO_0;
+ if (pcdev->channels == 3)
+ overrun |= CISR_IFO_1 | CISR_IFO_2;
+ if (camera_status & overrun) {
+ dev_dbg(pcdev->dev, "FIFO overrun! CISR: %x\n", camera_status);
+ /* Stop the Capture Interface */
+ CICR0 &= ~CICR0_ENB;
+ /* Stop DMA */
+ DCSR(channel) = 0;
+ /* Reset the FIFOs */
+ CIFR |= CIFR_RESET_F;
+ /* Enable End-Of-Frame Interrupt */
+ CICR0 &= ~CICR0_EOFM;
+ /* Restart the Capture Interface */
+ CICR0 |= CICR0_ENB;
+ goto out;
+ }
+
vb = &pcdev->active->vb;
buf = container_of(vb, struct pxa_buffer, vb);
WARN_ON(buf->inwork || list_empty(&vb->queue));
.buf_release = pxa_videobuf_release,
};
+static void pxa_camera_init_videobuf(struct videobuf_queue *q,
+ struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct pxa_camera_dev *pcdev = ici->priv;
+
+ /* We must pass NULL as dev pointer, then all pci_* dma operations
+ * transform to normal dma_* ones. */
+ videobuf_queue_sg_init(q, &pxa_videobuf_ops, NULL, &pcdev->lock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
+ sizeof(struct pxa_buffer), icd);
+}
+
static int mclk_get_divisor(struct pxa_camera_dev *pcdev)
{
unsigned int mclk_10khz = pcdev->platform_mclk_10khz;
dev_dbg(pcdev->dev, "Camera interrupt status 0x%x\n", status);
+ if (!status)
+ return IRQ_NONE;
+
CISR = status;
+
+ if (status & CISR_EOF) {
+ int i;
+ for (i = 0; i < pcdev->channels; i++) {
+ DDADR(pcdev->dma_chans[i]) =
+ pcdev->active->dmas[i].sg_dma;
+ DCSR(pcdev->dma_chans[i]) = DCSR_RUN;
+ }
+ CICR0 |= CICR0_EOFM;
+ }
+
return IRQ_HANDLED;
}
if (!common_flags)
return -EINVAL;
+ pcdev->channels = 1;
+
/* Make choises, based on platform preferences */
if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) &&
(common_flags & SOCAM_HSYNC_ACTIVE_LOW)) {
switch (pixfmt) {
case V4L2_PIX_FMT_YUV422P:
+ pcdev->channels = 3;
cicr1 |= CICR1_YCBCR_F;
case V4L2_PIX_FMT_YUYV:
cicr1 |= CICR1_COLOR_SP_VAL(2);
return 0;
}
-static spinlock_t *pxa_camera_spinlock_alloc(struct soc_camera_file *icf)
-{
- struct soc_camera_host *ici =
- to_soc_camera_host(icf->icd->dev.parent);
- struct pxa_camera_dev *pcdev = ici->priv;
-
- return &pcdev->lock;
-}
-
static struct soc_camera_host_ops pxa_soc_camera_host_ops = {
.owner = THIS_MODULE,
.add = pxa_camera_add_device,
.remove = pxa_camera_remove_device,
.set_fmt_cap = pxa_camera_set_fmt_cap,
.try_fmt_cap = pxa_camera_try_fmt_cap,
+ .init_videobuf = pxa_camera_init_videobuf,
.reqbufs = pxa_camera_reqbufs,
.poll = pxa_camera_poll,
.querycap = pxa_camera_querycap,
.try_bus_param = pxa_camera_try_bus_param,
.set_bus_param = pxa_camera_set_bus_param,
- .spinlock_alloc = pxa_camera_spinlock_alloc,
};
/* Should be allocated dynamically too, but we have only one. */
static struct soc_camera_host pxa_soc_camera_host = {
.drv_name = PXA_CAM_DRV_NAME,
- .vbq_ops = &pxa_videobuf_ops,
- .msize = sizeof(struct pxa_buffer),
.ops = &pxa_soc_camera_host_ops,
};
struct pxa_camera_dev *pcdev;
struct resource *res;
void __iomem *base;
- unsigned int irq;
+ int irq;
int err = 0;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0);
- if (!res || !irq) {
+ if (!res || irq < 0) {
err = -ENODEV;
goto exit;
}