camera_core.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,206 行 · 第 1/2 页

C
1,206
字号
/* * drivers/media/video/omap/camera_core.c * * Copyright (C) 2004 Texas Instruments, Inc.  * * Video-for-Linux (Version 2) camera capture driver for * the OMAP H2 and H3 camera controller. * * Adapted from omap24xx driver written by Andy Lowe (source@mvista.com) * Copyright (C) 2003-2004 MontaVista Software, Inc. *  * This package is free software; you can redistribute it and/or modify  * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation.  *  * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.  */ #include <linux/config.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/init.h>#include <linux/fs.h>#include <linux/vmalloc.h>#include <linux/slab.h>#include <linux/proc_fs.h>#include <linux/ctype.h>#include <linux/pagemap.h>#include <linux/mm.h>#include <linux/device.h>#include <linux/delay.h>#include <linux/interrupt.h>#include <linux/videodev.h>#include <linux/pci.h>#include <asm/semaphore.h>#include <asm/processor.h>#include <linux/dma-mapping.h>#include <linux/fb.h>#include <asm/io.h>#include <asm/byteorder.h>#include <asm/irq.h> #include "sensor_if.h"#include "camera_hw_if.h"#include "camera_core.h" struct camera_device *camera_dev;extern struct camera_sensor camera_sensor_if;extern struct camera_hardware camera_hardware_if; static void camera_core_sgdma_process(struct camera_device *cam);/* module parameters */static int video_nr = -1;	/* video device minor (-1 ==> auto assign) *//* Maximum amount of memory to use for capture buffers. * Default is 4800KB, enough to double-buffer SXGA. */static int capture_mem = 1280*960*2*2;/*Size of video overlay framebuffer. This determines the maximum image size *that can be previewed. Default is 600KB, enough for sxga. */static int overlay_mem = 640*480*2; /* DMA completion routine for the scatter-gather DMA fragments. *//* This function is called when a scatter DMA fragment is completed */static voidcamera_core_callback_sgdma(void *arg1, void *arg2){	struct camera_device *cam = (struct camera_device *)arg1;	int sgslot = (int)arg2;	struct sgdma_state *sgdma;	spin_lock(&cam->sg_lock);	sgdma = cam->sgdma + sgslot;	if (!sgdma->queued_sglist)	{		spin_unlock(&cam->sg_lock);		printk(KERN_ERR CAM_NAME ": SGDMA completed when none queued\n");		return;	}	if (!--sgdma->queued_sglist) {		/* queue for this sglist is empty so check whether transfer		** of the frame has been completed */		if (sgdma->next_sglist == sgdma->sglen) {			dma_callback_t callback = sgdma->callback;			void *arg = sgdma->arg;			/* all done with this sglist */			cam->free_sgdma++;			if (callback) {				spin_unlock(&cam->sg_lock);				(*callback)(cam, arg);				camera_core_sgdma_process(cam);				return;			}		}	}	spin_unlock(&cam->sg_lock);	camera_core_sgdma_process(cam);	return;}static voidcamera_core_sgdma_init(struct camera_device *cam){	int sg;	/* Initialize the underlying camera DMA */	cam->cam_hardware->init_dma(cam->hardware_data);	spin_lock_init(&cam->sg_lock);		cam->free_sgdma = NUM_SG_DMA;	cam->next_sgdma = 0;	for (sg = 0; sg < NUM_SG_DMA; sg++) {		cam->sgdma[sg].sglen = 0;		cam->sgdma[sg].next_sglist = 0;		cam->sgdma[sg].queued_sglist = 0;		cam->sgdma[sg].csr = 0;		cam->sgdma[sg].callback = NULL;		cam->sgdma[sg].arg = NULL;	}}/* * Process the scatter-gather DMA queue by starting queued transfers * This function is called to program the dma to start the transfer of an image. */static voidcamera_core_sgdma_process(struct camera_device *cam){	unsigned long irqflags;	int queued_sgdma, sgslot;	struct sgdma_state *sgdma;	const struct scatterlist *sglist;		spin_lock_irqsave(&cam->sg_lock, irqflags);	queued_sgdma = NUM_SG_DMA - cam->free_sgdma;	sgslot = (cam->next_sgdma + cam->free_sgdma) % (NUM_SG_DMA);	while (queued_sgdma > 0) {		sgdma = cam->sgdma + sgslot;		while (sgdma->next_sglist < sgdma->sglen) {			sglist = sgdma->sglist + sgdma->next_sglist;			if (cam->cam_hardware->start_dma(sgdma, camera_core_callback_sgdma,				(void *)cam, (void *)sgslot, cam->hardware_data)) {					/* dma start failed */					spin_unlock_irqrestore(&cam->sg_lock, irqflags);					return;			}			else {				/* dma start successful */				sgdma->next_sglist ++;				sgdma->queued_sglist ++;			}		}		queued_sgdma-- ;		sgslot = (sgslot + 1) % (NUM_SG_DMA); 	}	spin_unlock_irqrestore(&cam->sg_lock, irqflags);}/* Queue a scatter-gather DMA transfer from the camera to memory. * Returns zero if the transfer was successfully queued, or * non-zero if all of the scatter-gather slots are already in use. */static intcamera_core_sgdma_queue(struct camera_device *cam,        const struct scatterlist *sglist, int sglen, dma_callback_t callback,        void *arg){	unsigned long irqflags;	struct sgdma_state *sgdma;	if ((sglen < 0) || ((sglen > 0) & !sglist))		return -EINVAL;	spin_lock_irqsave(&cam->sg_lock, irqflags);	if (!cam->free_sgdma) {		spin_unlock_irqrestore(&cam->sg_lock, irqflags);		return -EBUSY;	}	sgdma = cam->sgdma + cam->next_sgdma;	sgdma->sglist = sglist;	sgdma->sglen = sglen;	sgdma->next_sglist = 0;	sgdma->queued_sglist = 0;	sgdma->csr = 0;	sgdma->callback = callback;	sgdma->arg = arg;	cam->next_sgdma = (cam->next_sgdma + 1) % (NUM_SG_DMA); 	cam->free_sgdma--;	spin_unlock_irqrestore(&cam->sg_lock, irqflags);	camera_core_sgdma_process(cam);	return 0;}/* -------------------overlay routines ------------------------------*//* callback routine for overlay DMA completion. We just start another DMA * transfer unless overlay has been turned off */static voidcamera_core_overlay_callback(void *arg1, void *arg){	struct camera_device *cam = (struct camera_device *)arg1;	int err;	unsigned long irqflags;	int i, j;	int count, index;	unsigned char *fb_buf = phys_to_virt((unsigned long)camera_dev->fbuf.base);	spin_lock_irqsave(&cam->overlay_lock, irqflags);	if (!cam->previewing || cam->overlay_cnt == 0) {		spin_unlock_irqrestore(&cam->overlay_lock, irqflags);		return;	}	--cam->overlay_cnt;	sg_dma_address(&cam->overlay_sglist) = cam->overlay_base_phys;	sg_dma_len(&cam->overlay_sglist) = cam->pix.sizeimage;	count = 0;	j = ((cam->pix.width - 1) * cam->fbuf.fmt.bytesperline);	for (i = 0 ; i < cam->pix.sizeimage; i += cam->pix.bytesperline) {		for (index = 0; index < cam->pix.bytesperline; index++) {			fb_buf[j] = *(((unsigned char *) cam->overlay_base) +								 i + index);			index++;			fb_buf[j + 1] = *(((unsigned char *) cam->overlay_base) + i + index);			j = j - cam->fbuf.fmt.bytesperline;		}		count += 2;		j = ((cam->pix.width - 1) * cam->fbuf.fmt.bytesperline) + count;	}	while (cam->overlay_cnt < 2) {		err = camera_core_sgdma_queue(cam, &cam->overlay_sglist, 1,			camera_core_overlay_callback, NULL);		if (err)			break;		++cam->overlay_cnt;	}	spin_unlock_irqrestore(&cam->overlay_lock, irqflags);} static voidcamera_core_start_overlay(struct camera_device *cam){	int err;	unsigned long irqflags;	if (!cam->previewing) 		return;	spin_lock_irqsave(&cam->overlay_lock, irqflags);	sg_dma_address(&cam->overlay_sglist) = cam->overlay_base_phys;	sg_dma_len(&cam->overlay_sglist)= cam->pix.sizeimage;	while (cam->overlay_cnt < 2) {		err = camera_core_sgdma_queue(cam, &cam->overlay_sglist, 1,				camera_core_overlay_callback, NULL);		if (err)			break;		++cam->overlay_cnt;	}	spin_unlock_irqrestore(&cam->overlay_lock, irqflags);}/* ------------------ videobuf_queue_ops ---------------------------------------- *//* This routine is called from interrupt context when a scatter-gather DMA * transfer of a videobuf_buffer completes. */static voidcamera_core_vbq_complete(void *arg1, void *arg){	struct camera_device *cam = (struct camera_device *)arg1;	struct videobuf_buffer *vb = (struct videobuf_buffer *)arg;	spin_lock(&cam->vbq_lock);	do_gettimeofday(&vb->ts);	vb->field_count = cam->field_count;	cam->field_count += 2;	vb->state = STATE_DONE;	wake_up(&vb->done);		spin_unlock(&cam->vbq_lock);}static voidcamera_core_vbq_release(struct file *file, struct videobuf_buffer *vb){	videobuf_waiton(vb, 0, 0);	videobuf_dma_pci_unmap(NULL, &vb->dma);	videobuf_dma_free(&vb->dma);	vb->state = STATE_NEEDS_INIT;}/* Limit the number of available kernel image capture buffers based on the * number requested, the currently selected image size, and the maximum * amount of memory permitted for kernel capture buffers. */static intcamera_core_vbq_setup(struct file *file, unsigned int *cnt, unsigned int *size){	struct camera_fh *fh = file->private_data;	struct camera_device *cam = fh->cam;	if (*cnt <= 0)		*cnt = VIDEO_MAX_FRAME; /* supply a default number of buffers */	if (*cnt > VIDEO_MAX_FRAME)		*cnt = VIDEO_MAX_FRAME;	spin_lock(&cam->img_lock);	*size = cam->pix.sizeimage;	spin_unlock(&cam->img_lock);	while (*size * *cnt > capture_mem)		(*cnt)--;	return 0;}static intcamera_core_vbq_prepare(struct file *file, struct videobuf_buffer *vb,        enum v4l2_field field){	struct camera_fh *fh = file->private_data;	struct camera_device *cam = fh->cam;	int err = 0;	spin_lock(&cam->img_lock);	if (cam->pix.sizeimage > vb->bsize) {		spin_unlock(&cam->img_lock);		return -EINVAL;	}	vb->size = cam->pix.sizeimage; 	vb->width = cam->pix.width;	vb->height = cam->pix.height;	vb->field = field;	spin_unlock(&cam->img_lock);	if (vb->state == STATE_NEEDS_INIT)		err = videobuf_iolock(NULL, vb, NULL);	if (!err)		vb->state = STATE_PREPARED;	else		camera_core_vbq_release (file, vb);	return err;}static voidcamera_core_vbq_queue(struct file *file, struct videobuf_buffer *vb){	struct camera_fh *fh = file->private_data;	struct camera_device *cam = fh->cam;	enum videobuf_state state = vb->state;	int err;	vb->state = STATE_QUEUED;	err = camera_core_sgdma_queue(cam, vb->dma.sglist, vb->dma.sglen,                camera_core_vbq_complete, vb);	if (err) {		/* Oops.  We're not supposed to get any errors here.  The only		* way we could get an error is if we ran out of scatter-gather		* DMA slots, but we are supposed to have at least as many		* scatter-gather DMA slots as video buffers so that can't		* happen.		*/		printk(KERN_DEBUG CAM_NAME			": Failed to queue a video buffer for SGDMA\n");		vb->state = state;	}}/* ------------------ videobuf_queue_ops ---------------------------------------- */static intcamera_core_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, 		     void *arg){	struct camera_fh *fh  = file->private_data;	struct camera_device *cam = fh->cam;	int err;	switch (cmd) {		case VIDIOC_ENUMINPUT:		{			/* default handler assumes 1 video input (the camera) */			struct v4l2_input *input = (struct v4l2_input *)arg;			int index = input->index;			memset(input, 0, sizeof(*input));			input->index = index;			if (index > 0)				return -EINVAL;			strlcpy(input->name, "camera", sizeof(input->name));			input->type = V4L2_INPUT_TYPE_CAMERA;			return 0;		}		case VIDIOC_G_INPUT:		{			unsigned int *input = arg;			*input = 0;			return 0;		}		case VIDIOC_S_INPUT:		{			unsigned int *input = arg;			if (*input > 0)				return -EINVAL;			return 0;		}		case VIDIOC_ENUM_FMT:		{			struct v4l2_fmtdesc *fmt = arg;			return cam->cam_sensor->enum_pixformat(fmt, cam->sensor_data);		}			case VIDIOC_TRY_FMT:		{			struct v4l2_format *fmt = arg;			return cam->cam_sensor->try_format(&fmt->fmt.pix, cam->sensor_data);		}		case VIDIOC_G_FMT:		{			struct v4l2_format *fmt = arg;			/* get the current format */			memset(&fmt->fmt.pix, 0, sizeof (fmt->fmt.pix));			fmt->fmt.pix = cam->pix;						return 0;		}		case VIDIOC_S_FMT:		{			struct v4l2_format *fmt = arg;			unsigned int temp_sizeimage = 0;			temp_sizeimage = cam->pix.sizeimage;			cam->cam_sensor->try_format(&fmt->fmt.pix, cam->sensor_data);			cam->pix = fmt->fmt.pix; 			cam->xclk = cam->cam_sensor->calc_xclk(&cam->pix, 				&cam->nominal_timeperframe, cam->sensor_data); 			cam->cparm.timeperframe = cam->nominal_timeperframe;			cam->xclk = cam->cam_hardware->set_xclk(cam->xclk, cam->hardware_data);			return cam->cam_sensor->configure(&cam->pix, cam->xclk, 						&cam->cparm.timeperframe, cam->sensor_data);		}		case VIDIOC_QUERYCTRL:		{			struct v4l2_queryctrl *qc = arg;			return cam->cam_sensor->query_control(qc, cam->sensor_data);		}		case VIDIOC_G_CTRL:		{			struct v4l2_control *vc = arg;			return cam->cam_sensor->get_control(vc, cam->sensor_data);		}		case VIDIOC_S_CTRL:		{			struct v4l2_control *vc = arg;			return cam->cam_sensor->set_control(vc, cam->sensor_data);		}				case VIDIOC_QUERYCAP:		{			struct v4l2_capability *cap = 				(struct v4l2_capability *) arg;			memset(cap, 0, sizeof(*cap));			strlcpy(cap->driver, CAM_NAME, sizeof(cap->driver));			strlcpy(cap->card, cam->vfd->name, sizeof(cap->card));			cap->bus_info[0] = '\0';			cap->version = KERNEL_VERSION(0, 0, 0);			cap->capabilities =				V4L2_CAP_VIDEO_CAPTURE |				V4L2_CAP_VIDEO_OVERLAY |				V4L2_CAP_READWRITE | 				V4L2_CAP_STREAMING;			return 0;		}		case VIDIOC_G_FBUF: /* Get the frame buffer parameters */		{			struct v4l2_framebuffer *fbuf =				(struct v4l2_framebuffer *) arg;			spin_lock(&cam->img_lock);			*fbuf = cam->fbuf;			spin_unlock(&cam->img_lock);			return 0;		}		case VIDIOC_S_FBUF: /* set the frame buffer parameters */		{			struct v4l2_framebuffer *fbuf =				(struct v4l2_framebuffer *) arg;			spin_lock(&cam->img_lock);			if (cam->previewing) {				spin_unlock(&cam->img_lock);				return -EBUSY;			}			cam->fbuf.base = fbuf->base;			cam->fbuf.fmt = fbuf->fmt;							spin_unlock(&cam->img_lock);			return 0;		}		case VIDIOC_OVERLAY:		{			int enable = *((int *) arg);			/* 			 * check whether the capture format and 			 ** the display format matches 			 * return failure if they are different			 */			if (cam->pix.pixelformat != cam->fbuf.fmt.pixelformat)			{				return -EINVAL;			}			/* If the camera image size is greater 			** than LCD size return failure */			if ((cam->pix.width > cam->fbuf.fmt.height) || 				(cam->pix.height > cam->fbuf.fmt.width))			{				return -EINVAL;			}						if (!cam->previewing && enable)			{				cam->previewing = fh;				cam->overlay_cnt = 0;				camera_core_start_overlay(cam);			}			else if (!enable)			{				cam->previewing = NULL;			}				return 0;		}		case VIDIOC_REQBUFS:			return videobuf_reqbufs(file, &fh->vbq, arg);		case VIDIOC_QUERYBUF:			return videobuf_querybuf(&fh->vbq, arg);		case VIDIOC_QBUF:			return videobuf_qbuf(file, &fh->vbq, arg);		case VIDIOC_DQBUF:			return videobuf_dqbuf(file, &fh->vbq, arg);		case VIDIOC_STREAMON:

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?