📄 pxa_camera.c
字号:
/* * V4L2 Driver for PXA camera host * * Copyright (C) 2006, Sascha Hauer, Pengutronix * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> * * 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 * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */#include <linux/init.h>#include <linux/module.h>#include <linux/io.h>#include <linux/delay.h>#include <linux/dma-mapping.h>#include <linux/errno.h>#include <linux/fs.h>#include <linux/interrupt.h>#include <linux/kernel.h>#include <linux/mm.h>#include <linux/moduleparam.h>#include <linux/time.h>#include <linux/version.h>#include <linux/device.h>#include <linux/platform_device.h>#include <linux/mutex.h>#include <linux/clk.h>#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>#include <asm/dma.h>#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)#include <mach/pxa-regs.h>#include <mach/camera.h>#else#include <asm/arch/pxa-regs.h>#include <asm/arch/camera.h>#endif#define PXA_CAM_VERSION_CODE KERNEL_VERSION(0, 0, 5)#define PXA_CAM_DRV_NAME "pxa27x-camera"#define CICR0_SIM_MP (0 << 24)#define CICR0_SIM_SP (1 << 24)#define CICR0_SIM_MS (2 << 24)#define CICR0_SIM_EP (3 << 24)#define CICR0_SIM_ES (4 << 24)#define CICR1_DW_VAL(x) ((x) & CICR1_DW) /* Data bus width */#define CICR1_PPL_VAL(x) (((x) << 15) & CICR1_PPL) /* Pixels per line */#define CICR1_COLOR_SP_VAL(x) (((x) << 3) & CICR1_COLOR_SP) /* color space */#define CICR1_RGB_BPP_VAL(x) (((x) << 7) & CICR1_RGB_BPP) /* bpp for rgb */#define CICR1_RGBT_CONV_VAL(x) (((x) << 29) & CICR1_RGBT_CONV) /* rgbt conv */#define CICR2_BLW_VAL(x) (((x) << 24) & CICR2_BLW) /* Beginning-of-line pixel clock wait count */#define CICR2_ELW_VAL(x) (((x) << 16) & CICR2_ELW) /* End-of-line pixel clock wait count */#define CICR2_HSW_VAL(x) (((x) << 10) & CICR2_HSW) /* Horizontal sync pulse width */#define CICR2_BFPW_VAL(x) (((x) << 3) & CICR2_BFPW) /* Beginning-of-frame pixel clock wait count */#define CICR2_FSW_VAL(x) (((x) << 0) & CICR2_FSW) /* Frame stabilization wait count */#define CICR3_BFW_VAL(x) (((x) << 24) & CICR3_BFW) /* Beginning-of-frame line clock wait count */#define CICR3_EFW_VAL(x) (((x) << 16) & CICR3_EFW) /* End-of-frame line clock wait count */#define CICR3_VSW_VAL(x) (((x) << 11) & CICR3_VSW) /* Vertical sync pulse width */#define CICR3_LPF_VAL(x) (((x) << 0) & CICR3_LPF) /* Lines per frame */#define CICR0_IRQ_MASK (CICR0_TOM | CICR0_RDAVM | CICR0_FEM | CICR0_EOLM | \ CICR0_PERRM | CICR0_QDM | CICR0_CDM | CICR0_SOFM | \ CICR0_EOFM | CICR0_FOM)static DEFINE_MUTEX(camera_lock);/* * Structures */enum pxa_camera_active_dma { DMA_Y = 0x1, DMA_U = 0x2, DMA_V = 0x4,};/* descriptor needed for the PXA DMA engine */struct pxa_cam_dma { dma_addr_t sg_dma; struct pxa_dma_desc *sg_cpu; size_t sg_size; int sglen;};/* buffer for one video frame */struct pxa_buffer { /* common v4l buffer stuff -- must be first */ struct videobuf_buffer vb; const struct soc_camera_data_format *fmt; /* our descriptor lists for Y, U and V channels */ struct pxa_cam_dma dmas[3]; int inwork; enum pxa_camera_active_dma active_dma;};struct pxa_camera_dev { struct device *dev; /* PXA27x is only supposed to handle one camera on its Quick Capture * interface. If anyone ever builds hardware to enable more than * one camera, they will have to modify this driver too */ struct soc_camera_device *icd; struct clk *clk; unsigned int irq; void __iomem *base; int channels; unsigned int dma_chans[3]; struct pxacamera_platform_data *pdata; struct resource *res; unsigned long platform_flags; unsigned long platform_mclk_10khz; struct list_head capture; spinlock_t lock; struct pxa_buffer *active; struct pxa_dma_desc *sg_tail[3]; u32 save_cicr[5];};static const char *pxa_cam_driver_description = "PXA_Camera";static unsigned int vid_limit = 16; /* Video memory limit, in Mb *//* * Videobuf operations */static int pxa_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, 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 (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 */ } else { *size = icd->width * icd->height * ((icd->current_fmt->depth + 7) >> 3); } if (0 == *count) *count = 32; while (*size * *count > vid_limit * 1024 * 1024) (*count)--; return 0;}static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf){ 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; struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); int i; BUG_ON(in_interrupt()); dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, &buf->vb, buf->vb.baddr, buf->vb.bsize); /* This waits until this buffer is out of danger, i.e., until it is no * longer in STATE_QUEUED or STATE_ACTIVE */ videobuf_waiton(&buf->vb, 0, 0); videobuf_dma_unmap(vq, dma); videobuf_dma_free(dma); for (i = 0; i < ARRAY_SIZE(buf->dmas); i++) { if (buf->dmas[i].sg_cpu) dma_free_coherent(pcdev->dev, buf->dmas[i].sg_size, buf->dmas[i].sg_cpu, buf->dmas[i].sg_dma); buf->dmas[i].sg_cpu = NULL; } buf->vb.state = VIDEOBUF_NEEDS_INIT;}static int pxa_init_dma_channel(struct pxa_camera_dev *pcdev, struct pxa_buffer *buf, struct videobuf_dmabuf *dma, int channel, int sglen, int sg_start, int cibr, unsigned int size){ struct pxa_cam_dma *pxa_dma = &buf->dmas[channel]; int i; if (pxa_dma->sg_cpu) dma_free_coherent(pcdev->dev, pxa_dma->sg_size, pxa_dma->sg_cpu, pxa_dma->sg_dma); pxa_dma->sg_size = (sglen + 1) * sizeof(struct pxa_dma_desc); pxa_dma->sg_cpu = dma_alloc_coherent(pcdev->dev, pxa_dma->sg_size, &pxa_dma->sg_dma, GFP_KERNEL); if (!pxa_dma->sg_cpu) return -ENOMEM; pxa_dma->sglen = sglen; for (i = 0; i < sglen; i++) { int sg_i = sg_start + i; struct scatterlist *sg = dma->sglist; unsigned int dma_len = sg_dma_len(&sg[sg_i]), xfer_len; pxa_dma->sg_cpu[i].dsadr = pcdev->res->start + cibr; pxa_dma->sg_cpu[i].dtadr = sg_dma_address(&sg[sg_i]); /* PXA27x Developer's Manual 27.4.4.1: round up to 8 bytes */ xfer_len = (min(dma_len, size) + 7) & ~7; pxa_dma->sg_cpu[i].dcmd = DCMD_FLOWSRC | DCMD_BURST8 | DCMD_INCTRGADDR | xfer_len; size -= dma_len; pxa_dma->sg_cpu[i].ddadr = pxa_dma->sg_dma + (i + 1) * sizeof(struct pxa_dma_desc); } pxa_dma->sg_cpu[sglen - 1].ddadr = DDADR_STOP; pxa_dma->sg_cpu[sglen - 1].dcmd |= DCMD_ENDIRQEN; return 0;}static int pxa_videobuf_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, enum v4l2_field field){ 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; struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); int ret; int sglen_y, sglen_yu = 0, sglen_u = 0, sglen_v = 0; int size_y, size_u = 0, size_v = 0; dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); /* Added list head initialization on alloc */ WARN_ON(!list_empty(&vb->queue));#ifdef DEBUG /* This can be useful if you want to see if we actually fill * the buffer with something */ memset((void *)vb->baddr, 0xaa, vb->bsize);#endif BUG_ON(NULL == icd->current_fmt); /* I think, in buf_prepare you only have to protect global data, * the actual buffer is yours */ buf->inwork = 1; if (buf->fmt != icd->current_fmt || vb->width != icd->width || vb->height != icd->height || vb->field != field) { buf->fmt = icd->current_fmt; vb->width = icd->width; vb->height = icd->height; vb->field = field; vb->state = VIDEOBUF_NEEDS_INIT; } vb->size = vb->width * vb->height * ((buf->fmt->depth + 7) >> 3); if (0 != vb->baddr && vb->bsize < vb->size) { ret = -EINVAL; goto out; } if (vb->state == VIDEOBUF_NEEDS_INIT) { unsigned int size = vb->size; struct videobuf_dmabuf *dma = videobuf_to_dma(vb); ret = videobuf_iolock(vq, vb, NULL); if (ret) goto fail; if (pcdev->channels == 3) { /* FIXME the calculations should be more precise */ sglen_y = dma->sglen / 2; sglen_u = sglen_v = dma->sglen / 4 + 1; sglen_yu = sglen_y + sglen_u; size_y = size / 2; size_u = size_v = size / 4; } else { sglen_y = dma->sglen; size_y = size; } /* init DMA for Y channel */ ret = pxa_init_dma_channel(pcdev, buf, dma, 0, sglen_y, 0, 0x28, size_y); if (ret) { dev_err(pcdev->dev, "DMA initialization for Y/RGB failed\n"); goto fail; } 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); if (ret) { dev_err(pcdev->dev, "DMA initialization for U failed\n"); goto fail_u; } /* init DMA for V channel */ ret = pxa_init_dma_channel(pcdev, buf, dma, 2, sglen_v, sglen_yu, 0x38, size_v); if (ret) { dev_err(pcdev->dev, "DMA initialization for V failed\n"); goto fail_v; } } vb->state = VIDEOBUF_PREPARED; } buf->inwork = 0; buf->active_dma = DMA_Y; if (pcdev->channels == 3) buf->active_dma |= DMA_U | DMA_V; return 0;fail_v: dma_free_coherent(pcdev->dev, buf->dmas[1].sg_size, buf->dmas[1].sg_cpu, buf->dmas[1].sg_dma);fail_u: dma_free_coherent(pcdev->dev, buf->dmas[0].sg_size, buf->dmas[0].sg_cpu, buf->dmas[0].sg_dma);fail: free_buffer(vq, buf);out: buf->inwork = 0; return ret;}static void pxa_videobuf_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb){ 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; 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); spin_lock_irqsave(&pcdev->lock, flags); list_add_tail(&vb->queue, &pcdev->capture); vb->state = VIDEOBUF_ACTIVE; active = pcdev->active; if (!active) { CIFR |= CIFR_RESET_F; 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; CICR0 |= CICR0_ENB; } else { struct pxa_cam_dma *buf_dma; struct pxa_cam_dma *act_dma; int nents; for (i = 0; i < pcdev->channels; i++) { buf_dma = &buf->dmas[i]; act_dma = &active->dmas[i]; nents = buf_dma->sglen; /* Stop DMA engine */ DCSR(pcdev->dma_chans[i]) = 0; /* Add the descriptors we just initialized to
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -