⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pxa_camera.c

📁 trident tm5600的linux驱动
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * 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 + -