📄 cx88-video.c
字号:
/* * * device driver for Conexant 2388x based TV cards * video4linux video interface * * (c) 2003-04 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs] * * (c) 2005-2006 Mauro Carvalho Chehab <mchehab@infradead.org> * - Multituner support * - video_ioctl2 conversion * - PAL/M fixes * * 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 <linux/dma-mapping.h>#include <linux/delay.h>#include <linux/kthread.h>#include <asm/div64.h>#include "cx88.h"#include <media/v4l2-common.h>#ifdef CONFIG_VIDEO_V4L1_COMPAT/* Include V4L1 specific functions. Should be removed soon */#include <linux/videodev.h>#endifMODULE_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 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 = 0x7f, .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 = 0x3f, .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 = 0x7f, .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 = 0x7f, .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, .default_value = 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 = 0x3f, .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);const u32 cx88_user_ctrls[] = { V4L2_CID_USER_CLASS, V4L2_CID_BRIGHTNESS, V4L2_CID_CONTRAST, V4L2_CID_SATURATION, V4L2_CID_HUE, V4L2_CID_AUDIO_VOLUME, V4L2_CID_AUDIO_BALANCE, V4L2_CID_AUDIO_MUTE, 0};EXPORT_SYMBOL(cx88_user_ctrls);static const u32 *ctrl_classes[] = { cx88_user_ctrls, NULL};int cx8800_ctrl_query(struct v4l2_queryctrl *qctrl){ int i; if (qctrl->id < V4L2_CID_BASE || qctrl->id >= V4L2_CID_LASTP1) return -EINVAL; for (i = 0; i < CX8800_CTLS; i++) if (cx8800_ctls[i].v.id == qctrl->id) break; if (i == CX8800_CTLS) { *qctrl = no_ctl; return 0; } *qctrl = cx8800_ctls[i].v; return 0;}EXPORT_SYMBOL(cx8800_ctrl_query);/* ------------------------------------------------------------------- *//* 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? */ mutex_lock(&core->lock); if (dev->resources & bit) { /* no, someone else uses it */ mutex_unlock(&core->lock); return 0; } /* it's free, grab it */ fh->resources |= bit; dev->resources |= bit; dprintk(1,"res: get %d\n",bit); mutex_unlock(&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; BUG_ON((fh->resources & bits) != bits); mutex_lock(&core->lock); fh->resources &= ~bits; dev->resources &= ~bits; dprintk(1,"res: put %d\n",bits); mutex_unlock(&core->lock);}/* ------------------------------------------------------------------ */int cx88_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; } if (core->board.mpeg & CX88_MPEG_BLACKBIRD) { /* sets sound input from external adc */ if (INPUT(input).extadc) cx_set(AUD_CTL, EN_I2SIN_ENABLE); else cx_clear(AUD_CTL, EN_I2SIN_ENABLE); } return 0;}EXPORT_SYMBOL(cx88_video_mux);/* ------------------------------------------------------------------ */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 | PCI_INT_VIDINT); /* 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;}#ifdef CONFIG_PMstatic 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, PCI_INT_VIDINT); cx_clear(MO_VID_INTMSK, 0x0f0011); return 0;}#endifstatic int restart_video_queue(struct cx8800_dev *dev, struct cx88_dmaqueue *q){ struct cx88_core *core = dev->core; struct cx88_buffer *buf, *prev; 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_entry(buf, &q->active, 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_move_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_move_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; }}/* ------------------------------------------------------------------ */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -