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

📄 cx88-video.c

📁 底层驱动开发
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * * device driver for Conexant 2388x based TV cards * video4linux video interface * * (c) 2003-04 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs] * *  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. * *  This program is distributed in the hope that it will be useful, *  but WITHOUT ANY WARRANTY; without even the implied warranty of *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the *  GNU General Public License for more details. * *  You should have received a copy of the GNU General Public License *  along with this program; if not, write to the Free Software *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */#include <linux/init.h>#include <linux/list.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/kmod.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/interrupt.h>#include <linux/delay.h>#include <linux/kthread.h>#include <asm/div64.h>#include "cx88.h"MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards");MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");MODULE_LICENSE("GPL");/* ------------------------------------------------------------------ */static unsigned int video_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };static unsigned int vbi_nr[]   = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };static unsigned int radio_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };module_param_array(video_nr, int, NULL, 0444);module_param_array(vbi_nr,   int, NULL, 0444);module_param_array(radio_nr, int, NULL, 0444);MODULE_PARM_DESC(video_nr,"video device numbers");MODULE_PARM_DESC(vbi_nr,"vbi device numbers");MODULE_PARM_DESC(radio_nr,"radio device numbers");static unsigned int video_debug = 0;module_param(video_debug,int,0644);MODULE_PARM_DESC(video_debug,"enable debug messages [video]");static unsigned int irq_debug = 0;module_param(irq_debug,int,0644);MODULE_PARM_DESC(irq_debug,"enable debug messages [IRQ handler]");static unsigned int vid_limit = 16;module_param(vid_limit,int,0644);MODULE_PARM_DESC(vid_limit,"capture memory limit in megabytes");#define dprintk(level,fmt, arg...)	if (video_debug >= level) \	printk(KERN_DEBUG "%s/0: " fmt, core->name , ## arg)/* ------------------------------------------------------------------ */static LIST_HEAD(cx8800_devlist);/* ------------------------------------------------------------------- *//* static data                                                         */static struct cx88_tvnorm tvnorms[] = {	{		.name      = "NTSC-M",		.id        = V4L2_STD_NTSC_M,		.cxiformat = VideoFormatNTSC,		.cxoformat = 0x181f0008,	},{		.name      = "NTSC-JP",		.id        = V4L2_STD_NTSC_M_JP,		.cxiformat = VideoFormatNTSCJapan,		.cxoformat = 0x181f0008,	},{		.name      = "PAL-BG",		.id        = V4L2_STD_PAL_BG,		.cxiformat = VideoFormatPAL,		.cxoformat = 0x181f0008,	},{		.name      = "PAL-DK",		.id        = V4L2_STD_PAL_DK,		.cxiformat = VideoFormatPAL,		.cxoformat = 0x181f0008,	},{		.name      = "PAL-I",		.id        = V4L2_STD_PAL_I,		.cxiformat = VideoFormatPAL,		.cxoformat = 0x181f0008,        },{		.name      = "PAL-M",		.id        = V4L2_STD_PAL_M,		.cxiformat = VideoFormatPALM,		.cxoformat = 0x1c1f0008,	},{		.name      = "PAL-N",		.id        = V4L2_STD_PAL_N,		.cxiformat = VideoFormatPALN,		.cxoformat = 0x1c1f0008,	},{		.name      = "PAL-Nc",		.id        = V4L2_STD_PAL_Nc,		.cxiformat = VideoFormatPALNC,		.cxoformat = 0x1c1f0008,	},{		.name      = "PAL-60",		.id        = V4L2_STD_PAL_60,		.cxiformat = VideoFormatPAL60,		.cxoformat = 0x181f0008,	},{		.name      = "SECAM-L",		.id        = V4L2_STD_SECAM_L,		.cxiformat = VideoFormatSECAM,		.cxoformat = 0x181f0008,	},{		.name      = "SECAM-DK",		.id        = V4L2_STD_SECAM_DK,		.cxiformat = VideoFormatSECAM,		.cxoformat = 0x181f0008,	}};static struct cx8800_fmt formats[] = {	{		.name     = "8 bpp, gray",		.fourcc   = V4L2_PIX_FMT_GREY,		.cxformat = ColorFormatY8,		.depth    = 8,		.flags    = FORMAT_FLAGS_PACKED,	},{		.name     = "15 bpp RGB, le",		.fourcc   = V4L2_PIX_FMT_RGB555,		.cxformat = ColorFormatRGB15,		.depth    = 16,		.flags    = FORMAT_FLAGS_PACKED,	},{		.name     = "15 bpp RGB, be",		.fourcc   = V4L2_PIX_FMT_RGB555X,		.cxformat = ColorFormatRGB15 | ColorFormatBSWAP,		.depth    = 16,		.flags    = FORMAT_FLAGS_PACKED,	},{		.name     = "16 bpp RGB, le",		.fourcc   = V4L2_PIX_FMT_RGB565,		.cxformat = ColorFormatRGB16,		.depth    = 16,		.flags    = FORMAT_FLAGS_PACKED,	},{		.name     = "16 bpp RGB, be",		.fourcc   = V4L2_PIX_FMT_RGB565X,		.cxformat = ColorFormatRGB16 | ColorFormatBSWAP,		.depth    = 16,		.flags    = FORMAT_FLAGS_PACKED,	},{		.name     = "24 bpp RGB, le",		.fourcc   = V4L2_PIX_FMT_BGR24,		.cxformat = ColorFormatRGB24,		.depth    = 24,		.flags    = FORMAT_FLAGS_PACKED,	},{		.name     = "32 bpp RGB, le",		.fourcc   = V4L2_PIX_FMT_BGR32,		.cxformat = ColorFormatRGB32,		.depth    = 32,		.flags    = FORMAT_FLAGS_PACKED,	},{		.name     = "32 bpp RGB, be",		.fourcc   = V4L2_PIX_FMT_RGB32,		.cxformat = ColorFormatRGB32 | ColorFormatBSWAP | ColorFormatWSWAP,		.depth    = 32,		.flags    = FORMAT_FLAGS_PACKED,	},{		.name     = "4:2:2, packed, YUYV",		.fourcc   = V4L2_PIX_FMT_YUYV,		.cxformat = ColorFormatYUY2,		.depth    = 16,		.flags    = FORMAT_FLAGS_PACKED,	},{		.name     = "4:2:2, packed, UYVY",		.fourcc   = V4L2_PIX_FMT_UYVY,		.cxformat = ColorFormatYUY2 | ColorFormatBSWAP,		.depth    = 16,		.flags    = FORMAT_FLAGS_PACKED,	},};static struct cx8800_fmt* format_by_fourcc(unsigned int fourcc){	unsigned int i;	for (i = 0; i < ARRAY_SIZE(formats); i++)		if (formats[i].fourcc == fourcc)			return formats+i;	return NULL;}/* ------------------------------------------------------------------- */static const struct v4l2_queryctrl no_ctl = {	.name  = "42",	.flags = V4L2_CTRL_FLAG_DISABLED,};static struct cx88_ctrl cx8800_ctls[] = {	/* --- video --- */	{		.v = {			.id            = V4L2_CID_BRIGHTNESS,			.name          = "Brightness",			.minimum       = 0x00,			.maximum       = 0xff,			.step          = 1,			.default_value = 0,			.type          = V4L2_CTRL_TYPE_INTEGER,		},		.off                   = 128,		.reg                   = MO_CONTR_BRIGHT,		.mask                  = 0x00ff,		.shift                 = 0,	},{		.v = {			.id            = V4L2_CID_CONTRAST,			.name          = "Contrast",			.minimum       = 0,			.maximum       = 0xff,			.step          = 1,			.default_value = 0,			.type          = V4L2_CTRL_TYPE_INTEGER,		},		.off                   = 0,		.reg                   = MO_CONTR_BRIGHT,		.mask                  = 0xff00,		.shift                 = 8,	},{		.v = {			.id            = V4L2_CID_HUE,			.name          = "Hue",			.minimum       = 0,			.maximum       = 0xff,			.step          = 1,			.default_value = 0,			.type          = V4L2_CTRL_TYPE_INTEGER,		},		.off                   = 128,		.reg                   = MO_HUE,		.mask                  = 0x00ff,		.shift                 = 0,	},{		/* strictly, this only describes only U saturation.		 * V saturation is handled specially through code.		 */		.v = {			.id            = V4L2_CID_SATURATION,			.name          = "Saturation",			.minimum       = 0,			.maximum       = 0xff,			.step          = 1,			.default_value = 0,			.type          = V4L2_CTRL_TYPE_INTEGER,		},		.off                   = 0,		.reg                   = MO_UV_SATURATION,		.mask                  = 0x00ff,		.shift                 = 0,	},{	/* --- audio --- */		.v = {			.id            = V4L2_CID_AUDIO_MUTE,			.name          = "Mute",			.minimum       = 0,			.maximum       = 1,			.type          = V4L2_CTRL_TYPE_BOOLEAN,		},		.reg                   = AUD_VOL_CTL,		.sreg                  = SHADOW_AUD_VOL_CTL,		.mask                  = (1 << 6),		.shift                 = 6,	},{		.v = {			.id            = V4L2_CID_AUDIO_VOLUME,			.name          = "Volume",			.minimum       = 0,			.maximum       = 0x3f,			.step          = 1,			.default_value = 0,			.type          = V4L2_CTRL_TYPE_INTEGER,		},		.reg                   = AUD_VOL_CTL,		.sreg                  = SHADOW_AUD_VOL_CTL,		.mask                  = 0x3f,		.shift                 = 0,	},{		.v = {			.id            = V4L2_CID_AUDIO_BALANCE,			.name          = "Balance",			.minimum       = 0,			.maximum       = 0x7f,			.step          = 1,			.default_value = 0x40,			.type          = V4L2_CTRL_TYPE_INTEGER,		},		.reg                   = AUD_BAL_CTL,		.sreg                  = SHADOW_AUD_BAL_CTL,		.mask                  = 0x7f,		.shift                 = 0,	}};static const int CX8800_CTLS = ARRAY_SIZE(cx8800_ctls);/* ------------------------------------------------------------------- *//* resource management                                                 */static int res_get(struct cx8800_dev *dev, struct cx8800_fh *fh, unsigned int bit){	struct cx88_core *core = dev->core;	if (fh->resources & bit)		/* have it already allocated */		return 1;	/* is it free? */	down(&core->lock);	if (dev->resources & bit) {		/* no, someone else uses it */		up(&core->lock);		return 0;	}	/* it's free, grab it */	fh->resources  |= bit;	dev->resources |= bit;	dprintk(1,"res: get %d\n",bit);	up(&core->lock);	return 1;}staticint res_check(struct cx8800_fh *fh, unsigned int bit){	return (fh->resources & bit);}staticint res_locked(struct cx8800_dev *dev, unsigned int bit){	return (dev->resources & bit);}staticvoid res_free(struct cx8800_dev *dev, struct cx8800_fh *fh, unsigned int bits){	struct cx88_core *core = dev->core;	if ((fh->resources & bits) != bits)		BUG();	down(&core->lock);	fh->resources  &= ~bits;	dev->resources &= ~bits;	dprintk(1,"res: put %d\n",bits);	up(&core->lock);}/* ------------------------------------------------------------------ *//* static int video_mux(struct cx8800_dev *dev, unsigned int input) */static int video_mux(struct cx88_core *core, unsigned int input){	/* struct cx88_core *core = dev->core; */	dprintk(1,"video_mux: %d [vmux=%d,gpio=0x%x,0x%x,0x%x,0x%x]\n",		input, INPUT(input)->vmux,		INPUT(input)->gpio0,INPUT(input)->gpio1,		INPUT(input)->gpio2,INPUT(input)->gpio3);	core->input = input;	cx_andor(MO_INPUT_FORMAT, 0x03 << 14, INPUT(input)->vmux << 14);	cx_write(MO_GP3_IO, INPUT(input)->gpio3);	cx_write(MO_GP0_IO, INPUT(input)->gpio0);	cx_write(MO_GP1_IO, INPUT(input)->gpio1);	cx_write(MO_GP2_IO, INPUT(input)->gpio2);	switch (INPUT(input)->type) {	case CX88_VMUX_SVIDEO:		cx_set(MO_AFECFG_IO,    0x00000001);		cx_set(MO_INPUT_FORMAT, 0x00010010);		cx_set(MO_FILTER_EVEN,  0x00002020);		cx_set(MO_FILTER_ODD,   0x00002020);		break;	default:		cx_clear(MO_AFECFG_IO,    0x00000001);		cx_clear(MO_INPUT_FORMAT, 0x00010010);		cx_clear(MO_FILTER_EVEN,  0x00002020);		cx_clear(MO_FILTER_ODD,   0x00002020);		break;	}	return 0;}/* ------------------------------------------------------------------ */static int start_video_dma(struct cx8800_dev    *dev,			   struct cx88_dmaqueue *q,			   struct cx88_buffer   *buf){	struct cx88_core *core = dev->core;	/* setup fifo + format */	cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH21],				buf->bpl, buf->risc.dma);	cx88_set_scale(core, buf->vb.width, buf->vb.height, buf->vb.field);	cx_write(MO_COLOR_CTRL, buf->fmt->cxformat | ColorFormatGamma);	/* reset counter */	cx_write(MO_VIDY_GPCNTRL,GP_COUNT_CONTROL_RESET);	q->count = 1;	/* enable irqs */	cx_set(MO_PCI_INTMSK, core->pci_irqmask | 0x01);	/* Enables corresponding bits at PCI_INT_STAT:		bits 0 to 4: video, audio, transport stream, VIP, Host		bit 7: timer		bits 8 and 9: DMA complete for: SRC, DST		bits 10 and 11: BERR signal asserted for RISC: RD, WR		bits 12 to 15: BERR signal asserted for: BRDG, SRC, DST, IPB	 */	cx_set(MO_VID_INTMSK, 0x0f0011);	/* enable capture */	cx_set(VID_CAPTURE_CONTROL,0x06);	/* start dma */	cx_set(MO_DEV_CNTRL2, (1<<5));	cx_set(MO_VID_DMACNTRL, 0x11); /* Planar Y and packed FIFO and RISC enable */	return 0;}static int stop_video_dma(struct cx8800_dev    *dev){	struct cx88_core *core = dev->core;	/* stop dma */	cx_clear(MO_VID_DMACNTRL, 0x11);	/* disable capture */	cx_clear(VID_CAPTURE_CONTROL,0x06);	/* disable irqs */	cx_clear(MO_PCI_INTMSK, 0x000001);	cx_clear(MO_VID_INTMSK, 0x0f0011);	return 0;}static int restart_video_queue(struct cx8800_dev    *dev,			       struct cx88_dmaqueue *q){	struct cx88_core *core = dev->core;	struct cx88_buffer *buf, *prev;	struct list_head *item;	if (!list_empty(&q->active)) {	        buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);		dprintk(2,"restart_queue [%p/%d]: restart dma\n",			buf, buf->vb.i);		start_video_dma(dev, q, buf);		list_for_each(item,&q->active) {			buf = list_entry(item, struct cx88_buffer, vb.queue);			buf->count    = q->count++;		}		mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);		return 0;	}	prev = NULL;	for (;;) {		if (list_empty(&q->queued))			return 0;	        buf = list_entry(q->queued.next, struct cx88_buffer, vb.queue);		if (NULL == prev) {			list_del(&buf->vb.queue);			list_add_tail(&buf->vb.queue,&q->active);			start_video_dma(dev, q, buf);			buf->vb.state = STATE_ACTIVE;			buf->count    = q->count++;			mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);			dprintk(2,"[%p/%d] restart_queue - first active\n",				buf,buf->vb.i);		} else if (prev->vb.width  == buf->vb.width  &&			   prev->vb.height == buf->vb.height &&			   prev->fmt       == buf->fmt) {			list_del(&buf->vb.queue);			list_add_tail(&buf->vb.queue,&q->active);			buf->vb.state = STATE_ACTIVE;			buf->count    = q->count++;			prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);			dprintk(2,"[%p/%d] restart_queue - move to active\n",				buf,buf->vb.i);		} else {			return 0;		}		prev = buf;	}}/* ------------------------------------------------------------------ */static intbuffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size){	struct cx8800_fh *fh = q->priv_data;	*size = fh->fmt->depth*fh->width*fh->height >> 3;	if (0 == *count)		*count = 32;

⌨️ 快捷键说明

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