vino.c

来自「linux-2.4.29操作系统的源码」· C语言 代码 · 共 1,197 行 · 第 1/2 页

C
1,197
字号
/* * Driver for the VINO (Video In No Out) system found in SGI Indys. *  * This file is subject to the terms and conditions of the GNU General Public * License version 2 as published by the Free Software Foundation. * * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org> */#include <linux/module.h>#include <linux/kmod.h>#include <linux/init.h>#include <linux/types.h>#include <linux/errno.h>#include <linux/mm.h>#include <linux/wrapper.h>#include <linux/irq.h>#include <linux/delay.h>#include <linux/pci.h>#include <linux/videodev.h>#include <linux/video_decoder.h>#include <linux/i2c.h>#include <linux/i2c-algo-sgi.h>#include <asm/paccess.h>#include <asm/io.h>#include <asm/sgi/ip22.h>#include <asm/sgi/hpc3.h>#include <asm/sgi/mc.h>#include "vino.h"/* debugging? */#if 1#define DEBUG(x...)	printk(x);#else#define DEBUG(x...)#endif/* VINO video size */#define VINO_PAL_WIDTH		768#define VINO_PAL_HEIGHT		576#define VINO_NTSC_WIDTH		646#define VINO_NTSC_HEIGHT	486/* set this to some sensible values. note: VINO_MIN_WIDTH has to be 8*x */#define VINO_MIN_WIDTH		32#define VINO_MIN_HEIGHT		32/* channel selection */#define VINO_INPUT_COMP		0#define VINO_INPUT_SVIDEO	1#define VINO_INPUT_CAMERA	2#define VINO_INPUT_CHANNELS	3#define PAGE_RATIO	(PAGE_SIZE / VINO_PAGE_SIZE)/* VINO ASIC registers */struct sgi_vino *vino;static const char *vinostr = "VINO IndyCam/TV";static int threshold_a = 512;static int threshold_b = 512;struct vino_device {	struct video_device vdev;#define VINO_CHAN_A	1#define VINO_CHAN_B	2	int chan;	int alpha;	/* clipping... */	unsigned int left, right, top, bottom;	/* decimation used */	unsigned int decimation;	/* palette used, picture hue, etc */	struct video_picture picture;	/* VINO_INPUT_COMP, VINO_INPUT_SVIDEO or VINO_INPUT_CAMERA */	unsigned int input;	/* bytes per line */	unsigned int line_size;	/* descriptor table (virtual addresses) */	unsigned long *desc;	/* # of allocated pages */	int page_count;	/* descriptor table (dma addresses) */	struct {		dma_addr_t *cpu;		dma_addr_t dma;	} dma_desc;	/* add some more space to let VINO trigger End Of Field interrupt	 * before reaching end of buffer */#define VINO_FBUFSIZE	(VINO_PAL_WIDTH * VINO_PAL_HEIGHT * 4 + 2 * PAGE_SIZE)	unsigned int frame_size;#define VINO_BUF_UNUSED		0#define VINO_BUF_GRABBING	1#define VINO_BUF_DONE		2	int buffer_state;	wait_queue_head_t dma_wait;	spinlock_t state_lock;	struct semaphore sem;	/* Make sure we only have one user at the time */	int users;};struct vino_client {	struct i2c_client *driver;	int owner;};struct vino_video {	struct vino_device chA;	struct vino_device chB;	struct vino_client decoder;	struct vino_client camera;	spinlock_t vino_lock;	spinlock_t input_lock;	/* Loaded into VINO descriptors to clear End Of Descriptors table	 * interupt condition */	unsigned long dummy_desc;	struct {		dma_addr_t *cpu;		dma_addr_t dma;	} dummy_dma;};static struct vino_video *Vino;/* --- */unsigned i2c_vino_getctrl(void *data){	return vino->i2c_control;}void i2c_vino_setctrl(void *data, unsigned val){	vino->i2c_control = val;}unsigned i2c_vino_rdata(void *data){	return vino->i2c_data;}void i2c_vino_wdata(void *data, unsigned val){	vino->i2c_data = val;}static struct i2c_algo_sgi_data i2c_sgi_vino_data ={	.getctrl = &i2c_vino_getctrl,	.setctrl = &i2c_vino_setctrl,	.rdata   = &i2c_vino_rdata,	.wdata   = &i2c_vino_wdata,	.xfer_timeout = 200,	.ack_timeout  = 1000,};/* * There are two possible clients on VINO I2C bus, so we limit usage only * to them. */static int i2c_vino_client_reg(struct i2c_client *client){	int res = 0;	spin_lock(&Vino->input_lock);	switch (client->driver->id) {	case I2C_DRIVERID_SAA7191:		if (Vino->decoder.driver)			res = -EBUSY;		else			Vino->decoder.driver = client;		break;	case I2C_DRIVERID_INDYCAM:		if (Vino->camera.driver)			res = -EBUSY;		else			Vino->camera.driver = client;		break;	default:		res = -ENODEV;	}	spin_unlock(&Vino->input_lock);	return res;}static int i2c_vino_client_unreg(struct i2c_client *client){	int res = 0;		spin_lock(&Vino->input_lock);	if (client == Vino->decoder.driver) {		if (Vino->decoder.owner)			res = -EBUSY;		else			Vino->decoder.driver = NULL;	} else if (client == Vino->camera.driver) {		if (Vino->camera.owner)			res = -EBUSY;		else			Vino->camera.driver = NULL;	}	spin_unlock(&Vino->input_lock);	return res;}static struct i2c_adapter vino_i2c_adapter ={	.name			= "VINO I2C bus",	.id			= I2C_HW_SGI_VINO,	.algo_data		= &i2c_sgi_vino_data,	.client_register	= &i2c_vino_client_reg,	.client_unregister	= &i2c_vino_client_unreg,};static int vino_i2c_add_bus(void){	return i2c_sgi_add_bus(&vino_i2c_adapter);}static int vino_i2c_del_bus(void){	return i2c_sgi_del_bus(&vino_i2c_adapter);}static int i2c_camera_command(unsigned int cmd, void *arg){	return Vino->camera.driver->driver->command(Vino->camera.driver,						    cmd, arg);}static int i2c_decoder_command(unsigned int cmd, void *arg){	return Vino->decoder.driver->driver->command(Vino->decoder.driver,						     cmd, arg);}/* --- */static int bytes_per_pixel(struct vino_device *v){	switch (v->picture.palette) {	case VIDEO_PALETTE_GREY:		return 1;	case VIDEO_PALETTE_YUV422:		return 2;	default: /* VIDEO_PALETTE_RGB32 */		return 4;	}}static int get_capture_norm(struct vino_device *v){	if (v->input == VINO_INPUT_CAMERA)		return VIDEO_MODE_NTSC;	else {		/* TODO */		return VIDEO_MODE_NTSC;	}}/* * Set clipping. Try new values to fit, if they don't return -EINVAL */static int set_clipping(struct vino_device *v, int x, int y, int w, int h,			int d){	int maxwidth, maxheight, lsize;	if (d < 1)		d = 1;	if (d > 8)		d = 8;	if (w / d < VINO_MIN_WIDTH || h / d < VINO_MIN_HEIGHT)		return -EINVAL;	if (get_capture_norm(v) == VIDEO_MODE_NTSC) {		maxwidth = VINO_NTSC_WIDTH;		maxheight = VINO_NTSC_HEIGHT;	} else {		maxwidth = VINO_PAL_WIDTH;		maxheight = VINO_PAL_HEIGHT;	}	if (x < 0)		x = 0;	if (y < 0)		y = 0;	y &= ~1;	/* odd/even fields */	if (x + w > maxwidth) {		w = maxwidth - x;		if (w / d < VINO_MIN_WIDTH)			x = maxwidth - VINO_MIN_WIDTH * d;	}	if (y + h > maxheight) {		h = maxheight - y;		if (h / d < VINO_MIN_HEIGHT)			y = maxheight - VINO_MIN_HEIGHT * d;	}	/* line size must be multiple of 8 bytes */	lsize = (bytes_per_pixel(v) * w / d) & ~7;	w = lsize * d / bytes_per_pixel(v);	v->left = x;	v->top = y;	v->right = x + w;	v->bottom = y + h;	v->decimation = d;	v->line_size = lsize;	DEBUG("VINO: clipping %d, %d, %d, %d / %d - %d\n", v->left, v->top,	      v->right, v->bottom, v->decimation, v->line_size);	return 0;}static int set_scaling(struct vino_device *v, int w, int h){	int maxwidth, maxheight, lsize, d;	if (w < VINO_MIN_WIDTH || h < VINO_MIN_HEIGHT)		return -EINVAL;	if (get_capture_norm(v) == VIDEO_MODE_NTSC) {		maxwidth = VINO_NTSC_WIDTH;		maxheight = VINO_NTSC_HEIGHT;	} else {		maxwidth = VINO_PAL_WIDTH;		maxheight = VINO_PAL_HEIGHT;	}	if (w > maxwidth)		w = maxwidth;	if (h > maxheight)		h = maxheight;	d = max(maxwidth / w, maxheight / h);	if (d > 8)		d = 8;	/* line size must be multiple of 8 bytes */	lsize = (bytes_per_pixel(v) * w) & ~7;	w = lsize * d / bytes_per_pixel(v);	h *= d;	if (v->left + w > maxwidth)		v->left = maxwidth - w;	if (v->top + h > maxheight)		v->top = (maxheight - h) & ~1;	/* odd/even fields */	/* FIXME: -1 bug... Verify clipping with video signal generator */	v->right = v->left + w;	v->bottom = v->top + h;	v->decimation = d;	v->line_size = lsize;	DEBUG("VINO: scaling %d, %d, %d, %d / %d - %d\n", v->left, v->top,	      v->right, v->bottom, v->decimation, v->line_size);	return 0;}/*  * Prepare vino for DMA transfer... (execute only with vino_lock locked) */static int dma_setup(struct vino_device *v){	u32 ctrl, intr;	struct sgi_vino_channel *ch;	ch = (v->chan == VINO_CHAN_A) ? &vino->a : &vino->b;	ch->page_index = 0;	ch->line_count = 0;	/* let VINO know where to transfer data */	ch->start_desc_tbl = v->dma_desc.dma;	ch->next_4_desc = v->dma_desc.dma;	/* give vino time to fetch the first four descriptors, 5 usec	 * should be more than enough time */	udelay(5);	/* VINO line size register is set 8 bytes less than actual */	ch->line_size = v->line_size - 8;	/* set the alpha register */	ch->alpha = v->alpha;	/* set cliping registers */	ch->clip_start = VINO_CLIP_ODD(v->top) | VINO_CLIP_EVEN(v->top+1) |			 VINO_CLIP_X(v->left);	ch->clip_end = VINO_CLIP_ODD(v->bottom) | VINO_CLIP_EVEN(v->bottom+1) |		       VINO_CLIP_X(v->right);	/* FIXME: end-of-field bug workaround		       VINO_CLIP_X(VINO_PAL_WIDTH);	 */	/* init the frame rate and norm (full frame rate only for now...) */	ch->frame_rate = VINO_FRAMERT_RT(0x1fff) |			 (get_capture_norm(v) == VIDEO_MODE_PAL ?			  VINO_FRAMERT_PAL : 0);	ctrl = vino->control;	intr = vino->intr_status;	if (v->chan == VINO_CHAN_A) {		/* All interrupt conditions for this channel was cleared		 * so clear the interrupt status register and enable		 * interrupts */		intr &=	~VINO_INTSTAT_A;		ctrl |= VINO_CTRL_A_INT;		/* enable synchronization */		ctrl |= VINO_CTRL_A_SYNC_ENBL;		/* enable frame assembly */		ctrl |= VINO_CTRL_A_INTERLEAVE_ENBL;		/* set decimation used */		if (v->decimation < 2)			ctrl &= ~VINO_CTRL_A_DEC_ENBL;		else {			ctrl |= VINO_CTRL_A_DEC_ENBL;			ctrl &= ~VINO_CTRL_A_DEC_SCALE_MASK;			ctrl |= (v->decimation - 1) <<				VINO_CTRL_A_DEC_SCALE_SHIFT;		}		/* select input interface */		if (v->input == VINO_INPUT_CAMERA)			ctrl |= VINO_CTRL_A_SELECT;		else			ctrl &= ~VINO_CTRL_A_SELECT;		/* palette */		ctrl &= ~(VINO_CTRL_A_LUMA_ONLY | VINO_CTRL_A_RGB |			  VINO_CTRL_A_DITHER);	} else {		intr &= ~VINO_INTSTAT_B;		ctrl |= VINO_CTRL_B_INT;		ctrl |= VINO_CTRL_B_SYNC_ENBL;		ctrl |= VINO_CTRL_B_INTERLEAVE_ENBL;		if (v->decimation < 2)			ctrl &= ~VINO_CTRL_B_DEC_ENBL;		else {			ctrl |= VINO_CTRL_B_DEC_ENBL;			ctrl &= ~VINO_CTRL_B_DEC_SCALE_MASK;			ctrl |= (v->decimation - 1) <<				VINO_CTRL_B_DEC_SCALE_SHIFT;		}		if (v->input == VINO_INPUT_CAMERA)			ctrl |= VINO_CTRL_B_SELECT;		else			ctrl &= ~VINO_CTRL_B_SELECT;		ctrl &= ~(VINO_CTRL_B_LUMA_ONLY | VINO_CTRL_B_RGB |			  VINO_CTRL_B_DITHER);	}	/* set palette */	switch (v->picture.palette) {		case VIDEO_PALETTE_GREY:			ctrl |= (v->chan == VINO_CHAN_A) ? 				VINO_CTRL_A_LUMA_ONLY : VINO_CTRL_B_LUMA_ONLY;			break;		case VIDEO_PALETTE_RGB32:			ctrl |= (v->chan == VINO_CHAN_A) ?				VINO_CTRL_A_RGB : VINO_CTRL_B_RGB;			break;#if 0		/* FIXME: this is NOT in v4l API :-( */		case VIDEO_PALETTE_RGB332:			ctrl |= (v->chan == VINO_CHAN_A) ?				VINO_CTRL_A_RGB | VINO_CTRL_A_DITHER : 				VINO_CTRL_B_RGB | VINO_CTRL_B_DITHER;			break;#endif	}	vino->control = ctrl;	vino->intr_status = intr;	return 0;}/* (execute only with vino_lock locked) */static void dma_stop(struct vino_device *v){	u32 ctrl = vino->control;	ctrl &= (v->chan == VINO_CHAN_A) ?		~VINO_CTRL_A_DMA_ENBL : ~VINO_CTRL_B_DMA_ENBL;	vino->control = ctrl;}/* (execute only with vino_lock locked) */static void dma_go(struct vino_device *v){	u32 ctrl = vino->control;	ctrl |= (v->chan == VINO_CHAN_A) ?		VINO_CTRL_A_DMA_ENBL : VINO_CTRL_B_DMA_ENBL;	vino->control = ctrl;}/* * Load dummy page to descriptor registers. This prevents generating of * spurious interrupts. (execute only with vino_lock locked) */static void clear_eod(struct vino_device *v){	struct sgi_vino_channel *ch;	DEBUG("VINO: chnl %c clear EOD\n", (v->chan == VINO_CHAN_A) ? 'A':'B');	ch = (v->chan == VINO_CHAN_A) ? &vino->a : &vino->b;	ch->page_index = 0;	ch->line_count = 0;	ch->start_desc_tbl = Vino->dummy_dma.dma;	ch->next_4_desc = Vino->dummy_dma.dma;	udelay(5);}static void field_done(struct vino_device *v){	spin_lock(&v->state_lock);	if (v->buffer_state == VINO_BUF_GRABBING)		v->buffer_state = VINO_BUF_DONE;	spin_unlock(&v->state_lock);	wake_up(&v->dma_wait);}static void vino_interrupt(int irq, void *dev_id, struct pt_regs *regs){	u32 intr, ctrl;	spin_lock(&Vino->vino_lock);	ctrl = vino->control;	intr = vino->intr_status;	DEBUG("VINO: intr status %04x\n", intr);	if (intr & (VINO_INTSTAT_A_FIFO | VINO_INTSTAT_A_EOD)) {		ctrl &= ~VINO_CTRL_A_DMA_ENBL;		vino->control = ctrl;		clear_eod(&Vino->chA);	}	if (intr & (VINO_INTSTAT_B_FIFO | VINO_INTSTAT_B_EOD)) {		ctrl &= ~VINO_CTRL_B_DMA_ENBL;		vino->control = ctrl;		clear_eod(&Vino->chB);	}	vino->intr_status = ~intr;	spin_unlock(&Vino->vino_lock);	/* FIXME: For now we are assuming that interrupt means that frame is	 * done. That's not true, but we can live with such brokeness for	 * a while ;-) */	field_done(&Vino->chA);}static int vino_grab(struct vino_device *v, int frame){	int err = 0;	spin_lock_irq(&v->state_lock);	if (v->buffer_state == VINO_BUF_GRABBING)		err = -EBUSY;	v->buffer_state = VINO_BUF_GRABBING;	spin_unlock_irq(&v->state_lock);	if (err)		return err;	spin_lock_irq(&Vino->vino_lock);	dma_setup(v);	dma_go(v);	spin_unlock_irq(&Vino->vino_lock);	return 0;}static int vino_waitfor(struct vino_device *v, int frame){	wait_queue_t wait;	int i, err = 0;	if (frame != 0)		return -EINVAL;	spin_lock_irq(&v->state_lock);	switch (v->buffer_state) {	case VINO_BUF_GRABBING:		init_waitqueue_entry(&wait, current);		/* add ourselves into wait queue */		add_wait_queue(&v->dma_wait, &wait);		/* and set current state */		set_current_state(TASK_INTERRUPTIBLE);		/* before releasing spinlock */		spin_unlock_irq(&v->state_lock);		/* to ensure that schedule_timeout will return imediately		 * if VINO interrupt was triggred meanwhile */		schedule_timeout(HZ / 10);		if (signal_pending(current))			err = -EINTR;		spin_lock_irq(&v->state_lock);		remove_wait_queue(&v->dma_wait, &wait);		/* don't rely on schedule_timeout return value and check what		 * really happened */		if (!err && v->buffer_state == VINO_BUF_GRABBING)			err = -EIO;		/* fall through */	case VINO_BUF_DONE:		for (i = 0; i < v->page_count; i++)			pci_dma_sync_single(NULL, v->dma_desc.cpu[PAGE_RATIO*i],					    PAGE_SIZE, PCI_DMA_FROMDEVICE);		v->buffer_state = VINO_BUF_UNUSED;		break;	default:		err = -EINVAL;	}	spin_unlock_irq(&v->state_lock);	if (err && err != -EINVAL) {		DEBUG("VINO: waiting for frame failed\n");

⌨️ 快捷键说明

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