📄 cx88-video.c
字号:
/* * 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/kmod.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/interrupt.h>#include <asm/div64.h>#include "cx88.h"#define V4L2_I2C_CLIENTS 1MODULE_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 };MODULE_PARM(video_nr,"1-" __stringify(CX88_MAXBOARDS) "i");MODULE_PARM_DESC(video_nr,"video device numbers");static unsigned int vbi_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };MODULE_PARM(vbi_nr,"1-" __stringify(CX88_MAXBOARDS) "i");MODULE_PARM_DESC(vbi_nr,"vbi device numbers");static unsigned int radio_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };MODULE_PARM(radio_nr,"1-" __stringify(CX88_MAXBOARDS) "i");MODULE_PARM_DESC(radio_nr,"radio device numbers");static unsigned int latency = UNSET;MODULE_PARM(latency,"i");MODULE_PARM_DESC(latency,"pci latency timer");static unsigned int video_debug = 0;MODULE_PARM(video_debug,"i");MODULE_PARM_DESC(video_debug,"enable debug messages [video]");static unsigned int irq_debug = 0;MODULE_PARM(irq_debug,"i");MODULE_PARM_DESC(irq_debug,"enable debug messages [IRQ handler]");static unsigned int vid_limit = 16;MODULE_PARM(vid_limit,"i");MODULE_PARM_DESC(vid_limit,"capture memory limit in megabytes");static unsigned int tuner[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };MODULE_PARM(tuner,"1-" __stringify(CX88_MAXBOARDS) "i");MODULE_PARM_DESC(tuner,"tuner type");static unsigned int card[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };MODULE_PARM(card,"1-" __stringify(CX88_MAXBOARDS) "i");MODULE_PARM_DESC(card,"card type");static unsigned int nicam = 0;MODULE_PARM(nicam,"i");MODULE_PARM_DESC(nicam,"tv audio is nicam");#define dprintk(level,fmt, arg...) if (video_debug >= level) \ printk(KERN_DEBUG "%s: " fmt, dev->name , ## arg)/* ------------------------------------------------------------------ */static struct list_head cx8800_devlist;static unsigned int cx8800_devcount;/* ------------------------------------------------------------------- *//* static data */static unsigned int inline norm_swidth(struct cx8800_tvnorm *norm){ return (norm->id & V4L2_STD_625_50) ? 922 : 754;}static unsigned int inline norm_hdelay(struct cx8800_tvnorm *norm){ return (norm->id & V4L2_STD_625_50) ? 186 : 135;}static unsigned int inline norm_vdelay(struct cx8800_tvnorm *norm){ return (norm->id & V4L2_STD_625_50) ? 0x24 : 0x18;}static unsigned int inline norm_maxw(struct cx8800_tvnorm *norm){ return (norm->id & V4L2_STD_625_50) ? 768 : 640;// return (norm->id & V4L2_STD_625_50) ? 720 : 640;}static unsigned int inline norm_maxh(struct cx8800_tvnorm *norm){ return (norm->id & V4L2_STD_625_50) ? 576 : 480;}static unsigned int inline norm_fsc8(struct cx8800_tvnorm *norm){ static const unsigned int ntsc = 28636360; static const unsigned int pal = 35468950; return (norm->id & V4L2_STD_625_50) ? pal : ntsc;}static unsigned int inline norm_notchfilter(struct cx8800_tvnorm *norm){ return (norm->id & V4L2_STD_625_50) ? HLNotchFilter135PAL : HLNotchFilter135NTSC;}static unsigned int inline norm_htotal(struct cx8800_tvnorm *norm){ return (norm->id & V4L2_STD_625_50) ? 1135 : 910;}static unsigned int inline norm_vbipack(struct cx8800_tvnorm *norm){ return (norm->id & V4L2_STD_625_50) ? 511 : 288;}static struct cx8800_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,#if 0 },{ .name = "NTSC-4.43", .id = FIXME, .cxiformat = VideoFormatNTSC443, .cxoformat = 0x181f0008,#endif },{ .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, }, .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 = 0, .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, }};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){ if (fh->resources & bit) /* have it already allocated */ return 1; /* is it free? */ down(&dev->lock); if (dev->resources & bit) { /* no, someone else uses it */ up(&dev->lock); return 0; } /* it's free, grab it */ fh->resources |= bit; dev->resources |= bit; dprintk(1,"res: get %d\n",bit); up(&dev->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){ if ((fh->resources & bits) != bits) BUG(); down(&dev->lock); fh->resources &= ~bits; dev->resources &= ~bits; dprintk(1,"res: put %d\n",bits); up(&dev->lock);}/* ------------------------------------------------------------------ */static const u32 xtal = 28636363;static int set_pll(struct cx8800_dev *dev, int prescale, u32 ofreq){ static u32 pre[] = { 0, 0, 0, 3, 2, 1 }; u64 pll; u32 reg; int i; if (prescale < 2) prescale = 2; if (prescale > 5) prescale = 5; pll = ofreq * 8 * prescale * (u64)(1 << 20); do_div(pll,xtal); reg = (pll & 0x3ffffff) | (pre[prescale] << 26); if (((reg >> 20) & 0x3f) < 14) { printk("%s: pll out of range\n",dev->name); return -1; } dprintk(1,"set_pll: MO_PLL_REG 0x%08x [old=0x%08x,freq=%d]\n", reg, cx_read(MO_PLL_REG), ofreq); cx_write(MO_PLL_REG, reg); for (i = 0; i < 10; i++) { reg = cx_read(MO_DEVICE_STATUS); if (reg & (1<<2)) { dprintk(1,"pll locked [pre=%d,ofreq=%d]\n", prescale,ofreq); return 0; } dprintk(1,"pll not locked yet, waiting ...\n"); set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(HZ/10); } dprintk(1,"pll NOT locked [pre=%d,ofreq=%d]\n",prescale,ofreq); return -1;}static int set_tvaudio(struct cx8800_dev *dev){ if (CX88_VMUX_TELEVISION != INPUT(dev->input)->type) return 0; if (V4L2_STD_PAL_BG & dev->tvnorm->id) { dev->tvaudio = nicam ? WW_NICAM_BGDKL : WW_A2_BG; } else if (V4L2_STD_PAL_DK & dev->tvnorm->id) { dev->tvaudio = nicam ? WW_NICAM_BGDKL : WW_A2_DK; } else if (V4L2_STD_PAL_I & dev->tvnorm->id) { dev->tvaudio = WW_NICAM_I; } else if (V4L2_STD_SECAM_L & dev->tvnorm->id) { dev->tvaudio = WW_SYSTEM_L_AM; } else if (V4L2_STD_SECAM_DK & dev->tvnorm->id) { dev->tvaudio = WW_A2_DK; } else if ((V4L2_STD_NTSC_M & dev->tvnorm->id) || (V4L2_STD_PAL_M & dev->tvnorm->id)) { dev->tvaudio = WW_BTSC; } else if (V4L2_STD_NTSC_M_JP & dev->tvnorm->id) { dev->tvaudio = WW_EIAJ; } else { printk("%s: tvaudio support needs work for this tv norm [%s], sorry\n", dev->name, dev->tvnorm->name); dev->tvaudio = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -