add idl4k kernel firmware version 1.13.0.105

This commit is contained in:
Jaroslav Kysela
2015-03-26 17:22:37 +01:00
parent 5194d2792e
commit e9070cdc77
31064 changed files with 12769984 additions and 0 deletions

View File

@@ -0,0 +1,35 @@
config VIDEO_CX231XX
tristate "Conexant cx231xx USB video capture support"
depends on VIDEO_DEV && I2C && INPUT
select VIDEO_TUNER
select VIDEO_TVEEPROM
select VIDEO_IR
select VIDEOBUF_VMALLOC
select VIDEO_CX25840
---help---
This is a video4linux driver for Conexant 231xx USB based TV cards.
To compile this driver as a module, choose M here: the
module will be called cx231xx
config VIDEO_CX231XX_ALSA
tristate "Conexant Cx231xx ALSA audio module"
depends on VIDEO_CX231XX && SND
select SND_PCM
---help---
This is an ALSA driver for Cx231xx USB based TV cards.
To compile this driver as a module, choose M here: the
module will be called cx231xx-alsa
config VIDEO_CX231XX_DVB
tristate "DVB/ATSC Support for Cx231xx based TV cards"
depends on VIDEO_CX231XX && DVB_CORE
select VIDEOBUF_DVB
select MEDIA_TUNER_XC5000 if !DVB_FE_CUSTOMISE
---help---
This adds support for DVB cards based on the
Conexant cx231xx chips.

View File

@@ -0,0 +1,14 @@
cx231xx-objs := cx231xx-video.o cx231xx-i2c.o cx231xx-cards.o cx231xx-core.o \
cx231xx-avcore.o cx231xx-pcb-cfg.o cx231xx-vbi.o
cx231xx-alsa-objs := cx231xx-audio.o
obj-$(CONFIG_VIDEO_CX231XX) += cx231xx.o
obj-$(CONFIG_VIDEO_CX231XX_ALSA) += cx231xx-alsa.o
obj-$(CONFIG_VIDEO_CX231XX_DVB) += cx231xx-dvb.o
EXTRA_CFLAGS += -Idrivers/media/video
EXTRA_CFLAGS += -Idrivers/media/common/tuners
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
EXTRA_CFLAGS += -Idrivers/media/dvb/frontends

View File

@@ -0,0 +1,586 @@
/*
* Conexant Cx231xx audio extension
*
* Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
* Based on em28xx driver
*
*
* 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/kernel.h>
#include <linux/usb.h>
#include <linux/init.h>
#include <linux/sound.h>
#include <linux/spinlock.h>
#include <linux/soundcard.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/proc_fs.h>
#include <linux/module.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/info.h>
#include <sound/initval.h>
#include <sound/control.h>
#include <media/v4l2-common.h>
#include "cx231xx.h"
static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "activates debug info");
#define dprintk(fmt, arg...) do { \
if (debug) \
printk(KERN_INFO "cx231xx-audio %s: " fmt, \
__func__, ##arg); \
} while (0)
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static int cx231xx_isoc_audio_deinit(struct cx231xx *dev)
{
int i;
dprintk("Stopping isoc\n");
for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
if (dev->adev.urb[i]) {
if (!irqs_disabled())
usb_kill_urb(dev->adev.urb[i]);
else
usb_unlink_urb(dev->adev.urb[i]);
usb_free_urb(dev->adev.urb[i]);
dev->adev.urb[i] = NULL;
kfree(dev->adev.transfer_buffer[i]);
dev->adev.transfer_buffer[i] = NULL;
}
}
return 0;
}
static void cx231xx_audio_isocirq(struct urb *urb)
{
struct cx231xx *dev = urb->context;
int i;
unsigned int oldptr;
int period_elapsed = 0;
int status;
unsigned char *cp;
unsigned int stride;
struct snd_pcm_substream *substream;
struct snd_pcm_runtime *runtime;
switch (urb->status) {
case 0: /* success */
case -ETIMEDOUT: /* NAK */
break;
case -ECONNRESET: /* kill */
case -ENOENT:
case -ESHUTDOWN:
return;
default: /* error */
dprintk("urb completition error %d.\n", urb->status);
break;
}
if (dev->adev.capture_pcm_substream) {
substream = dev->adev.capture_pcm_substream;
runtime = substream->runtime;
stride = runtime->frame_bits >> 3;
for (i = 0; i < urb->number_of_packets; i++) {
int length = urb->iso_frame_desc[i].actual_length /
stride;
cp = (unsigned char *)urb->transfer_buffer +
urb->iso_frame_desc[i].offset;
if (!length)
continue;
oldptr = dev->adev.hwptr_done_capture;
if (oldptr + length >= runtime->buffer_size) {
unsigned int cnt;
cnt = runtime->buffer_size - oldptr;
memcpy(runtime->dma_area + oldptr * stride, cp,
cnt * stride);
memcpy(runtime->dma_area, cp + cnt * stride,
length * stride - cnt * stride);
} else {
memcpy(runtime->dma_area + oldptr * stride, cp,
length * stride);
}
snd_pcm_stream_lock(substream);
dev->adev.hwptr_done_capture += length;
if (dev->adev.hwptr_done_capture >=
runtime->buffer_size)
dev->adev.hwptr_done_capture -=
runtime->buffer_size;
dev->adev.capture_transfer_done += length;
if (dev->adev.capture_transfer_done >=
runtime->period_size) {
dev->adev.capture_transfer_done -=
runtime->period_size;
period_elapsed = 1;
}
snd_pcm_stream_unlock(substream);
}
if (period_elapsed)
snd_pcm_period_elapsed(substream);
}
urb->status = 0;
status = usb_submit_urb(urb, GFP_ATOMIC);
if (status < 0) {
cx231xx_errdev("resubmit of audio urb failed (error=%i)\n",
status);
}
return;
}
static int cx231xx_init_audio_isoc(struct cx231xx *dev)
{
int i, errCode;
int sb_size;
cx231xx_info("%s: Starting AUDIO transfers\n", __func__);
sb_size = CX231XX_NUM_AUDIO_PACKETS * dev->adev.max_pkt_size;
for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
struct urb *urb;
int j, k;
dev->adev.transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC);
if (!dev->adev.transfer_buffer[i])
return -ENOMEM;
memset(dev->adev.transfer_buffer[i], 0x80, sb_size);
urb = usb_alloc_urb(CX231XX_NUM_AUDIO_PACKETS, GFP_ATOMIC);
if (!urb) {
cx231xx_errdev("usb_alloc_urb failed!\n");
for (j = 0; j < i; j++) {
usb_free_urb(dev->adev.urb[j]);
kfree(dev->adev.transfer_buffer[j]);
}
return -ENOMEM;
}
urb->dev = dev->udev;
urb->context = dev;
urb->pipe = usb_rcvisocpipe(dev->udev,
dev->adev.end_point_addr);
urb->transfer_flags = URB_ISO_ASAP;
urb->transfer_buffer = dev->adev.transfer_buffer[i];
urb->interval = 1;
urb->complete = cx231xx_audio_isocirq;
urb->number_of_packets = CX231XX_NUM_AUDIO_PACKETS;
urb->transfer_buffer_length = sb_size;
for (j = k = 0; j < CX231XX_NUM_AUDIO_PACKETS;
j++, k += dev->adev.max_pkt_size) {
urb->iso_frame_desc[j].offset = k;
urb->iso_frame_desc[j].length = dev->adev.max_pkt_size;
}
dev->adev.urb[i] = urb;
}
for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC);
if (errCode < 0) {
cx231xx_isoc_audio_deinit(dev);
return errCode;
}
}
return errCode;
}
static int cx231xx_cmd(struct cx231xx *dev, int cmd, int arg)
{
dprintk("%s transfer\n", (dev->adev.capture_stream == STREAM_ON) ?
"stop" : "start");
switch (cmd) {
case CX231XX_CAPTURE_STREAM_EN:
if (dev->adev.capture_stream == STREAM_OFF && arg == 1) {
dev->adev.capture_stream = STREAM_ON;
cx231xx_init_audio_isoc(dev);
} else if (dev->adev.capture_stream == STREAM_ON && arg == 0) {
dev->adev.capture_stream = STREAM_OFF;
cx231xx_isoc_audio_deinit(dev);
} else {
cx231xx_errdev("An underrun very likely occurred. "
"Ignoring it.\n");
}
return 0;
default:
return -EINVAL;
}
}
static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs,
size_t size)
{
struct snd_pcm_runtime *runtime = subs->runtime;
dprintk("Allocating vbuffer\n");
if (runtime->dma_area) {
if (runtime->dma_bytes > size)
return 0;
vfree(runtime->dma_area);
}
runtime->dma_area = vmalloc(size);
if (!runtime->dma_area)
return -ENOMEM;
runtime->dma_bytes = size;
return 0;
}
static struct snd_pcm_hardware snd_cx231xx_hw_capture = {
.info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT,
.rate_min = 48000,
.rate_max = 48000,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */
.period_bytes_min = 64, /* 12544/2, */
.period_bytes_max = 12544,
.periods_min = 2,
.periods_max = 98, /* 12544, */
};
static int snd_cx231xx_capture_open(struct snd_pcm_substream *substream)
{
struct cx231xx *dev = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
int ret = 0;
dprintk("opening device and trying to acquire exclusive lock\n");
if (!dev) {
cx231xx_errdev("BUG: cx231xx can't find device struct."
" Can't proceed with open\n");
return -ENODEV;
}
/* Sets volume, mute, etc */
dev->mute = 0;
/* set alternate setting for audio interface */
/* 1 - 48000 samples per sec */
ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 1);
if (ret < 0) {
cx231xx_errdev("failed to set alternate setting !\n");
return ret;
}
/* inform hardware to start streaming */
ret = cx231xx_capture_start(dev, 1, Audio);
runtime->hw = snd_cx231xx_hw_capture;
mutex_lock(&dev->lock);
dev->adev.users++;
mutex_unlock(&dev->lock);
snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
dev->adev.capture_pcm_substream = substream;
runtime->private_data = dev;
return 0;
}
static int snd_cx231xx_pcm_close(struct snd_pcm_substream *substream)
{
int ret;
struct cx231xx *dev = snd_pcm_substream_chip(substream);
dprintk("closing device\n");
/* set alternate setting for audio interface */
/* 1 - 48000 samples per sec */
ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 0);
if (ret < 0) {
cx231xx_errdev("failed to set alternate setting !\n");
return ret;
}
/* inform hardware to start streaming */
ret = cx231xx_capture_start(dev, 0, Audio);
dev->mute = 1;
mutex_lock(&dev->lock);
dev->adev.users--;
mutex_unlock(&dev->lock);
if (dev->adev.users == 0 && dev->adev.shutdown == 1) {
dprintk("audio users: %d\n", dev->adev.users);
dprintk("disabling audio stream!\n");
dev->adev.shutdown = 0;
dprintk("released lock\n");
cx231xx_cmd(dev, CX231XX_CAPTURE_STREAM_EN, 0);
}
return 0;
}
static int snd_cx231xx_hw_capture_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
unsigned int channels, rate, format;
int ret;
dprintk("Setting capture parameters\n");
ret = snd_pcm_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
format = params_format(hw_params);
rate = params_rate(hw_params);
channels = params_channels(hw_params);
/* TODO: set up cx231xx audio chip to deliver the correct audio format,
current default is 48000hz multiplexed => 96000hz mono
which shouldn't matter since analogue TV only supports mono */
return 0;
}
static int snd_cx231xx_hw_capture_free(struct snd_pcm_substream *substream)
{
struct cx231xx *dev = snd_pcm_substream_chip(substream);
dprintk("Stop capture, if needed\n");
if (dev->adev.capture_stream == STREAM_ON)
cx231xx_cmd(dev, CX231XX_CAPTURE_STREAM_EN, CX231XX_STOP_AUDIO);
return 0;
}
static int snd_cx231xx_prepare(struct snd_pcm_substream *substream)
{
return 0;
}
static int snd_cx231xx_capture_trigger(struct snd_pcm_substream *substream,
int cmd)
{
struct cx231xx *dev = snd_pcm_substream_chip(substream);
int retval;
dprintk("Should %s capture\n", (cmd == SNDRV_PCM_TRIGGER_START) ?
"start" : "stop");
spin_lock(&dev->adev.slock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
cx231xx_cmd(dev, CX231XX_CAPTURE_STREAM_EN,
CX231XX_START_AUDIO);
retval = 0;
break;
case SNDRV_PCM_TRIGGER_STOP:
cx231xx_cmd(dev, CX231XX_CAPTURE_STREAM_EN, CX231XX_STOP_AUDIO);
retval = 0;
break;
default:
retval = -EINVAL;
}
spin_unlock(&dev->adev.slock);
return retval;
}
static snd_pcm_uframes_t snd_cx231xx_capture_pointer(struct snd_pcm_substream
*substream)
{
struct cx231xx *dev;
unsigned long flags;
snd_pcm_uframes_t hwptr_done;
dev = snd_pcm_substream_chip(substream);
spin_lock_irqsave(&dev->adev.slock, flags);
hwptr_done = dev->adev.hwptr_done_capture;
spin_unlock_irqrestore(&dev->adev.slock, flags);
return hwptr_done;
}
static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs,
unsigned long offset)
{
void *pageptr = subs->runtime->dma_area + offset;
return vmalloc_to_page(pageptr);
}
static struct snd_pcm_ops snd_cx231xx_pcm_capture = {
.open = snd_cx231xx_capture_open,
.close = snd_cx231xx_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_cx231xx_hw_capture_params,
.hw_free = snd_cx231xx_hw_capture_free,
.prepare = snd_cx231xx_prepare,
.trigger = snd_cx231xx_capture_trigger,
.pointer = snd_cx231xx_capture_pointer,
.page = snd_pcm_get_vmalloc_page,
};
static int cx231xx_audio_init(struct cx231xx *dev)
{
struct cx231xx_audio *adev = &dev->adev;
struct snd_pcm *pcm;
struct snd_card *card;
static int devnr;
int err;
struct usb_interface *uif;
int i, isoc_pipe = 0;
if (dev->has_alsa_audio != 1) {
/* This device does not support the extension (in this case
the device is expecting the snd-usb-audio module or
doesn't have analog audio support at all) */
return 0;
}
cx231xx_info("cx231xx-audio.c: probing for cx231xx "
"non standard usbaudio\n");
err = snd_card_create(index[devnr], "Cx231xx Audio", THIS_MODULE,
0, &card);
if (err < 0)
return err;
spin_lock_init(&adev->slock);
err = snd_pcm_new(card, "Cx231xx Audio", 0, 0, 1, &pcm);
if (err < 0) {
snd_card_free(card);
return err;
}
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&snd_cx231xx_pcm_capture);
pcm->info_flags = 0;
pcm->private_data = dev;
strcpy(pcm->name, "Conexant cx231xx Capture");
strcpy(card->driver, "Conexant cx231xx Audio");
strcpy(card->shortname, "Cx231xx Audio");
strcpy(card->longname, "Conexant cx231xx Audio");
err = snd_card_register(card);
if (err < 0) {
snd_card_free(card);
return err;
}
adev->sndcard = card;
adev->udev = dev->udev;
/* compute alternate max packet sizes for Audio */
uif =
dev->udev->actconfig->interface[dev->current_pcb_config.
hs_config_info[0].interface_info.
audio_index + 1];
adev->end_point_addr =
le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].desc.
bEndpointAddress);
adev->num_alt = uif->num_altsetting;
cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n",
adev->end_point_addr, adev->num_alt);
adev->alt_max_pkt_size = kmalloc(32 * adev->num_alt, GFP_KERNEL);
if (adev->alt_max_pkt_size == NULL) {
cx231xx_errdev("out of memory!\n");
return -ENOMEM;
}
for (i = 0; i < adev->num_alt; i++) {
u16 tmp =
le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc.
wMaxPacketSize);
adev->alt_max_pkt_size[i] =
(tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
cx231xx_info("Alternate setting %i, max size= %i\n", i,
adev->alt_max_pkt_size[i]);
}
return 0;
}
static int cx231xx_audio_fini(struct cx231xx *dev)
{
if (dev == NULL)
return 0;
if (dev->has_alsa_audio != 1) {
/* This device does not support the extension (in this case
the device is expecting the snd-usb-audio module or
doesn't have analog audio support at all) */
return 0;
}
if (dev->adev.sndcard) {
snd_card_free(dev->adev.sndcard);
kfree(dev->adev.alt_max_pkt_size);
dev->adev.sndcard = NULL;
}
return 0;
}
static struct cx231xx_ops audio_ops = {
.id = CX231XX_AUDIO,
.name = "Cx231xx Audio Extension",
.init = cx231xx_audio_init,
.fini = cx231xx_audio_fini,
};
static int __init cx231xx_alsa_register(void)
{
return cx231xx_register_extension(&audio_ops);
}
static void __exit cx231xx_alsa_unregister(void)
{
cx231xx_unregister_extension(&audio_ops);
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Srinivasa Deevi <srinivasa.deevi@conexant.com>");
MODULE_DESCRIPTION("Cx231xx Audio driver");
module_init(cx231xx_alsa_register);
module_exit(cx231xx_alsa_unregister);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,916 @@
/*
cx231xx-cards.c - driver for Conexant Cx23100/101/102
USB video capture devices
Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
Based on em28xx driver
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/module.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/usb.h>
#include <media/tuner.h>
#include <media/tveeprom.h>
#include <media/v4l2-common.h>
#include <media/v4l2-chip-ident.h>
#include <media/cx25840.h>
#include "xc5000.h"
#include "cx231xx.h"
static int tuner = -1;
module_param(tuner, int, 0444);
MODULE_PARM_DESC(tuner, "tuner type");
static unsigned int disable_ir;
module_param(disable_ir, int, 0444);
MODULE_PARM_DESC(disable_ir, "disable infrared remote support");
/* Bitmask marking allocated devices from 0 to CX231XX_MAXBOARDS */
static unsigned long cx231xx_devused;
/*
* Reset sequences for analog/digital modes
*/
static struct cx231xx_reg_seq RDE250_XCV_TUNER[] = {
{0x03, 0x01, 10},
{0x03, 0x00, 30},
{0x03, 0x01, 10},
{-1, -1, -1},
};
/*
* Board definitions
*/
struct cx231xx_board cx231xx_boards[] = {
[CX231XX_BOARD_UNKNOWN] = {
.name = "Unknown CX231xx video grabber",
.tuner_type = TUNER_ABSENT,
.input = {{
.type = CX231XX_VMUX_TELEVISION,
.vmux = CX231XX_VIN_3_1,
.amux = CX231XX_AMUX_VIDEO,
.gpio = 0,
}, {
.type = CX231XX_VMUX_COMPOSITE1,
.vmux = CX231XX_VIN_2_1,
.amux = CX231XX_AMUX_LINE_IN,
.gpio = 0,
}, {
.type = CX231XX_VMUX_SVIDEO,
.vmux = CX231XX_VIN_1_1 |
(CX231XX_VIN_1_2 << 8) |
CX25840_SVIDEO_ON,
.amux = CX231XX_AMUX_LINE_IN,
.gpio = 0,
}
},
},
[CX231XX_BOARD_CNXT_RDE_250] = {
.name = "Conexant Hybrid TV - RDE250",
.tuner_type = TUNER_XC5000,
.tuner_addr = 0x61,
.tuner_gpio = RDE250_XCV_TUNER,
.tuner_sif_gpio = 0x05,
.tuner_scl_gpio = 0x1a,
.tuner_sda_gpio = 0x1b,
.decoder = CX231XX_AVDECODER,
.demod_xfer_mode = 0,
.ctl_pin_status_mask = 0xFFFFFFC4,
.agc_analog_digital_select_gpio = 0x0c,
.gpio_pin_status_mask = 0x4001000,
.tuner_i2c_master = 1,
.demod_i2c_master = 2,
.has_dvb = 1,
.demod_addr = 0x02,
.norm = V4L2_STD_PAL,
.input = {{
.type = CX231XX_VMUX_TELEVISION,
.vmux = CX231XX_VIN_3_1,
.amux = CX231XX_AMUX_VIDEO,
.gpio = 0,
}, {
.type = CX231XX_VMUX_COMPOSITE1,
.vmux = CX231XX_VIN_2_1,
.amux = CX231XX_AMUX_LINE_IN,
.gpio = 0,
}, {
.type = CX231XX_VMUX_SVIDEO,
.vmux = CX231XX_VIN_1_1 |
(CX231XX_VIN_1_2 << 8) |
CX25840_SVIDEO_ON,
.amux = CX231XX_AMUX_LINE_IN,
.gpio = 0,
}
},
},
[CX231XX_BOARD_CNXT_RDU_250] = {
.name = "Conexant Hybrid TV - RDU250",
.tuner_type = TUNER_XC5000,
.tuner_addr = 0x61,
.tuner_gpio = RDE250_XCV_TUNER,
.tuner_sif_gpio = 0x05,
.tuner_scl_gpio = 0x1a,
.tuner_sda_gpio = 0x1b,
.decoder = CX231XX_AVDECODER,
.demod_xfer_mode = 0,
.ctl_pin_status_mask = 0xFFFFFFC4,
.agc_analog_digital_select_gpio = 0x0c,
.gpio_pin_status_mask = 0x4001000,
.tuner_i2c_master = 1,
.demod_i2c_master = 2,
.has_dvb = 1,
.demod_addr = 0x32,
.norm = V4L2_STD_NTSC,
.input = {{
.type = CX231XX_VMUX_TELEVISION,
.vmux = CX231XX_VIN_3_1,
.amux = CX231XX_AMUX_VIDEO,
.gpio = 0,
}, {
.type = CX231XX_VMUX_COMPOSITE1,
.vmux = CX231XX_VIN_2_1,
.amux = CX231XX_AMUX_LINE_IN,
.gpio = 0,
}, {
.type = CX231XX_VMUX_SVIDEO,
.vmux = CX231XX_VIN_1_1 |
(CX231XX_VIN_1_2 << 8) |
CX25840_SVIDEO_ON,
.amux = CX231XX_AMUX_LINE_IN,
.gpio = 0,
}
},
},
};
const unsigned int cx231xx_bcount = ARRAY_SIZE(cx231xx_boards);
/* table of devices that work with this driver */
struct usb_device_id cx231xx_id_table[] = {
{USB_DEVICE(0x0572, 0x5A3C),
.driver_info = CX231XX_BOARD_UNKNOWN},
{USB_DEVICE(0x0572, 0x58A2),
.driver_info = CX231XX_BOARD_CNXT_RDE_250},
{USB_DEVICE(0x0572, 0x58A1),
.driver_info = CX231XX_BOARD_CNXT_RDU_250},
{},
};
MODULE_DEVICE_TABLE(usb, cx231xx_id_table);
/* cx231xx_tuner_callback
* will be used to reset XC5000 tuner using GPIO pin
*/
int cx231xx_tuner_callback(void *ptr, int component, int command, int arg)
{
int rc = 0;
struct cx231xx *dev = ptr;
if (dev->tuner_type == TUNER_XC5000) {
if (command == XC5000_TUNER_RESET) {
cx231xx_info
("Tuner CB: RESET: cmd %d : tuner type %d \n",
command, dev->tuner_type);
cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit,
1);
msleep(10);
cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit,
0);
msleep(330);
cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit,
1);
msleep(10);
}
}
return rc;
}
EXPORT_SYMBOL_GPL(cx231xx_tuner_callback);
static inline void cx231xx_set_model(struct cx231xx *dev)
{
memcpy(&dev->board, &cx231xx_boards[dev->model], sizeof(dev->board));
}
/* Since cx231xx_pre_card_setup() requires a proper dev->model,
* this won't work for boards with generic PCI IDs
*/
void cx231xx_pre_card_setup(struct cx231xx *dev)
{
cx231xx_set_model(dev);
cx231xx_info("Identified as %s (card=%d)\n",
dev->board.name, dev->model);
/* set the direction for GPIO pins */
if (dev->board.tuner_gpio) {
cx231xx_set_gpio_direction(dev, dev->board.tuner_gpio->bit, 1);
cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit, 1);
cx231xx_set_gpio_direction(dev, dev->board.tuner_sif_gpio, 1);
/* request some modules if any required */
/* reset the Tuner */
cx231xx_gpio_set(dev, dev->board.tuner_gpio);
}
/* set the mode to Analog mode initially */
cx231xx_set_mode(dev, CX231XX_ANALOG_MODE);
/* Unlock device */
/* cx231xx_set_mode(dev, CX231XX_SUSPEND); */
}
static void cx231xx_config_tuner(struct cx231xx *dev)
{
struct tuner_setup tun_setup;
struct v4l2_frequency f;
if (dev->tuner_type == TUNER_ABSENT)
return;
tun_setup.mode_mask = T_ANALOG_TV | T_RADIO;
tun_setup.type = dev->tuner_type;
tun_setup.addr = dev->tuner_addr;
tun_setup.tuner_callback = cx231xx_tuner_callback;
tuner_call(dev, tuner, s_type_addr, &tun_setup);
#if 0
if (tun_setup.type == TUNER_XC5000) {
static struct xc2028_ctrl ctrl = {
.fname = XC5000_DEFAULT_FIRMWARE,
.max_len = 64,
.demod = 0;
};
struct v4l2_priv_tun_config cfg = {
.tuner = dev->tuner_type,
.priv = &ctrl,
};
tuner_call(dev, tuner, s_config, &cfg);
}
#endif
/* configure tuner */
f.tuner = 0;
f.type = V4L2_TUNER_ANALOG_TV;
f.frequency = 9076; /* just a magic number */
dev->ctl_freq = f.frequency;
call_all(dev, tuner, s_frequency, &f);
}
/* ----------------------------------------------------------------------- */
void cx231xx_register_i2c_ir(struct cx231xx *dev)
{
if (disable_ir)
return;
/* REVISIT: instantiate IR device */
/* detect & configure */
switch (dev->model) {
case CX231XX_BOARD_CNXT_RDE_250:
break;
case CX231XX_BOARD_CNXT_RDU_250:
break;
default:
break;
}
}
void cx231xx_card_setup(struct cx231xx *dev)
{
cx231xx_set_model(dev);
dev->tuner_type = cx231xx_boards[dev->model].tuner_type;
if (cx231xx_boards[dev->model].tuner_addr)
dev->tuner_addr = cx231xx_boards[dev->model].tuner_addr;
/* request some modules */
if (dev->board.decoder == CX231XX_AVDECODER) {
dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev,
&dev->i2c_bus[0].i2c_adap,
"cx25840", "cx25840", 0x88 >> 1, NULL);
if (dev->sd_cx25840 == NULL)
cx231xx_info("cx25840 subdev registration failure\n");
cx25840_call(dev, core, load_fw);
}
if (dev->board.tuner_type != TUNER_ABSENT) {
dev->sd_tuner = v4l2_i2c_new_subdev(&dev->v4l2_dev,
&dev->i2c_bus[1].i2c_adap,
"tuner", "tuner", 0xc2 >> 1, NULL);
if (dev->sd_tuner == NULL)
cx231xx_info("tuner subdev registration failure\n");
cx231xx_config_tuner(dev);
}
cx231xx_config_tuner(dev);
#if 0
/* TBD IR will be added later */
cx231xx_ir_init(dev);
#endif
}
/*
* cx231xx_config()
* inits registers with sane defaults
*/
int cx231xx_config(struct cx231xx *dev)
{
/* TBD need to add cx231xx specific code */
dev->mute = 1; /* maybe not the right place... */
dev->volume = 0x1f;
return 0;
}
/*
* cx231xx_config_i2c()
* configure i2c attached devices
*/
void cx231xx_config_i2c(struct cx231xx *dev)
{
/* u32 input = INPUT(dev->video_input)->vmux; */
call_all(dev, video, s_stream, 1);
}
/*
* cx231xx_realease_resources()
* unregisters the v4l2,i2c and usb devices
* called when the device gets disconected or at module unload
*/
void cx231xx_release_resources(struct cx231xx *dev)
{
#if 0 /* TBD IR related */
if (dev->ir)
cx231xx_ir_fini(dev);
#endif
cx231xx_release_analog_resources(dev);
cx231xx_remove_from_devlist(dev);
cx231xx_dev_uninit(dev);
usb_put_dev(dev->udev);
/* Mark device as unused */
cx231xx_devused &= ~(1 << dev->devno);
}
/*
* cx231xx_init_dev()
* allocates and inits the device structs, registers i2c bus and v4l device
*/
static int cx231xx_init_dev(struct cx231xx **devhandle, struct usb_device *udev,
int minor)
{
struct cx231xx *dev = *devhandle;
int retval = -ENOMEM;
int errCode;
unsigned int maxh, maxw;
dev->udev = udev;
mutex_init(&dev->lock);
mutex_init(&dev->ctrl_urb_lock);
mutex_init(&dev->gpio_i2c_lock);
spin_lock_init(&dev->video_mode.slock);
spin_lock_init(&dev->vbi_mode.slock);
spin_lock_init(&dev->sliced_cc_mode.slock);
init_waitqueue_head(&dev->open);
init_waitqueue_head(&dev->wait_frame);
init_waitqueue_head(&dev->wait_stream);
dev->cx231xx_read_ctrl_reg = cx231xx_read_ctrl_reg;
dev->cx231xx_write_ctrl_reg = cx231xx_write_ctrl_reg;
dev->cx231xx_send_usb_command = cx231xx_send_usb_command;
dev->cx231xx_gpio_i2c_read = cx231xx_gpio_i2c_read;
dev->cx231xx_gpio_i2c_write = cx231xx_gpio_i2c_write;
/* Query cx231xx to find what pcb config it is related to */
initialize_cx231xx(dev);
/* Cx231xx pre card setup */
cx231xx_pre_card_setup(dev);
errCode = cx231xx_config(dev);
if (errCode) {
cx231xx_errdev("error configuring device\n");
return -ENOMEM;
}
/* set default norm */
dev->norm = dev->board.norm;
/* register i2c bus */
errCode = cx231xx_dev_init(dev);
if (errCode < 0) {
cx231xx_errdev("%s: cx231xx_i2c_register - errCode [%d]!\n",
__func__, errCode);
return errCode;
}
/* Do board specific init */
cx231xx_card_setup(dev);
/* configure the device */
cx231xx_config_i2c(dev);
maxw = norm_maxw(dev);
maxh = norm_maxh(dev);
/* set default image size */
dev->width = maxw;
dev->height = maxh;
dev->interlaced = 0;
dev->hscale = 0;
dev->vscale = 0;
dev->video_input = 0;
errCode = cx231xx_config(dev);
if (errCode < 0) {
cx231xx_errdev("%s: cx231xx_config - errCode [%d]!\n",
__func__, errCode);
return errCode;
}
/* init video dma queues */
INIT_LIST_HEAD(&dev->video_mode.vidq.active);
INIT_LIST_HEAD(&dev->video_mode.vidq.queued);
/* init vbi dma queues */
INIT_LIST_HEAD(&dev->vbi_mode.vidq.active);
INIT_LIST_HEAD(&dev->vbi_mode.vidq.queued);
/* Reset other chips required if they are tied up with GPIO pins */
cx231xx_add_into_devlist(dev);
retval = cx231xx_register_analog_devices(dev);
if (retval < 0) {
cx231xx_release_resources(dev);
goto fail_reg_devices;
}
cx231xx_init_extension(dev);
return 0;
fail_reg_devices:
mutex_unlock(&dev->lock);
return retval;
}
#if defined(CONFIG_MODULES) && defined(MODULE)
static void request_module_async(struct work_struct *work)
{
struct cx231xx *dev = container_of(work,
struct cx231xx, request_module_wk);
if (dev->has_alsa_audio)
request_module("cx231xx-alsa");
if (dev->board.has_dvb)
request_module("cx231xx-dvb");
}
static void request_modules(struct cx231xx *dev)
{
INIT_WORK(&dev->request_module_wk, request_module_async);
schedule_work(&dev->request_module_wk);
}
#else
#define request_modules(dev)
#endif /* CONFIG_MODULES */
/*
* cx231xx_usb_probe()
* checks for supported devices
*/
static int cx231xx_usb_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
struct usb_device *udev;
struct usb_interface *uif;
struct cx231xx *dev = NULL;
int retval = -ENODEV;
int nr = 0, ifnum;
int i, isoc_pipe = 0;
char *speed;
char descr[255] = "";
struct usb_interface *lif = NULL;
int skip_interface = 0;
struct usb_interface_assoc_descriptor *assoc_desc;
udev = usb_get_dev(interface_to_usbdev(interface));
ifnum = interface->altsetting[0].desc.bInterfaceNumber;
if (!ifnum) {
/*
* Interface number 0 - IR interface
*/
/* Check to see next free device and mark as used */
nr = find_first_zero_bit(&cx231xx_devused, CX231XX_MAXBOARDS);
cx231xx_devused |= 1 << nr;
if (nr >= CX231XX_MAXBOARDS) {
cx231xx_err(DRIVER_NAME ": Supports only %i cx231xx boards.\n",
CX231XX_MAXBOARDS);
cx231xx_devused &= ~(1 << nr);
return -ENOMEM;
}
/* allocate memory for our device state and initialize it */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (dev == NULL) {
cx231xx_err(DRIVER_NAME ": out of memory!\n");
cx231xx_devused &= ~(1 << nr);
return -ENOMEM;
}
snprintf(dev->name, 29, "cx231xx #%d", nr);
dev->devno = nr;
dev->model = id->driver_info;
dev->video_mode.alt = -1;
dev->interface_count++;
/* reset gpio dir and value */
dev->gpio_dir = 0;
dev->gpio_val = 0;
dev->xc_fw_load_done = 0;
dev->has_alsa_audio = 1;
dev->power_mode = -1;
/* 0 - vbi ; 1 -sliced cc mode */
dev->vbi_or_sliced_cc_mode = 0;
/* get maximum no.of IAD interfaces */
assoc_desc = udev->actconfig->intf_assoc[0];
dev->max_iad_interface_count = assoc_desc->bInterfaceCount;
/* init CIR module TBD */
/* store the current interface */
lif = interface;
switch (udev->speed) {
case USB_SPEED_LOW:
speed = "1.5";
break;
case USB_SPEED_UNKNOWN:
case USB_SPEED_FULL:
speed = "12";
break;
case USB_SPEED_HIGH:
speed = "480";
break;
default:
speed = "unknown";
}
if (udev->manufacturer)
strlcpy(descr, udev->manufacturer, sizeof(descr));
if (udev->product) {
if (*descr)
strlcat(descr, " ", sizeof(descr));
strlcat(descr, udev->product, sizeof(descr));
}
if (*descr)
strlcat(descr, " ", sizeof(descr));
cx231xx_info("New device %s@ %s Mbps "
"(%04x:%04x) with %d interfaces\n",
descr,
speed,
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct),
dev->max_iad_interface_count);
} else {
/* Get dev structure first */
dev = usb_get_intfdata(udev->actconfig->interface[0]);
if (dev == NULL) {
cx231xx_err(DRIVER_NAME ": out of first interface!\n");
return -ENODEV;
}
/* store the interface 0 back */
lif = udev->actconfig->interface[0];
/* increment interface count */
dev->interface_count++;
/* get device number */
nr = dev->devno;
/*
* set skip interface, for all interfaces but
* interface 1 and the last one
*/
if ((ifnum != 1) && ((dev->interface_count - 1)
!= dev->max_iad_interface_count))
skip_interface = 1;
if (ifnum == 1) {
assoc_desc = udev->actconfig->intf_assoc[0];
if (assoc_desc->bFirstInterface != ifnum) {
cx231xx_err(DRIVER_NAME ": Not found "
"matching IAD interface\n");
return -ENODEV;
}
}
}
if (skip_interface)
return -ENODEV;
cx231xx_info("registering interface %d\n", ifnum);
/* save our data pointer in this interface device */
usb_set_intfdata(lif, dev);
if ((dev->interface_count - 1) != dev->max_iad_interface_count)
return 0;
/*
* AV device initialization - only done at the last interface
*/
/* Create v4l2 device */
retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev);
if (retval) {
cx231xx_errdev("v4l2_device_register failed\n");
cx231xx_devused &= ~(1 << nr);
kfree(dev);
return -EIO;
}
/* allocate device struct */
retval = cx231xx_init_dev(&dev, udev, nr);
if (retval) {
cx231xx_devused &= ~(1 << dev->devno);
v4l2_device_unregister(&dev->v4l2_dev);
kfree(dev);
return retval;
}
/* compute alternate max packet sizes for video */
uif = udev->actconfig->interface[dev->current_pcb_config.
hs_config_info[0].interface_info.video_index + 1];
dev->video_mode.end_point_addr = le16_to_cpu(uif->altsetting[0].
endpoint[isoc_pipe].desc.bEndpointAddress);
dev->video_mode.num_alt = uif->num_altsetting;
cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n",
dev->video_mode.end_point_addr,
dev->video_mode.num_alt);
dev->video_mode.alt_max_pkt_size =
kmalloc(32 * dev->video_mode.num_alt, GFP_KERNEL);
if (dev->video_mode.alt_max_pkt_size == NULL) {
cx231xx_errdev("out of memory!\n");
cx231xx_devused &= ~(1 << nr);
v4l2_device_unregister(&dev->v4l2_dev);
kfree(dev);
return -ENOMEM;
}
for (i = 0; i < dev->video_mode.num_alt; i++) {
u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].
desc.wMaxPacketSize);
dev->video_mode.alt_max_pkt_size[i] =
(tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
cx231xx_info("Alternate setting %i, max size= %i\n", i,
dev->video_mode.alt_max_pkt_size[i]);
}
/* compute alternate max packet sizes for vbi */
uif = udev->actconfig->interface[dev->current_pcb_config.
hs_config_info[0].interface_info.
vanc_index + 1];
dev->vbi_mode.end_point_addr =
le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].desc.
bEndpointAddress);
dev->vbi_mode.num_alt = uif->num_altsetting;
cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n",
dev->vbi_mode.end_point_addr,
dev->vbi_mode.num_alt);
dev->vbi_mode.alt_max_pkt_size =
kmalloc(32 * dev->vbi_mode.num_alt, GFP_KERNEL);
if (dev->vbi_mode.alt_max_pkt_size == NULL) {
cx231xx_errdev("out of memory!\n");
cx231xx_devused &= ~(1 << nr);
v4l2_device_unregister(&dev->v4l2_dev);
kfree(dev);
return -ENOMEM;
}
for (i = 0; i < dev->vbi_mode.num_alt; i++) {
u16 tmp =
le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].
desc.wMaxPacketSize);
dev->vbi_mode.alt_max_pkt_size[i] =
(tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
cx231xx_info("Alternate setting %i, max size= %i\n", i,
dev->vbi_mode.alt_max_pkt_size[i]);
}
/* compute alternate max packet sizes for sliced CC */
uif = udev->actconfig->interface[dev->current_pcb_config.
hs_config_info[0].interface_info.
hanc_index + 1];
dev->sliced_cc_mode.end_point_addr =
le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].desc.
bEndpointAddress);
dev->sliced_cc_mode.num_alt = uif->num_altsetting;
cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n",
dev->sliced_cc_mode.end_point_addr,
dev->sliced_cc_mode.num_alt);
dev->sliced_cc_mode.alt_max_pkt_size =
kmalloc(32 * dev->sliced_cc_mode.num_alt, GFP_KERNEL);
if (dev->sliced_cc_mode.alt_max_pkt_size == NULL) {
cx231xx_errdev("out of memory!\n");
cx231xx_devused &= ~(1 << nr);
v4l2_device_unregister(&dev->v4l2_dev);
kfree(dev);
return -ENOMEM;
}
for (i = 0; i < dev->sliced_cc_mode.num_alt; i++) {
u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].
desc.wMaxPacketSize);
dev->sliced_cc_mode.alt_max_pkt_size[i] =
(tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
cx231xx_info("Alternate setting %i, max size= %i\n", i,
dev->sliced_cc_mode.alt_max_pkt_size[i]);
}
if (dev->current_pcb_config.ts1_source != 0xff) {
/* compute alternate max packet sizes for TS1 */
uif = udev->actconfig->interface[dev->current_pcb_config.
hs_config_info[0].
interface_info.
ts1_index + 1];
dev->ts1_mode.end_point_addr =
le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].
desc.bEndpointAddress);
dev->ts1_mode.num_alt = uif->num_altsetting;
cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n",
dev->ts1_mode.end_point_addr,
dev->ts1_mode.num_alt);
dev->ts1_mode.alt_max_pkt_size =
kmalloc(32 * dev->ts1_mode.num_alt, GFP_KERNEL);
if (dev->ts1_mode.alt_max_pkt_size == NULL) {
cx231xx_errdev("out of memory!\n");
cx231xx_devused &= ~(1 << nr);
v4l2_device_unregister(&dev->v4l2_dev);
kfree(dev);
return -ENOMEM;
}
for (i = 0; i < dev->ts1_mode.num_alt; i++) {
u16 tmp = le16_to_cpu(uif->altsetting[i].
endpoint[isoc_pipe].desc.
wMaxPacketSize);
dev->ts1_mode.alt_max_pkt_size[i] =
(tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
cx231xx_info("Alternate setting %i, max size= %i\n", i,
dev->ts1_mode.alt_max_pkt_size[i]);
}
}
/* load other modules required */
request_modules(dev);
return 0;
}
/*
* cx231xx_usb_disconnect()
* called when the device gets diconencted
* video device will be unregistered on v4l2_close in case it is still open
*/
static void cx231xx_usb_disconnect(struct usb_interface *interface)
{
struct cx231xx *dev;
dev = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL);
if (!dev)
return;
if (!dev->udev)
return;
/* delete v4l2 device */
v4l2_device_unregister(&dev->v4l2_dev);
/* wait until all current v4l2 io is finished then deallocate
resources */
mutex_lock(&dev->lock);
wake_up_interruptible_all(&dev->open);
if (dev->users) {
cx231xx_warn
("device /dev/video%d is open! Deregistration and memory "
"deallocation are deferred on close.\n", dev->vdev->num);
dev->state |= DEV_MISCONFIGURED;
cx231xx_uninit_isoc(dev);
dev->state |= DEV_DISCONNECTED;
wake_up_interruptible(&dev->wait_frame);
wake_up_interruptible(&dev->wait_stream);
} else {
dev->state |= DEV_DISCONNECTED;
cx231xx_release_resources(dev);
}
cx231xx_close_extension(dev);
mutex_unlock(&dev->lock);
if (!dev->users) {
kfree(dev->video_mode.alt_max_pkt_size);
kfree(dev->vbi_mode.alt_max_pkt_size);
kfree(dev->sliced_cc_mode.alt_max_pkt_size);
kfree(dev->ts1_mode.alt_max_pkt_size);
kfree(dev);
}
}
static struct usb_driver cx231xx_usb_driver = {
.name = "cx231xx",
.probe = cx231xx_usb_probe,
.disconnect = cx231xx_usb_disconnect,
.id_table = cx231xx_id_table,
};
static int __init cx231xx_module_init(void)
{
int result;
printk(KERN_INFO DRIVER_NAME " v4l2 driver loaded.\n");
/* register this driver with the USB subsystem */
result = usb_register(&cx231xx_usb_driver);
if (result)
cx231xx_err(DRIVER_NAME
" usb_register failed. Error number %d.\n", result);
return result;
}
static void __exit cx231xx_module_exit(void)
{
/* deregister this driver with the USB subsystem */
usb_deregister(&cx231xx_usb_driver);
}
module_init(cx231xx_module_init);
module_exit(cx231xx_module_exit);

View File

@@ -0,0 +1,494 @@
/*
cx231xx_conf-reg.h - driver for Conexant Cx23100/101/102 USB
video capture devices
Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
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.
*/
#ifndef _POLARIS_REG_H_
#define _POLARIS_REG_H_
#define BOARD_CFG_STAT 0x0
#define TS_MODE_REG 0x4
#define TS1_CFG_REG 0x8
#define TS1_LENGTH_REG 0xc
#define TS2_CFG_REG 0x10
#define TS2_LENGTH_REG 0x14
#define EP_MODE_SET 0x18
#define CIR_PWR_PTN1 0x1c
#define CIR_PWR_PTN2 0x20
#define CIR_PWR_PTN3 0x24
#define CIR_PWR_MASK0 0x28
#define CIR_PWR_MASK1 0x2c
#define CIR_PWR_MASK2 0x30
#define CIR_GAIN 0x34
#define CIR_CAR_REG 0x38
#define CIR_OT_CFG1 0x40
#define CIR_OT_CFG2 0x44
#define PWR_CTL_EN 0x74
/* Polaris Endpoints capture mask for register EP_MODE_SET */
#define ENABLE_EP1 0x01 /* Bit[0]=1 */
#define ENABLE_EP2 0x02 /* Bit[1]=1 */
#define ENABLE_EP3 0x04 /* Bit[2]=1 */
#define ENABLE_EP4 0x08 /* Bit[3]=1 */
#define ENABLE_EP5 0x10 /* Bit[4]=1 */
#define ENABLE_EP6 0x20 /* Bit[5]=1 */
/* Bit definition for register PWR_CTL_EN */
#define PWR_MODE_MASK 0x17f
#define PWR_AV_EN 0x08 /* bit3 */
#define PWR_ISO_EN 0x40 /* bit6 */
#define PWR_AV_MODE 0x30 /* bit4,5 */
#define PWR_TUNER_EN 0x04 /* bit2 */
#define PWR_DEMOD_EN 0x02 /* bit1 */
#define I2C_DEMOD_EN 0x01 /* bit0 */
#define PWR_RESETOUT_EN 0x100 /* bit8 */
enum AV_MODE{
POLARIS_AVMODE_DEFAULT = 0,
POLARIS_AVMODE_DIGITAL = 0x10,
POLARIS_AVMODE_ANALOGT_TV = 0x20,
POLARIS_AVMODE_ENXTERNAL_AV = 0x30,
};
/* Colibri Registers */
#define SINGLE_ENDED 0x0
#define LOW_IF 0x4
#define EU_IF 0x9
#define US_IF 0xa
#define SUP_BLK_TUNE1 0x00
#define SUP_BLK_TUNE2 0x01
#define SUP_BLK_TUNE3 0x02
#define SUP_BLK_XTAL 0x03
#define SUP_BLK_PLL1 0x04
#define SUP_BLK_PLL2 0x05
#define SUP_BLK_PLL3 0x06
#define SUP_BLK_REF 0x07
#define SUP_BLK_PWRDN 0x08
#define SUP_BLK_TESTPAD 0x09
#define ADC_COM_INT5_STAB_REF 0x0a
#define ADC_COM_QUANT 0x0b
#define ADC_COM_BIAS1 0x0c
#define ADC_COM_BIAS2 0x0d
#define ADC_COM_BIAS3 0x0e
#define TESTBUS_CTRL 0x12
#define FLD_PWRDN_TUNING_BIAS 0x10
#define FLD_PWRDN_ENABLE_PLL 0x08
#define FLD_PWRDN_PD_BANDGAP 0x04
#define FLD_PWRDN_PD_BIAS 0x02
#define FLD_PWRDN_PD_TUNECK 0x01
#define ADC_STATUS_CH1 0x20
#define ADC_STATUS_CH2 0x40
#define ADC_STATUS_CH3 0x60
#define ADC_STATUS2_CH1 0x21
#define ADC_STATUS2_CH2 0x41
#define ADC_STATUS2_CH3 0x61
#define ADC_CAL_ATEST_CH1 0x22
#define ADC_CAL_ATEST_CH2 0x42
#define ADC_CAL_ATEST_CH3 0x62
#define ADC_PWRDN_CLAMP_CH1 0x23
#define ADC_PWRDN_CLAMP_CH2 0x43
#define ADC_PWRDN_CLAMP_CH3 0x63
#define ADC_CTRL_DAC23_CH1 0x24
#define ADC_CTRL_DAC23_CH2 0x44
#define ADC_CTRL_DAC23_CH3 0x64
#define ADC_CTRL_DAC1_CH1 0x25
#define ADC_CTRL_DAC1_CH2 0x45
#define ADC_CTRL_DAC1_CH3 0x65
#define ADC_DCSERVO_DEM_CH1 0x26
#define ADC_DCSERVO_DEM_CH2 0x46
#define ADC_DCSERVO_DEM_CH3 0x66
#define ADC_FB_FRCRST_CH1 0x27
#define ADC_FB_FRCRST_CH2 0x47
#define ADC_FB_FRCRST_CH3 0x67
#define ADC_INPUT_CH1 0x28
#define ADC_INPUT_CH2 0x48
#define ADC_INPUT_CH3 0x68
#define INPUT_SEL_MASK 0x30 /* [5:4] in_sel */
#define ADC_NTF_PRECLMP_EN_CH1 0x29
#define ADC_NTF_PRECLMP_EN_CH2 0x49
#define ADC_NTF_PRECLMP_EN_CH3 0x69
#define ADC_QGAIN_RES_TRM_CH1 0x2a
#define ADC_QGAIN_RES_TRM_CH2 0x4a
#define ADC_QGAIN_RES_TRM_CH3 0x6a
#define ADC_SOC_PRECLMP_TERM_CH1 0x2b
#define ADC_SOC_PRECLMP_TERM_CH2 0x4b
#define ADC_SOC_PRECLMP_TERM_CH3 0x6b
#define TESTBUS_CTRL_CH1 0x32
#define TESTBUS_CTRL_CH2 0x52
#define TESTBUS_CTRL_CH3 0x72
/******************************************************************************
* DIF registers *
******************************************************************************/
#define DIRECT_IF_REVB_BASE 0x00300
/*****************************************************************************/
#define DIF_PLL_FREQ_WORD (DIRECT_IF_REVB_BASE + 0x00000000)
/*****************************************************************************/
#define FLD_DIF_PLL_LOCK 0x80000000
/* Reserved [30:29] */
#define FLD_DIF_PLL_FREE_RUN 0x10000000
#define FLD_DIF_PLL_FREQ 0x0fffffff
/*****************************************************************************/
#define DIF_PLL_CTRL (DIRECT_IF_REVB_BASE + 0x00000004)
/*****************************************************************************/
#define FLD_DIF_KD_PD 0xff000000
/* Reserved [23:20] */
#define FLD_DIF_KDS_PD 0x000f0000
#define FLD_DIF_KI_PD 0x0000ff00
/* Reserved [7:4] */
#define FLD_DIF_KIS_PD 0x0000000f
/*****************************************************************************/
#define DIF_PLL_CTRL1 (DIRECT_IF_REVB_BASE + 0x00000008)
/*****************************************************************************/
#define FLD_DIF_KD_FD 0xff000000
/* Reserved [23:20] */
#define FLD_DIF_KDS_FD 0x000f0000
#define FLD_DIF_KI_FD 0x0000ff00
#define FLD_DIF_SIG_PROP_SZ 0x000000f0
#define FLD_DIF_KIS_FD 0x0000000f
/*****************************************************************************/
#define DIF_PLL_CTRL2 (DIRECT_IF_REVB_BASE + 0x0000000c)
/*****************************************************************************/
#define FLD_DIF_PLL_AGC_REF 0xfff00000
#define FLD_DIF_PLL_AGC_KI 0x000f0000
/* Reserved [15] */
#define FLD_DIF_FREQ_LIMIT 0x00007000
#define FLD_DIF_K_FD 0x00000f00
#define FLD_DIF_DOWNSMPL_FD 0x000000ff
/*****************************************************************************/
#define DIF_PLL_CTRL3 (DIRECT_IF_REVB_BASE + 0x00000010)
/*****************************************************************************/
/* Reserved [31:16] */
#define FLD_DIF_PLL_AGC_EN 0x00008000
/* Reserved [14:12] */
#define FLD_DIF_PLL_MAN_GAIN 0x00000fff
/*****************************************************************************/
#define DIF_AGC_IF_REF (DIRECT_IF_REVB_BASE + 0x00000014)
/*****************************************************************************/
#define FLD_DIF_K_AGC_RF 0xf0000000
#define FLD_DIF_K_AGC_IF 0x0f000000
#define FLD_DIF_K_AGC_INT 0x00f00000
/* Reserved [19:12] */
#define FLD_DIF_IF_REF 0x00000fff
/*****************************************************************************/
#define DIF_AGC_CTRL_IF (DIRECT_IF_REVB_BASE + 0x00000018)
/*****************************************************************************/
#define FLD_DIF_IF_MAX 0xff000000
#define FLD_DIF_IF_MIN 0x00ff0000
#define FLD_DIF_IF_AGC 0x0000ffff
/*****************************************************************************/
#define DIF_AGC_CTRL_INT (DIRECT_IF_REVB_BASE + 0x0000001c)
/*****************************************************************************/
#define FLD_DIF_INT_MAX 0xff000000
#define FLD_DIF_INT_MIN 0x00ff0000
#define FLD_DIF_INT_AGC 0x0000ffff
/*****************************************************************************/
#define DIF_AGC_CTRL_RF (DIRECT_IF_REVB_BASE + 0x00000020)
/*****************************************************************************/
#define FLD_DIF_RF_MAX 0xff000000
#define FLD_DIF_RF_MIN 0x00ff0000
#define FLD_DIF_RF_AGC 0x0000ffff
/*****************************************************************************/
#define DIF_AGC_IF_INT_CURRENT (DIRECT_IF_REVB_BASE + 0x00000024)
/*****************************************************************************/
#define FLD_DIF_IF_AGC_IN 0xffff0000
#define FLD_DIF_INT_AGC_IN 0x0000ffff
/*****************************************************************************/
#define DIF_AGC_RF_CURRENT (DIRECT_IF_REVB_BASE + 0x00000028)
/*****************************************************************************/
/* Reserved [31:16] */
#define FLD_DIF_RF_AGC_IN 0x0000ffff
/*****************************************************************************/
#define DIF_VIDEO_AGC_CTRL (DIRECT_IF_REVB_BASE + 0x0000002c)
/*****************************************************************************/
#define FLD_DIF_AFD 0xc0000000
#define FLD_DIF_K_VID_AGC 0x30000000
#define FLD_DIF_LINE_LENGTH 0x0fff0000
#define FLD_DIF_AGC_GAIN 0x0000ffff
/*****************************************************************************/
#define DIF_VID_AUD_OVERRIDE (DIRECT_IF_REVB_BASE + 0x00000030)
/*****************************************************************************/
#define FLD_DIF_AUDIO_AGC_OVERRIDE 0x80000000
/* Reserved [30:30] */
#define FLD_DIF_AUDIO_MAN_GAIN 0x3f000000
/* Reserved [23:17] */
#define FLD_DIF_VID_AGC_OVERRIDE 0x00010000
#define FLD_DIF_VID_MAN_GAIN 0x0000ffff
/*****************************************************************************/
#define DIF_AV_SEP_CTRL (DIRECT_IF_REVB_BASE + 0x00000034)
/*****************************************************************************/
#define FLD_DIF_LPF_FREQ 0xc0000000
#define FLD_DIF_AV_PHASE_INC 0x3f000000
#define FLD_DIF_AUDIO_FREQ 0x00ffffff
/*****************************************************************************/
#define DIF_COMP_FLT_CTRL (DIRECT_IF_REVB_BASE + 0x00000038)
/*****************************************************************************/
/* Reserved [31:24] */
#define FLD_DIF_IIR23_R2 0x00ff0000
#define FLD_DIF_IIR23_R1 0x0000ff00
#define FLD_DIF_IIR1_R1 0x000000ff
/*****************************************************************************/
#define DIF_MISC_CTRL (DIRECT_IF_REVB_BASE + 0x0000003c)
/*****************************************************************************/
#define FLD_DIF_DIF_BYPASS 0x80000000
#define FLD_DIF_FM_NYQ_GAIN 0x40000000
#define FLD_DIF_RF_AGC_ENA 0x20000000
#define FLD_DIF_INT_AGC_ENA 0x10000000
#define FLD_DIF_IF_AGC_ENA 0x08000000
#define FLD_DIF_FORCE_RF_IF_LOCK 0x04000000
#define FLD_DIF_VIDEO_AGC_ENA 0x02000000
#define FLD_DIF_RF_AGC_INV 0x01000000
#define FLD_DIF_INT_AGC_INV 0x00800000
#define FLD_DIF_IF_AGC_INV 0x00400000
#define FLD_DIF_SPEC_INV 0x00200000
#define FLD_DIF_AUD_FULL_BW 0x00100000
#define FLD_DIF_AUD_SRC_SEL 0x00080000
/* Reserved [18] */
#define FLD_DIF_IF_FREQ 0x00030000
/* Reserved [15:14] */
#define FLD_DIF_TIP_OFFSET 0x00003f00
/* Reserved [7:5] */
#define FLD_DIF_DITHER_ENA 0x00000010
/* Reserved [3:1] */
#define FLD_DIF_RF_IF_LOCK 0x00000001
/*****************************************************************************/
#define DIF_SRC_PHASE_INC (DIRECT_IF_REVB_BASE + 0x00000040)
/*****************************************************************************/
/* Reserved [31:29] */
#define FLD_DIF_PHASE_INC 0x1fffffff
/*****************************************************************************/
#define DIF_SRC_GAIN_CONTROL (DIRECT_IF_REVB_BASE + 0x00000044)
/*****************************************************************************/
/* Reserved [31:16] */
#define FLD_DIF_SRC_KI 0x0000ff00
#define FLD_DIF_SRC_KD 0x000000ff
/*****************************************************************************/
#define DIF_BPF_COEFF01 (DIRECT_IF_REVB_BASE + 0x00000048)
/*****************************************************************************/
/* Reserved [31:19] */
#define FLD_DIF_BPF_COEFF_0 0x00070000
/* Reserved [15:4] */
#define FLD_DIF_BPF_COEFF_1 0x0000000f
/*****************************************************************************/
#define DIF_BPF_COEFF23 (DIRECT_IF_REVB_BASE + 0x0000004c)
/*****************************************************************************/
/* Reserved [31:22] */
#define FLD_DIF_BPF_COEFF_2 0x003f0000
/* Reserved [15:7] */
#define FLD_DIF_BPF_COEFF_3 0x0000007f
/*****************************************************************************/
#define DIF_BPF_COEFF45 (DIRECT_IF_REVB_BASE + 0x00000050)
/*****************************************************************************/
/* Reserved [31:24] */
#define FLD_DIF_BPF_COEFF_4 0x00ff0000
/* Reserved [15:8] */
#define FLD_DIF_BPF_COEFF_5 0x000000ff
/*****************************************************************************/
#define DIF_BPF_COEFF67 (DIRECT_IF_REVB_BASE + 0x00000054)
/*****************************************************************************/
/* Reserved [31:25] */
#define FLD_DIF_BPF_COEFF_6 0x01ff0000
/* Reserved [15:9] */
#define FLD_DIF_BPF_COEFF_7 0x000001ff
/*****************************************************************************/
#define DIF_BPF_COEFF89 (DIRECT_IF_REVB_BASE + 0x00000058)
/*****************************************************************************/
/* Reserved [31:26] */
#define FLD_DIF_BPF_COEFF_8 0x03ff0000
/* Reserved [15:10] */
#define FLD_DIF_BPF_COEFF_9 0x000003ff
/*****************************************************************************/
#define DIF_BPF_COEFF1011 (DIRECT_IF_REVB_BASE + 0x0000005c)
/*****************************************************************************/
/* Reserved [31:27] */
#define FLD_DIF_BPF_COEFF_10 0x07ff0000
/* Reserved [15:11] */
#define FLD_DIF_BPF_COEFF_11 0x000007ff
/*****************************************************************************/
#define DIF_BPF_COEFF1213 (DIRECT_IF_REVB_BASE + 0x00000060)
/*****************************************************************************/
/* Reserved [31:27] */
#define FLD_DIF_BPF_COEFF_12 0x07ff0000
/* Reserved [15:12] */
#define FLD_DIF_BPF_COEFF_13 0x00000fff
/*****************************************************************************/
#define DIF_BPF_COEFF1415 (DIRECT_IF_REVB_BASE + 0x00000064)
/*****************************************************************************/
/* Reserved [31:28] */
#define FLD_DIF_BPF_COEFF_14 0x0fff0000
/* Reserved [15:12] */
#define FLD_DIF_BPF_COEFF_15 0x00000fff
/*****************************************************************************/
#define DIF_BPF_COEFF1617 (DIRECT_IF_REVB_BASE + 0x00000068)
/*****************************************************************************/
/* Reserved [31:29] */
#define FLD_DIF_BPF_COEFF_16 0x1fff0000
/* Reserved [15:13] */
#define FLD_DIF_BPF_COEFF_17 0x00001fff
/*****************************************************************************/
#define DIF_BPF_COEFF1819 (DIRECT_IF_REVB_BASE + 0x0000006c)
/*****************************************************************************/
/* Reserved [31:29] */
#define FLD_DIF_BPF_COEFF_18 0x1fff0000
/* Reserved [15:13] */
#define FLD_DIF_BPF_COEFF_19 0x00001fff
/*****************************************************************************/
#define DIF_BPF_COEFF2021 (DIRECT_IF_REVB_BASE + 0x00000070)
/*****************************************************************************/
/* Reserved [31:29] */
#define FLD_DIF_BPF_COEFF_20 0x1fff0000
/* Reserved [15:14] */
#define FLD_DIF_BPF_COEFF_21 0x00003fff
/*****************************************************************************/
#define DIF_BPF_COEFF2223 (DIRECT_IF_REVB_BASE + 0x00000074)
/*****************************************************************************/
/* Reserved [31:30] */
#define FLD_DIF_BPF_COEFF_22 0x3fff0000
/* Reserved [15:14] */
#define FLD_DIF_BPF_COEFF_23 0x00003fff
/*****************************************************************************/
#define DIF_BPF_COEFF2425 (DIRECT_IF_REVB_BASE + 0x00000078)
/*****************************************************************************/
/* Reserved [31:30] */
#define FLD_DIF_BPF_COEFF_24 0x3fff0000
/* Reserved [15:14] */
#define FLD_DIF_BPF_COEFF_25 0x00003fff
/*****************************************************************************/
#define DIF_BPF_COEFF2627 (DIRECT_IF_REVB_BASE + 0x0000007c)
/*****************************************************************************/
/* Reserved [31:30] */
#define FLD_DIF_BPF_COEFF_26 0x3fff0000
/* Reserved [15:14] */
#define FLD_DIF_BPF_COEFF_27 0x00003fff
/*****************************************************************************/
#define DIF_BPF_COEFF2829 (DIRECT_IF_REVB_BASE + 0x00000080)
/*****************************************************************************/
/* Reserved [31:30] */
#define FLD_DIF_BPF_COEFF_28 0x3fff0000
/* Reserved [15:14] */
#define FLD_DIF_BPF_COEFF_29 0x00003fff
/*****************************************************************************/
#define DIF_BPF_COEFF3031 (DIRECT_IF_REVB_BASE + 0x00000084)
/*****************************************************************************/
/* Reserved [31:30] */
#define FLD_DIF_BPF_COEFF_30 0x3fff0000
/* Reserved [15:14] */
#define FLD_DIF_BPF_COEFF_31 0x00003fff
/*****************************************************************************/
#define DIF_BPF_COEFF3233 (DIRECT_IF_REVB_BASE + 0x00000088)
/*****************************************************************************/
/* Reserved [31:30] */
#define FLD_DIF_BPF_COEFF_32 0x3fff0000
/* Reserved [15:14] */
#define FLD_DIF_BPF_COEFF_33 0x00003fff
/*****************************************************************************/
#define DIF_BPF_COEFF3435 (DIRECT_IF_REVB_BASE + 0x0000008c)
/*****************************************************************************/
/* Reserved [31:30] */
#define FLD_DIF_BPF_COEFF_34 0x3fff0000
/* Reserved [15:14] */
#define FLD_DIF_BPF_COEFF_35 0x00003fff
/*****************************************************************************/
#define DIF_BPF_COEFF36 (DIRECT_IF_REVB_BASE + 0x00000090)
/*****************************************************************************/
/* Reserved [31:30] */
#define FLD_DIF_BPF_COEFF_36 0x3fff0000
/* Reserved [15:0] */
/*****************************************************************************/
#define DIF_RPT_VARIANCE (DIRECT_IF_REVB_BASE + 0x00000094)
/*****************************************************************************/
/* Reserved [31:20] */
#define FLD_DIF_RPT_VARIANCE 0x000fffff
/*****************************************************************************/
#define DIF_SOFT_RST_CTRL_REVB (DIRECT_IF_REVB_BASE + 0x00000098)
/*****************************************************************************/
/* Reserved [31:8] */
#define FLD_DIF_DIF_SOFT_RST 0x00000080
#define FLD_DIF_DIF_REG_RST_MSK 0x00000040
#define FLD_DIF_AGC_RST_MSK 0x00000020
#define FLD_DIF_CMP_RST_MSK 0x00000010
#define FLD_DIF_AVS_RST_MSK 0x00000008
#define FLD_DIF_NYQ_RST_MSK 0x00000004
#define FLD_DIF_DIF_SRC_RST_MSK 0x00000002
#define FLD_DIF_PLL_RST_MSK 0x00000001
/*****************************************************************************/
#define DIF_PLL_FREQ_ERR (DIRECT_IF_REVB_BASE + 0x0000009c)
/*****************************************************************************/
/* Reserved [31:25] */
#define FLD_DIF_CTL_IP 0x01ffffff
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,559 @@
/*
DVB device driver for cx231xx
Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
Based on em28xx driver
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/kernel.h>
#include <linux/usb.h>
#include "cx231xx.h"
#include <media/v4l2-common.h>
#include <media/videobuf-vmalloc.h>
#include "xc5000.h"
#include "dvb_dummy_fe.h"
MODULE_DESCRIPTION("driver for cx231xx based DVB cards");
MODULE_AUTHOR("Srinivasa Deevi <srinivasa.deevi@conexant.com>");
MODULE_LICENSE("GPL");
static unsigned int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "enable debug messages [dvb]");
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
#define dprintk(level, fmt, arg...) do { \
if (debug >= level) \
printk(KERN_DEBUG "%s/2-dvb: " fmt, dev->name, ## arg); \
} while (0)
#define CX231XX_DVB_NUM_BUFS 5
#define CX231XX_DVB_MAX_PACKETSIZE 564
#define CX231XX_DVB_MAX_PACKETS 64
struct cx231xx_dvb {
struct dvb_frontend *frontend;
/* feed count management */
struct mutex lock;
int nfeeds;
/* general boilerplate stuff */
struct dvb_adapter adapter;
struct dvb_demux demux;
struct dmxdev dmxdev;
struct dmx_frontend fe_hw;
struct dmx_frontend fe_mem;
struct dvb_net net;
};
static inline void print_err_status(struct cx231xx *dev, int packet, int status)
{
char *errmsg = "Unknown";
switch (status) {
case -ENOENT:
errmsg = "unlinked synchronuously";
break;
case -ECONNRESET:
errmsg = "unlinked asynchronuously";
break;
case -ENOSR:
errmsg = "Buffer error (overrun)";
break;
case -EPIPE:
errmsg = "Stalled (device not responding)";
break;
case -EOVERFLOW:
errmsg = "Babble (bad cable?)";
break;
case -EPROTO:
errmsg = "Bit-stuff error (bad cable?)";
break;
case -EILSEQ:
errmsg = "CRC/Timeout (could be anything)";
break;
case -ETIME:
errmsg = "Device does not respond";
break;
}
if (packet < 0) {
dprintk(1, "URB status %d [%s].\n", status, errmsg);
} else {
dprintk(1, "URB packet %d, status %d [%s].\n",
packet, status, errmsg);
}
}
static inline int dvb_isoc_copy(struct cx231xx *dev, struct urb *urb)
{
int i;
if (!dev)
return 0;
if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED))
return 0;
if (urb->status < 0) {
print_err_status(dev, -1, urb->status);
if (urb->status == -ENOENT)
return 0;
}
for (i = 0; i < urb->number_of_packets; i++) {
int status = urb->iso_frame_desc[i].status;
if (status < 0) {
print_err_status(dev, i, status);
if (urb->iso_frame_desc[i].status != -EPROTO)
continue;
}
dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer +
urb->iso_frame_desc[i].offset,
urb->iso_frame_desc[i].actual_length);
}
return 0;
}
static int start_streaming(struct cx231xx_dvb *dvb)
{
int rc;
struct cx231xx *dev = dvb->adapter.priv;
usb_set_interface(dev->udev, 0, 1);
rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
if (rc < 0)
return rc;
return cx231xx_init_isoc(dev, CX231XX_DVB_MAX_PACKETS,
CX231XX_DVB_NUM_BUFS,
CX231XX_DVB_MAX_PACKETSIZE, dvb_isoc_copy);
}
static int stop_streaming(struct cx231xx_dvb *dvb)
{
struct cx231xx *dev = dvb->adapter.priv;
cx231xx_uninit_isoc(dev);
cx231xx_set_mode(dev, CX231XX_SUSPEND);
return 0;
}
static int start_feed(struct dvb_demux_feed *feed)
{
struct dvb_demux *demux = feed->demux;
struct cx231xx_dvb *dvb = demux->priv;
int rc, ret;
if (!demux->dmx.frontend)
return -EINVAL;
mutex_lock(&dvb->lock);
dvb->nfeeds++;
rc = dvb->nfeeds;
if (dvb->nfeeds == 1) {
ret = start_streaming(dvb);
if (ret < 0)
rc = ret;
}
mutex_unlock(&dvb->lock);
return rc;
}
static int stop_feed(struct dvb_demux_feed *feed)
{
struct dvb_demux *demux = feed->demux;
struct cx231xx_dvb *dvb = demux->priv;
int err = 0;
mutex_lock(&dvb->lock);
dvb->nfeeds--;
if (0 == dvb->nfeeds)
err = stop_streaming(dvb);
mutex_unlock(&dvb->lock);
return err;
}
/* ------------------------------------------------------------------ */
static int cx231xx_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire)
{
struct cx231xx *dev = fe->dvb->priv;
if (acquire)
return cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
else
return cx231xx_set_mode(dev, CX231XX_SUSPEND);
}
/* ------------------------------------------------------------------ */
static struct xc5000_config cnxt_rde250_tunerconfig = {
.i2c_address = 0x61,
.if_khz = 5380,
};
/* ------------------------------------------------------------------ */
#if 0
static int attach_xc5000(u8 addr, struct cx231xx *dev)
{
struct dvb_frontend *fe;
struct xc5000_config cfg;
memset(&cfg, 0, sizeof(cfg));
cfg.i2c_adap = &dev->i2c_bus[1].i2c_adap;
cfg.i2c_addr = addr;
if (!dev->dvb->frontend) {
printk(KERN_ERR "%s/2: dvb frontend not attached. "
"Can't attach xc5000\n", dev->name);
return -EINVAL;
}
fe = dvb_attach(xc5000_attach, dev->dvb->frontend, &cfg);
if (!fe) {
printk(KERN_ERR "%s/2: xc5000 attach failed\n", dev->name);
dvb_frontend_detach(dev->dvb->frontend);
dev->dvb->frontend = NULL;
return -EINVAL;
}
printk(KERN_INFO "%s/2: xc5000 attached\n", dev->name);
return 0;
}
#endif
int cx231xx_set_analog_freq(struct cx231xx *dev, u32 freq)
{
int status = 0;
if ((dev->dvb != NULL) && (dev->dvb->frontend != NULL)) {
struct dvb_tuner_ops *dops = &dev->dvb->frontend->ops.tuner_ops;
if (dops->set_analog_params != NULL) {
struct analog_parameters params;
params.frequency = freq;
params.std = dev->norm;
params.mode = 0; /* 0- Air; 1 - cable */
/*params.audmode = ; */
/* Set the analog parameters to set the frequency */
cx231xx_info("Setting Frequency for XC5000\n");
dops->set_analog_params(dev->dvb->frontend, &params);
}
}
return status;
}
int cx231xx_reset_analog_tuner(struct cx231xx *dev)
{
int status = 0;
if ((dev->dvb != NULL) && (dev->dvb->frontend != NULL)) {
struct dvb_tuner_ops *dops = &dev->dvb->frontend->ops.tuner_ops;
if (dops->init != NULL && !dev->xc_fw_load_done) {
cx231xx_info("Reloading firmware for XC5000\n");
status = dops->init(dev->dvb->frontend);
if (status == 0) {
dev->xc_fw_load_done = 1;
cx231xx_info
("XC5000 firmware download completed\n");
} else {
dev->xc_fw_load_done = 0;
cx231xx_info
("XC5000 firmware download failed !!!\n");
}
}
}
return status;
}
/* ------------------------------------------------------------------ */
static int register_dvb(struct cx231xx_dvb *dvb,
struct module *module,
struct cx231xx *dev, struct device *device)
{
int result;
mutex_init(&dvb->lock);
/* register adapter */
result = dvb_register_adapter(&dvb->adapter, dev->name, module, device,
adapter_nr);
if (result < 0) {
printk(KERN_WARNING
"%s: dvb_register_adapter failed (errno = %d)\n",
dev->name, result);
goto fail_adapter;
}
/* Ensure all frontends negotiate bus access */
dvb->frontend->ops.ts_bus_ctrl = cx231xx_dvb_bus_ctrl;
dvb->adapter.priv = dev;
/* register frontend */
result = dvb_register_frontend(&dvb->adapter, dvb->frontend);
if (result < 0) {
printk(KERN_WARNING
"%s: dvb_register_frontend failed (errno = %d)\n",
dev->name, result);
goto fail_frontend;
}
/* register demux stuff */
dvb->demux.dmx.capabilities =
DMX_TS_FILTERING | DMX_SECTION_FILTERING |
DMX_MEMORY_BASED_FILTERING;
dvb->demux.priv = dvb;
dvb->demux.filternum = 256;
dvb->demux.feednum = 256;
dvb->demux.start_feed = start_feed;
dvb->demux.stop_feed = stop_feed;
result = dvb_dmx_init(&dvb->demux);
if (result < 0) {
printk(KERN_WARNING "%s: dvb_dmx_init failed (errno = %d)\n",
dev->name, result);
goto fail_dmx;
}
dvb->dmxdev.filternum = 256;
dvb->dmxdev.demux = &dvb->demux.dmx;
dvb->dmxdev.capabilities = 0;
result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter);
if (result < 0) {
printk(KERN_WARNING "%s: dvb_dmxdev_init failed (errno = %d)\n",
dev->name, result);
goto fail_dmxdev;
}
dvb->fe_hw.source = DMX_FRONTEND_0;
result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw);
if (result < 0) {
printk(KERN_WARNING
"%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n",
dev->name, result);
goto fail_fe_hw;
}
dvb->fe_mem.source = DMX_MEMORY_FE;
result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem);
if (result < 0) {
printk(KERN_WARNING
"%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n",
dev->name, result);
goto fail_fe_mem;
}
result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw);
if (result < 0) {
printk(KERN_WARNING
"%s: connect_frontend failed (errno = %d)\n", dev->name,
result);
goto fail_fe_conn;
}
/* register network adapter */
dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx);
return 0;
fail_fe_conn:
dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
fail_fe_mem:
dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
fail_fe_hw:
dvb_dmxdev_release(&dvb->dmxdev);
fail_dmxdev:
dvb_dmx_release(&dvb->demux);
fail_dmx:
dvb_unregister_frontend(dvb->frontend);
fail_frontend:
dvb_frontend_detach(dvb->frontend);
dvb_unregister_adapter(&dvb->adapter);
fail_adapter:
return result;
}
static void unregister_dvb(struct cx231xx_dvb *dvb)
{
dvb_net_release(&dvb->net);
dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
dvb_dmxdev_release(&dvb->dmxdev);
dvb_dmx_release(&dvb->demux);
dvb_unregister_frontend(dvb->frontend);
dvb_frontend_detach(dvb->frontend);
dvb_unregister_adapter(&dvb->adapter);
}
static int dvb_init(struct cx231xx *dev)
{
int result = 0;
struct cx231xx_dvb *dvb;
if (!dev->board.has_dvb) {
/* This device does not support the extension */
return 0;
}
dvb = kzalloc(sizeof(struct cx231xx_dvb), GFP_KERNEL);
if (dvb == NULL) {
printk(KERN_INFO "cx231xx_dvb: memory allocation failed\n");
return -ENOMEM;
}
dev->dvb = dvb;
dev->cx231xx_set_analog_freq = cx231xx_set_analog_freq;
dev->cx231xx_reset_analog_tuner = cx231xx_reset_analog_tuner;
cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
/* init frontend */
switch (dev->model) {
case CX231XX_BOARD_CNXT_RDE_250:
/* dev->dvb->frontend = dvb_attach(s5h1411_attach,
&dvico_s5h1411_config,
&dev->i2c_bus[1].i2c_adap); */
dev->dvb->frontend = dvb_attach(dvb_dummy_fe_ofdm_attach);
if (dev->dvb->frontend == NULL) {
printk(DRIVER_NAME
": Failed to attach dummy front end\n");
result = -EINVAL;
goto out_free;
}
/* define general-purpose callback pointer */
dvb->frontend->callback = cx231xx_tuner_callback;
if (dvb_attach(xc5000_attach, dev->dvb->frontend,
&dev->i2c_bus[1].i2c_adap,
&cnxt_rde250_tunerconfig) < 0) {
result = -EINVAL;
goto out_free;
}
break;
case CX231XX_BOARD_CNXT_RDU_250:
dev->dvb->frontend = dvb_attach(dvb_dummy_fe_ofdm_attach);
if (dev->dvb->frontend == NULL) {
printk(DRIVER_NAME
": Failed to attach dummy front end\n");
result = -EINVAL;
goto out_free;
}
/* define general-purpose callback pointer */
dvb->frontend->callback = cx231xx_tuner_callback;
if (dvb_attach(xc5000_attach, dev->dvb->frontend,
&dev->i2c_bus[1].i2c_adap,
&cnxt_rde250_tunerconfig) < 0) {
result = -EINVAL;
goto out_free;
}
break;
default:
printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card"
" isn't supported yet\n", dev->name);
break;
}
if (NULL == dvb->frontend) {
printk(KERN_ERR
"%s/2: frontend initialization failed\n", dev->name);
result = -EINVAL;
goto out_free;
}
/* register everything */
result = register_dvb(dvb, THIS_MODULE, dev, &dev->udev->dev);
if (result < 0)
goto out_free;
cx231xx_set_mode(dev, CX231XX_SUSPEND);
printk(KERN_INFO "Successfully loaded cx231xx-dvb\n");
return 0;
out_free:
cx231xx_set_mode(dev, CX231XX_SUSPEND);
kfree(dvb);
dev->dvb = NULL;
return result;
}
static int dvb_fini(struct cx231xx *dev)
{
if (!dev->board.has_dvb) {
/* This device does not support the extension */
return 0;
}
if (dev->dvb) {
unregister_dvb(dev->dvb);
dev->dvb = NULL;
}
return 0;
}
static struct cx231xx_ops dvb_ops = {
.id = CX231XX_DVB,
.name = "Cx231xx dvb Extension",
.init = dvb_init,
.fini = dvb_fini,
};
static int __init cx231xx_dvb_register(void)
{
return cx231xx_register_extension(&dvb_ops);
}
static void __exit cx231xx_dvb_unregister(void)
{
cx231xx_unregister_extension(&dvb_ops);
}
module_init(cx231xx_dvb_register);
module_exit(cx231xx_dvb_unregister);

View File

@@ -0,0 +1,528 @@
/*
cx231xx-i2c.c - driver for Conexant Cx23100/101/102 USB video capture devices
Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
Based on em28xx driver
Based on Cx23885 driver
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/module.h>
#include <linux/kernel.h>
#include <linux/usb.h>
#include <linux/i2c.h>
#include <media/v4l2-common.h>
#include <media/tuner.h>
#include "cx231xx.h"
/* ----------------------------------------------------------- */
static unsigned int i2c_scan;
module_param(i2c_scan, int, 0444);
MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time");
static unsigned int i2c_debug;
module_param(i2c_debug, int, 0644);
MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]");
#define dprintk1(lvl, fmt, args...) \
do { \
if (i2c_debug >= lvl) { \
printk(fmt, ##args); \
} \
} while (0)
#define dprintk2(lvl, fmt, args...) \
do { \
if (i2c_debug >= lvl) { \
printk(KERN_DEBUG "%s at %s: " fmt, \
dev->name, __func__ , ##args); \
} \
} while (0)
/*
* cx231xx_i2c_send_bytes()
*/
int cx231xx_i2c_send_bytes(struct i2c_adapter *i2c_adap,
const struct i2c_msg *msg)
{
struct cx231xx_i2c *bus = i2c_adap->algo_data;
struct cx231xx *dev = bus->dev;
struct cx231xx_i2c_xfer_data req_data;
int status = 0;
u16 size = 0;
u8 loop = 0;
u8 saddr_len = 1;
u8 *buf_ptr = NULL;
u16 saddr = 0;
u8 need_gpio = 0;
if ((bus->nr == 1) && (msg->addr == 0x61)
&& (dev->tuner_type == TUNER_XC5000)) {
size = msg->len;
if (size == 2) { /* register write sub addr */
/* Just writing sub address will cause problem
* to XC5000. So ignore the request */
return 0;
} else if (size == 4) { /* register write with sub addr */
if (msg->len >= 2)
saddr = msg->buf[0] << 8 | msg->buf[1];
else if (msg->len == 1)
saddr = msg->buf[0];
switch (saddr) {
case 0x0000: /* start tuner calibration mode */
need_gpio = 1;
/* FW Loading is done */
dev->xc_fw_load_done = 1;
break;
case 0x000D: /* Set signal source */
case 0x0001: /* Set TV standard - Video */
case 0x0002: /* Set TV standard - Audio */
case 0x0003: /* Set RF Frequency */
need_gpio = 1;
break;
default:
if (dev->xc_fw_load_done)
need_gpio = 1;
break;
}
if (need_gpio) {
dprintk1(1,
"GPIO WRITE: addr 0x%x, len %d, saddr 0x%x\n",
msg->addr, msg->len, saddr);
return dev->cx231xx_gpio_i2c_write(dev,
msg->addr,
msg->buf,
msg->len);
}
}
/* special case for Xc5000 tuner case */
saddr_len = 1;
/* adjust the length to correct length */
size -= saddr_len;
buf_ptr = (u8 *) (msg->buf + 1);
do {
/* prepare xfer_data struct */
req_data.dev_addr = msg->addr;
req_data.direction = msg->flags;
req_data.saddr_len = saddr_len;
req_data.saddr_dat = msg->buf[0];
req_data.buf_size = size > 16 ? 16 : size;
req_data.p_buffer = (u8 *) (buf_ptr + loop * 16);
bus->i2c_nostop = (size > 16) ? 1 : 0;
bus->i2c_reserve = (loop == 0) ? 0 : 1;
/* usb send command */
status = dev->cx231xx_send_usb_command(bus, &req_data);
loop++;
if (size >= 16)
size -= 16;
else
size = 0;
} while (size > 0);
bus->i2c_nostop = 0;
bus->i2c_reserve = 0;
} else { /* regular case */
/* prepare xfer_data struct */
req_data.dev_addr = msg->addr;
req_data.direction = msg->flags;
req_data.saddr_len = 0;
req_data.saddr_dat = 0;
req_data.buf_size = msg->len;
req_data.p_buffer = msg->buf;
/* usb send command */
status = dev->cx231xx_send_usb_command(bus, &req_data);
}
return status < 0 ? status : 0;
}
/*
* cx231xx_i2c_recv_bytes()
* read a byte from the i2c device
*/
static int cx231xx_i2c_recv_bytes(struct i2c_adapter *i2c_adap,
const struct i2c_msg *msg)
{
struct cx231xx_i2c *bus = i2c_adap->algo_data;
struct cx231xx *dev = bus->dev;
struct cx231xx_i2c_xfer_data req_data;
int status = 0;
u16 saddr = 0;
u8 need_gpio = 0;
if ((bus->nr == 1) && (msg->addr == 0x61)
&& dev->tuner_type == TUNER_XC5000) {
if (msg->len == 2)
saddr = msg->buf[0] << 8 | msg->buf[1];
else if (msg->len == 1)
saddr = msg->buf[0];
if (dev->xc_fw_load_done) {
switch (saddr) {
case 0x0009: /* BUSY check */
dprintk1(1,
"GPIO R E A D: Special case BUSY check \n");
/*Try read BUSY register, just set it to zero*/
msg->buf[0] = 0;
if (msg->len == 2)
msg->buf[1] = 0;
return 0;
case 0x0004: /* read Lock status */
need_gpio = 1;
break;
}
if (need_gpio) {
/* this is a special case to handle Xceive tuner
clock stretch issue with gpio based I2C */
dprintk1(1,
"GPIO R E A D: addr 0x%x, len %d, saddr 0x%x\n",
msg->addr, msg->len,
msg->buf[0] << 8 | msg->buf[1]);
status =
dev->cx231xx_gpio_i2c_write(dev, msg->addr,
msg->buf,
msg->len);
status =
dev->cx231xx_gpio_i2c_read(dev, msg->addr,
msg->buf,
msg->len);
return status;
}
}
/* prepare xfer_data struct */
req_data.dev_addr = msg->addr;
req_data.direction = msg->flags;
req_data.saddr_len = msg->len;
req_data.saddr_dat = msg->buf[0] << 8 | msg->buf[1];
req_data.buf_size = msg->len;
req_data.p_buffer = msg->buf;
/* usb send command */
status = dev->cx231xx_send_usb_command(bus, &req_data);
} else {
/* prepare xfer_data struct */
req_data.dev_addr = msg->addr;
req_data.direction = msg->flags;
req_data.saddr_len = 0;
req_data.saddr_dat = 0;
req_data.buf_size = msg->len;
req_data.p_buffer = msg->buf;
/* usb send command */
status = dev->cx231xx_send_usb_command(bus, &req_data);
}
return status < 0 ? status : 0;
}
/*
* cx231xx_i2c_recv_bytes_with_saddr()
* read a byte from the i2c device
*/
static int cx231xx_i2c_recv_bytes_with_saddr(struct i2c_adapter *i2c_adap,
const struct i2c_msg *msg1,
const struct i2c_msg *msg2)
{
struct cx231xx_i2c *bus = i2c_adap->algo_data;
struct cx231xx *dev = bus->dev;
struct cx231xx_i2c_xfer_data req_data;
int status = 0;
u16 saddr = 0;
u8 need_gpio = 0;
if (msg1->len == 2)
saddr = msg1->buf[0] << 8 | msg1->buf[1];
else if (msg1->len == 1)
saddr = msg1->buf[0];
if ((bus->nr == 1) && (msg2->addr == 0x61)
&& dev->tuner_type == TUNER_XC5000) {
if ((msg2->len < 16)) {
dprintk1(1,
"i2c_read: addr 0x%x, len %d, saddr 0x%x, len %d\n",
msg2->addr, msg2->len, saddr, msg1->len);
switch (saddr) {
case 0x0008: /* read FW load status */
need_gpio = 1;
break;
case 0x0004: /* read Lock status */
need_gpio = 1;
break;
}
if (need_gpio) {
status =
dev->cx231xx_gpio_i2c_write(dev, msg1->addr,
msg1->buf,
msg1->len);
status =
dev->cx231xx_gpio_i2c_read(dev, msg2->addr,
msg2->buf,
msg2->len);
return status;
}
}
}
/* prepare xfer_data struct */
req_data.dev_addr = msg2->addr;
req_data.direction = msg2->flags;
req_data.saddr_len = msg1->len;
req_data.saddr_dat = saddr;
req_data.buf_size = msg2->len;
req_data.p_buffer = msg2->buf;
/* usb send command */
status = dev->cx231xx_send_usb_command(bus, &req_data);
return status < 0 ? status : 0;
}
/*
* cx231xx_i2c_check_for_device()
* check if there is a i2c_device at the supplied address
*/
static int cx231xx_i2c_check_for_device(struct i2c_adapter *i2c_adap,
const struct i2c_msg *msg)
{
struct cx231xx_i2c *bus = i2c_adap->algo_data;
struct cx231xx *dev = bus->dev;
struct cx231xx_i2c_xfer_data req_data;
int status = 0;
/* prepare xfer_data struct */
req_data.dev_addr = msg->addr;
req_data.direction = msg->flags;
req_data.saddr_len = 0;
req_data.saddr_dat = 0;
req_data.buf_size = 0;
req_data.p_buffer = NULL;
/* usb send command */
status = dev->cx231xx_send_usb_command(bus, &req_data);
return status < 0 ? status : 0;
}
/*
* cx231xx_i2c_xfer()
* the main i2c transfer function
*/
static int cx231xx_i2c_xfer(struct i2c_adapter *i2c_adap,
struct i2c_msg msgs[], int num)
{
struct cx231xx_i2c *bus = i2c_adap->algo_data;
struct cx231xx *dev = bus->dev;
int addr, rc, i, byte;
if (num <= 0)
return 0;
for (i = 0; i < num; i++) {
addr = msgs[i].addr >> 1;
dprintk2(2, "%s %s addr=%x len=%d:",
(msgs[i].flags & I2C_M_RD) ? "read" : "write",
i == num - 1 ? "stop" : "nonstop", addr, msgs[i].len);
if (!msgs[i].len) {
/* no len: check only for device presence */
rc = cx231xx_i2c_check_for_device(i2c_adap, &msgs[i]);
if (rc < 0) {
dprintk2(2, " no device\n");
return rc;
}
} else if (msgs[i].flags & I2C_M_RD) {
/* read bytes */
rc = cx231xx_i2c_recv_bytes(i2c_adap, &msgs[i]);
if (i2c_debug >= 2) {
for (byte = 0; byte < msgs[i].len; byte++)
printk(" %02x", msgs[i].buf[byte]);
}
} else if (i + 1 < num && (msgs[i + 1].flags & I2C_M_RD) &&
msgs[i].addr == msgs[i + 1].addr
&& (msgs[i].len <= 2) && (bus->nr < 2)) {
/* read bytes */
rc = cx231xx_i2c_recv_bytes_with_saddr(i2c_adap,
&msgs[i],
&msgs[i + 1]);
if (i2c_debug >= 2) {
for (byte = 0; byte < msgs[i].len; byte++)
printk(" %02x", msgs[i].buf[byte]);
}
i++;
} else {
/* write bytes */
if (i2c_debug >= 2) {
for (byte = 0; byte < msgs[i].len; byte++)
printk(" %02x", msgs[i].buf[byte]);
}
rc = cx231xx_i2c_send_bytes(i2c_adap, &msgs[i]);
}
if (rc < 0)
goto err;
if (i2c_debug >= 2)
printk("\n");
}
return num;
err:
dprintk2(2, " ERROR: %i\n", rc);
return rc;
}
/* ----------------------------------------------------------- */
/*
* functionality()
*/
static u32 functionality(struct i2c_adapter *adap)
{
return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C;
}
static struct i2c_algorithm cx231xx_algo = {
.master_xfer = cx231xx_i2c_xfer,
.functionality = functionality,
};
static struct i2c_adapter cx231xx_adap_template = {
.owner = THIS_MODULE,
.name = "cx231xx",
.algo = &cx231xx_algo,
};
static struct i2c_client cx231xx_client_template = {
.name = "cx231xx internal",
};
/* ----------------------------------------------------------- */
/*
* i2c_devs
* incomplete list of known devices
*/
static char *i2c_devs[128] = {
[0x60 >> 1] = "colibri",
[0x88 >> 1] = "hammerhead",
[0x8e >> 1] = "CIR",
[0x32 >> 1] = "GeminiIII",
[0x02 >> 1] = "Aquarius",
[0xa0 >> 1] = "eeprom",
[0xc0 >> 1] = "tuner/XC3028",
[0xc2 >> 1] = "tuner/XC5000",
};
/*
* cx231xx_do_i2c_scan()
* check i2c address range for devices
*/
void cx231xx_do_i2c_scan(struct cx231xx *dev, struct i2c_client *c)
{
unsigned char buf;
int i, rc;
cx231xx_info(": Checking for I2C devices ..\n");
for (i = 0; i < 128; i++) {
c->addr = i;
rc = i2c_master_recv(c, &buf, 0);
if (rc < 0)
continue;
cx231xx_info("%s: i2c scan: found device @ 0x%x [%s]\n",
dev->name, i << 1,
i2c_devs[i] ? i2c_devs[i] : "???");
}
cx231xx_info(": Completed Checking for I2C devices.\n");
}
/*
* cx231xx_i2c_register()
* register i2c bus
*/
int cx231xx_i2c_register(struct cx231xx_i2c *bus)
{
struct cx231xx *dev = bus->dev;
BUG_ON(!dev->cx231xx_send_usb_command);
memcpy(&bus->i2c_adap, &cx231xx_adap_template, sizeof(bus->i2c_adap));
memcpy(&bus->i2c_algo, &cx231xx_algo, sizeof(bus->i2c_algo));
memcpy(&bus->i2c_client, &cx231xx_client_template,
sizeof(bus->i2c_client));
bus->i2c_adap.dev.parent = &dev->udev->dev;
strlcpy(bus->i2c_adap.name, bus->dev->name, sizeof(bus->i2c_adap.name));
bus->i2c_algo.data = bus;
bus->i2c_adap.algo_data = bus;
i2c_set_adapdata(&bus->i2c_adap, &dev->v4l2_dev);
i2c_add_adapter(&bus->i2c_adap);
bus->i2c_client.adapter = &bus->i2c_adap;
if (0 == bus->i2c_rc) {
if (i2c_scan)
cx231xx_do_i2c_scan(dev, &bus->i2c_client);
/* Instantiate the IR receiver device, if present */
cx231xx_register_i2c_ir(dev);
} else
cx231xx_warn("%s: i2c bus %d register FAILED\n",
dev->name, bus->nr);
return bus->i2c_rc;
}
/*
* cx231xx_i2c_unregister()
* unregister i2c_bus
*/
int cx231xx_i2c_unregister(struct cx231xx_i2c *bus)
{
i2c_del_adapter(&bus->i2c_adap);
return 0;
}

View File

@@ -0,0 +1,246 @@
/*
handle cx231xx IR remotes via linux kernel input layer.
Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
Based on em28xx driver
< This is a place holder for IR now.>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/usb.h>
#include "cx231xx.h"
static unsigned int ir_debug;
module_param(ir_debug, int, 0644);
MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]");
#define i2cdprintk(fmt, arg...) \
if (ir_debug) { \
printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \
}
#define dprintk(fmt, arg...) \
if (ir_debug) { \
printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \
}
/**********************************************************
Polling structure used by cx231xx IR's
**********************************************************/
struct cx231xx_ir_poll_result {
unsigned int toggle_bit:1;
unsigned int read_count:7;
u8 rc_address;
u8 rc_data[4];
};
struct cx231xx_IR {
struct cx231xx *dev;
struct input_dev *input;
struct ir_input_state ir;
char name[32];
char phys[32];
/* poll external decoder */
int polling;
struct work_struct work;
struct timer_list timer;
unsigned int last_toggle:1;
unsigned int last_readcount;
unsigned int repeat_interval;
int (*get_key) (struct cx231xx_IR *, struct cx231xx_ir_poll_result *);
};
/**********************************************************
Polling code for cx231xx
**********************************************************/
static void cx231xx_ir_handle_key(struct cx231xx_IR *ir)
{
int result;
int do_sendkey = 0;
struct cx231xx_ir_poll_result poll_result;
/* read the registers containing the IR status */
result = ir->get_key(ir, &poll_result);
if (result < 0) {
dprintk("ir->get_key() failed %d\n", result);
return;
}
dprintk("ir->get_key result tb=%02x rc=%02x lr=%02x data=%02x\n",
poll_result.toggle_bit, poll_result.read_count,
ir->last_readcount, poll_result.rc_data[0]);
if (ir->dev->chip_id == CHIP_ID_EM2874) {
/* The em2874 clears the readcount field every time the
register is read. The em2860/2880 datasheet says that it
is supposed to clear the readcount, but it doesn't. So with
the em2874, we are looking for a non-zero read count as
opposed to a readcount that is incrementing */
ir->last_readcount = 0;
}
if (poll_result.read_count == 0) {
/* The button has not been pressed since the last read */
} else if (ir->last_toggle != poll_result.toggle_bit) {
/* A button has been pressed */
dprintk("button has been pressed\n");
ir->last_toggle = poll_result.toggle_bit;
ir->repeat_interval = 0;
do_sendkey = 1;
} else if (poll_result.toggle_bit == ir->last_toggle &&
poll_result.read_count > 0 &&
poll_result.read_count != ir->last_readcount) {
/* The button is still being held down */
dprintk("button being held down\n");
/* Debouncer for first keypress */
if (ir->repeat_interval++ > 9) {
/* Start repeating after 1 second */
do_sendkey = 1;
}
}
if (do_sendkey) {
dprintk("sending keypress\n");
ir_input_keydown(ir->input, &ir->ir, poll_result.rc_data[0],
poll_result.rc_data[0]);
ir_input_nokey(ir->input, &ir->ir);
}
ir->last_readcount = poll_result.read_count;
return;
}
static void ir_timer(unsigned long data)
{
struct cx231xx_IR *ir = (struct cx231xx_IR *)data;
schedule_work(&ir->work);
}
static void cx231xx_ir_work(struct work_struct *work)
{
struct cx231xx_IR *ir = container_of(work, struct cx231xx_IR, work);
cx231xx_ir_handle_key(ir);
mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling));
}
void cx231xx_ir_start(struct cx231xx_IR *ir)
{
setup_timer(&ir->timer, ir_timer, (unsigned long)ir);
INIT_WORK(&ir->work, cx231xx_ir_work);
schedule_work(&ir->work);
}
static void cx231xx_ir_stop(struct cx231xx_IR *ir)
{
del_timer_sync(&ir->timer);
flush_scheduled_work();
}
int cx231xx_ir_init(struct cx231xx *dev)
{
struct cx231xx_IR *ir;
struct input_dev *input_dev;
u8 ir_config;
int err = -ENOMEM;
if (dev->board.ir_codes == NULL) {
/* No remote control support */
return 0;
}
ir = kzalloc(sizeof(*ir), GFP_KERNEL);
input_dev = input_allocate_device();
if (!ir || !input_dev)
goto err_out_free;
ir->input = input_dev;
/* Setup the proper handler based on the chip */
switch (dev->chip_id) {
default:
printk("Unrecognized cx231xx chip id: IR not supported\n");
goto err_out_free;
}
/* This is how often we ask the chip for IR information */
ir->polling = 100; /* ms */
/* init input device */
snprintf(ir->name, sizeof(ir->name), "cx231xx IR (%s)", dev->name);
usb_make_path(dev->udev, ir->phys, sizeof(ir->phys));
strlcat(ir->phys, "/input0", sizeof(ir->phys));
ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER, dev->board.ir_codes);
input_dev->name = ir->name;
input_dev->phys = ir->phys;
input_dev->id.bustype = BUS_USB;
input_dev->id.version = 1;
input_dev->id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor);
input_dev->id.product = le16_to_cpu(dev->udev->descriptor.idProduct);
input_dev->dev.parent = &dev->udev->dev;
/* record handles to ourself */
ir->dev = dev;
dev->ir = ir;
cx231xx_ir_start(ir);
/* all done */
err = input_register_device(ir->input);
if (err)
goto err_out_stop;
return 0;
err_out_stop:
cx231xx_ir_stop(ir);
dev->ir = NULL;
err_out_free:
input_free_device(input_dev);
kfree(ir);
return err;
}
int cx231xx_ir_fini(struct cx231xx *dev)
{
struct cx231xx_IR *ir = dev->ir;
/* skip detach on non attached boards */
if (!ir)
return 0;
cx231xx_ir_stop(ir);
input_unregister_device(ir->input);
kfree(ir);
/* done */
dev->ir = NULL;
return 0;
}

View File

@@ -0,0 +1,795 @@
/*
cx231xx-pcb-config.c - driver for Conexant
Cx23100/101/102 USB video capture devices
Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
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 "cx231xx.h"
#include "cx231xx-conf-reg.h"
static unsigned int pcb_debug;
module_param(pcb_debug, int, 0644);
MODULE_PARM_DESC(pcb_debug, "enable pcb config debug messages [video]");
/******************************************************************************/
struct pcb_config cx231xx_Scenario[] = {
{
INDEX_SELFPOWER_DIGITAL_ONLY, /* index */
USB_SELF_POWER, /* power_type */
0, /* speed , not decide yet */
MOD_DIGITAL, /* mode */
SOURCE_TS_BDA, /* ts1_source, digital tv only */
NOT_SUPPORTED, /* ts2_source */
NOT_SUPPORTED, /* analog source */
0, /* digital_index */
0, /* analog index */
0, /* dif_index */
0, /* external_index */
1, /* only one configuration */
{
{
0, /* config index */
{
0, /* interrupt ep index */
1, /* ts1 index */
NOT_SUPPORTED, /* TS2 index */
NOT_SUPPORTED, /* AUDIO */
NOT_SUPPORTED, /* VIDEO */
NOT_SUPPORTED, /* VANC */
NOT_SUPPORTED, /* HANC */
NOT_SUPPORTED /* ir_index */
}
,
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
,
/* full-speed config */
{
{
0, /* config index */
{
0, /* interrupt ep index */
1, /* ts1 index */
NOT_SUPPORTED, /* TS2 index */
NOT_SUPPORTED, /* AUDIO */
NOT_SUPPORTED, /* VIDEO */
NOT_SUPPORTED, /* VANC */
NOT_SUPPORTED, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
}
,
{
INDEX_SELFPOWER_DUAL_DIGITAL, /* index */
USB_SELF_POWER, /* power_type */
0, /* speed , not decide yet */
MOD_DIGITAL, /* mode */
SOURCE_TS_BDA, /* ts1_source, digital tv only */
0, /* ts2_source,need update from register */
NOT_SUPPORTED, /* analog source */
0, /* digital_index */
0, /* analog index */
0, /* dif_index */
0, /* external_index */
1, /* only one configuration */
{
{
0, /* config index */
{
0, /* interrupt ep index */
1, /* ts1 index */
2, /* TS2 index */
NOT_SUPPORTED, /* AUDIO */
NOT_SUPPORTED, /* VIDEO */
NOT_SUPPORTED, /* VANC */
NOT_SUPPORTED, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
,
/* full-speed */
{
{
0, /* config index */
{
0, /* interrupt ep index */
1, /* ts1 index */
2, /* TS2 index */
NOT_SUPPORTED, /* AUDIO */
NOT_SUPPORTED, /* VIDEO */
NOT_SUPPORTED, /* VANC */
NOT_SUPPORTED, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
}
,
{
INDEX_SELFPOWER_ANALOG_ONLY, /* index */
USB_SELF_POWER, /* power_type */
0, /* speed , not decide yet */
MOD_ANALOG | MOD_DIF | MOD_EXTERNAL, /* mode ,analog tv only */
NOT_SUPPORTED, /* ts1_source, NOT SUPPORT */
NOT_SUPPORTED, /* ts2_source,NOT SUPPORT */
0, /* analog source, need update */
0, /* digital_index */
0, /* analog index */
0, /* dif_index */
0, /* external_index */
1, /* only one configuration */
{
{
0, /* config index */
{
0, /* interrupt ep index */
NOT_SUPPORTED, /* ts1 index */
NOT_SUPPORTED, /* TS2 index */
1, /* AUDIO */
2, /* VIDEO */
3, /* VANC */
4, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
,
/* full-speed */
{
{
0, /* config index */
{
0, /* interrupt ep index */
NOT_SUPPORTED, /* ts1 index */
NOT_SUPPORTED, /* TS2 index */
1, /* AUDIO */
2, /* VIDEO */
NOT_SUPPORTED, /* VANC */
NOT_SUPPORTED, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
}
,
{
INDEX_SELFPOWER_DUAL, /* index */
USB_SELF_POWER, /* power_type */
0, /* speed , not decide yet */
/* mode ,analog tv and digital path */
MOD_ANALOG | MOD_DIF | MOD_DIGITAL | MOD_EXTERNAL,
0, /* ts1_source,will update in register */
NOT_SUPPORTED, /* ts2_source,NOT SUPPORT */
0, /* analog source need update */
0, /* digital_index */
0, /* analog index */
0, /* dif_index */
0, /* external_index */
1, /* only one configuration */
{
{
0, /* config index */
{
0, /* interrupt ep index */
1, /* ts1 index */
NOT_SUPPORTED, /* TS2 index */
2, /* AUDIO */
3, /* VIDEO */
4, /* VANC */
5, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
,
/* full-speed */
{
{
0, /* config index */
{
0, /* interrupt ep index */
1, /* ts1 index */
NOT_SUPPORTED, /* TS2 index */
2, /* AUDIO */
3, /* VIDEO */
NOT_SUPPORTED, /* VANC */
NOT_SUPPORTED, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
}
,
{
INDEX_SELFPOWER_TRIPLE, /* index */
USB_SELF_POWER, /* power_type */
0, /* speed , not decide yet */
/* mode ,analog tv and digital path */
MOD_ANALOG | MOD_DIF | MOD_DIGITAL | MOD_EXTERNAL,
0, /* ts1_source, update in register */
0, /* ts2_source,update in register */
0, /* analog source, need update */
0, /* digital_index */
0, /* analog index */
0, /* dif_index */
0, /* external_index */
1, /* only one configuration */
{
{
0, /* config index */
{
0, /* interrupt ep index */
1, /* ts1 index */
2, /* TS2 index */
3, /* AUDIO */
4, /* VIDEO */
5, /* VANC */
6, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
,
/* full-speed */
{
{
0, /* config index */
{
0, /* interrupt ep index */
1, /* ts1 index */
2, /* TS2 index */
3, /* AUDIO */
4, /* VIDEO */
NOT_SUPPORTED, /* VANC */
NOT_SUPPORTED, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
}
,
{
INDEX_SELFPOWER_COMPRESSOR, /* index */
USB_SELF_POWER, /* power_type */
0, /* speed , not decide yet */
/* mode ,analog tv AND DIGITAL path */
MOD_ANALOG | MOD_DIF | MOD_DIGITAL | MOD_EXTERNAL,
NOT_SUPPORTED, /* ts1_source, disable */
SOURCE_TS_BDA, /* ts2_source */
0, /* analog source,need update */
0, /* digital_index */
0, /* analog index */
0, /* dif_index */
0, /* external_index */
1, /* only one configuration */
{
{
0, /* config index */
{
0, /* interrupt ep index */
NOT_SUPPORTED, /* ts1 index */
1, /* TS2 index */
2, /* AUDIO */
3, /* VIDEO */
4, /* VANC */
5, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
,
/* full-speed */
{
{
0, /* config index */
{
0, /* interrupt ep index */
NOT_SUPPORTED, /* ts1 index */
1, /* TS2 index */
2, /* AUDIO */
3, /* VIDEO */
NOT_SUPPORTED, /* VANC */
NOT_SUPPORTED, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
}
,
{
INDEX_BUSPOWER_DIGITAL_ONLY, /* index */
USB_BUS_POWER, /* power_type */
0, /* speed , not decide yet */
MOD_DIGITAL, /* mode ,analog tv AND DIGITAL path */
SOURCE_TS_BDA, /* ts1_source, disable */
NOT_SUPPORTED, /* ts2_source */
NOT_SUPPORTED, /* analog source */
0, /* digital_index */
0, /* analog index */
0, /* dif_index */
0, /* external_index */
1, /* only one configuration */
{
{
0, /* config index */
{
0, /* interrupt ep index = 2 */
1, /* ts1 index */
NOT_SUPPORTED, /* TS2 index */
NOT_SUPPORTED, /* AUDIO */
NOT_SUPPORTED, /* VIDEO */
NOT_SUPPORTED, /* VANC */
NOT_SUPPORTED, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
,
/* full-speed */
{
{
0, /* config index */
{
0, /* interrupt ep index = 2 */
1, /* ts1 index */
NOT_SUPPORTED, /* TS2 index */
NOT_SUPPORTED, /* AUDIO */
NOT_SUPPORTED, /* VIDEO */
NOT_SUPPORTED, /* VANC */
NOT_SUPPORTED, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
}
,
{
INDEX_BUSPOWER_ANALOG_ONLY, /* index */
USB_BUS_POWER, /* power_type */
0, /* speed , not decide yet */
MOD_ANALOG, /* mode ,analog tv AND DIGITAL path */
NOT_SUPPORTED, /* ts1_source, disable */
NOT_SUPPORTED, /* ts2_source */
SOURCE_ANALOG, /* analog source--analog */
0, /* digital_index */
0, /* analog index */
0, /* dif_index */
0, /* external_index */
1, /* only one configuration */
{
{
0, /* config index */
{
0, /* interrupt ep index */
NOT_SUPPORTED, /* ts1 index */
NOT_SUPPORTED, /* TS2 index */
1, /* AUDIO */
2, /* VIDEO */
3, /* VANC */
4, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
,
{ /* full-speed */
{
0, /* config index */
{
0, /* interrupt ep index */
NOT_SUPPORTED, /* ts1 index */
NOT_SUPPORTED, /* TS2 index */
1, /* AUDIO */
2, /* VIDEO */
NOT_SUPPORTED, /* VANC */
NOT_SUPPORTED, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
}
,
{
INDEX_BUSPOWER_DIF_ONLY, /* index */
USB_BUS_POWER, /* power_type */
0, /* speed , not decide yet */
/* mode ,analog tv AND DIGITAL path */
MOD_DIF | MOD_ANALOG | MOD_DIGITAL | MOD_EXTERNAL,
SOURCE_TS_BDA, /* ts1_source, disable */
NOT_SUPPORTED, /* ts2_source */
SOURCE_DIF | SOURCE_ANALOG | SOURCE_EXTERNAL, /* analog source, dif */
0, /* digital_index */
0, /* analog index */
0, /* dif_index */
0, /* external_index */
1, /* only one configuration */
{
{
0, /* config index */
{
0, /* interrupt ep index */
1, /* ts1 index */
NOT_SUPPORTED, /* TS2 index */
2, /* AUDIO */
3, /* VIDEO */
4, /* VANC */
5, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
,
{ /* full speed */
{
0, /* config index */
{
0, /* interrupt ep index */
1, /* ts1 index */
NOT_SUPPORTED, /* TS2 index */
2, /* AUDIO */
3, /* VIDEO */
NOT_SUPPORTED, /* VANC */
NOT_SUPPORTED, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
}
,
};
/*****************************************************************/
u32 initialize_cx231xx(struct cx231xx *dev)
{
u32 config_info = 0;
struct pcb_config *p_pcb_info;
u8 usb_speed = 1; /* from register,1--HS, 0--FS */
u8 data[4] = { 0, 0, 0, 0 };
u32 ts1_source = 0;
u32 ts2_source = 0;
u32 analog_source = 0;
u8 _current_scenario_idx = 0xff;
ts1_source = SOURCE_TS_BDA;
ts2_source = SOURCE_TS_BDA;
/* read board config register to find out which
pcb config it is related to */
cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, BOARD_CFG_STAT, data, 4);
config_info = *((u32 *) data);
usb_speed = (u8) (config_info & 0x1);
/* Verify this device belongs to Bus power or Self power device */
if (config_info & BUS_POWER) { /* bus-power */
switch (config_info & BUSPOWER_MASK) {
case TS1_PORT | BUS_POWER:
cx231xx_Scenario[INDEX_BUSPOWER_DIGITAL_ONLY].speed =
usb_speed;
p_pcb_info =
&cx231xx_Scenario[INDEX_BUSPOWER_DIGITAL_ONLY];
_current_scenario_idx = INDEX_BUSPOWER_DIGITAL_ONLY;
break;
case AVDEC_ENABLE | BUS_POWER:
cx231xx_Scenario[INDEX_BUSPOWER_ANALOG_ONLY].speed =
usb_speed;
p_pcb_info =
&cx231xx_Scenario[INDEX_BUSPOWER_ANALOG_ONLY];
_current_scenario_idx = INDEX_BUSPOWER_ANALOG_ONLY;
break;
case AVDEC_ENABLE | BUS_POWER | TS1_PORT:
cx231xx_Scenario[INDEX_BUSPOWER_DIF_ONLY].speed =
usb_speed;
p_pcb_info = &cx231xx_Scenario[INDEX_BUSPOWER_DIF_ONLY];
_current_scenario_idx = INDEX_BUSPOWER_DIF_ONLY;
break;
default:
cx231xx_info("bad config in buspower!!!!\n");
cx231xx_info("config_info=%x\n",
(config_info & BUSPOWER_MASK));
return 1;
}
} else { /* self-power */
switch (config_info & SELFPOWER_MASK) {
case TS1_PORT | SELF_POWER:
cx231xx_Scenario[INDEX_SELFPOWER_DIGITAL_ONLY].speed =
usb_speed;
p_pcb_info =
&cx231xx_Scenario[INDEX_SELFPOWER_DIGITAL_ONLY];
_current_scenario_idx = INDEX_SELFPOWER_DIGITAL_ONLY;
break;
case TS1_TS2_PORT | SELF_POWER:
cx231xx_Scenario[INDEX_SELFPOWER_DUAL_DIGITAL].speed =
usb_speed;
cx231xx_Scenario[INDEX_SELFPOWER_DUAL_DIGITAL].
ts2_source = ts2_source;
p_pcb_info =
&cx231xx_Scenario[INDEX_SELFPOWER_DUAL_DIGITAL];
_current_scenario_idx = INDEX_SELFPOWER_DUAL_DIGITAL;
break;
case AVDEC_ENABLE | SELF_POWER:
cx231xx_Scenario[INDEX_SELFPOWER_ANALOG_ONLY].speed =
usb_speed;
cx231xx_Scenario[INDEX_SELFPOWER_ANALOG_ONLY].
analog_source = analog_source;
p_pcb_info =
&cx231xx_Scenario[INDEX_SELFPOWER_ANALOG_ONLY];
_current_scenario_idx = INDEX_SELFPOWER_ANALOG_ONLY;
break;
case AVDEC_ENABLE | TS1_PORT | SELF_POWER:
cx231xx_Scenario[INDEX_SELFPOWER_DUAL].speed =
usb_speed;
cx231xx_Scenario[INDEX_SELFPOWER_DUAL].ts1_source =
ts1_source;
cx231xx_Scenario[INDEX_SELFPOWER_DUAL].analog_source =
analog_source;
p_pcb_info = &cx231xx_Scenario[INDEX_SELFPOWER_DUAL];
_current_scenario_idx = INDEX_SELFPOWER_DUAL;
break;
case AVDEC_ENABLE | TS1_TS2_PORT | SELF_POWER:
cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].speed =
usb_speed;
cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].ts1_source =
ts1_source;
cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].ts2_source =
ts2_source;
cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].analog_source =
analog_source;
p_pcb_info = &cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE];
_current_scenario_idx = INDEX_SELFPOWER_TRIPLE;
break;
case AVDEC_ENABLE | TS1VIP_TS2_PORT | SELF_POWER:
cx231xx_Scenario[INDEX_SELFPOWER_COMPRESSOR].speed =
usb_speed;
cx231xx_Scenario[INDEX_SELFPOWER_COMPRESSOR].
analog_source = analog_source;
p_pcb_info =
&cx231xx_Scenario[INDEX_SELFPOWER_COMPRESSOR];
_current_scenario_idx = INDEX_SELFPOWER_COMPRESSOR;
break;
default:
cx231xx_info("bad senario!!!!!\n");
cx231xx_info("config_info=%x\n",
(config_info & SELFPOWER_MASK));
return 1;
}
}
dev->current_scenario_idx = _current_scenario_idx;
memcpy(&dev->current_pcb_config, p_pcb_info,
sizeof(struct pcb_config));
if (pcb_debug) {
cx231xx_info("SC(0x00) register = 0x%x\n", config_info);
cx231xx_info("scenario %d\n",
(dev->current_pcb_config.index) + 1);
cx231xx_info("type=%x\n", dev->current_pcb_config.type);
cx231xx_info("mode=%x\n", dev->current_pcb_config.mode);
cx231xx_info("speed=%x\n", dev->current_pcb_config.speed);
cx231xx_info("ts1_source=%x\n",
dev->current_pcb_config.ts1_source);
cx231xx_info("ts2_source=%x\n",
dev->current_pcb_config.ts2_source);
cx231xx_info("analog_source=%x\n",
dev->current_pcb_config.analog_source);
}
return 0;
}

View File

@@ -0,0 +1,231 @@
/*
cx231xx-pcb-cfg.h - driver for Conexant
Cx23100/101/102 USB video capture devices
Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
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.
*/
#ifndef _PCB_CONFIG_H_
#define _PCB_CONFIG_H_
#include <linux/init.h>
#include <linux/module.h>
/***************************************************************************
* Class Information *
***************************************************************************/
#define CLASS_DEFAULT 0xFF
enum VENDOR_REQUEST_TYPE {
/* Set/Get I2C */
VRT_SET_I2C0 = 0x0,
VRT_SET_I2C1 = 0x1,
VRT_SET_I2C2 = 0x2,
VRT_GET_I2C0 = 0x4,
VRT_GET_I2C1 = 0x5,
VRT_GET_I2C2 = 0x6,
/* Set/Get GPIO */
VRT_SET_GPIO = 0x8,
VRT_GET_GPIO = 0x9,
/* Set/Get GPIE */
VRT_SET_GPIE = 0xA,
VRT_GET_GPIE = 0xB,
/* Set/Get Register Control/Status */
VRT_SET_REGISTER = 0xC,
VRT_GET_REGISTER = 0xD,
/* Get Extended Compat ID Descriptor */
VRT_GET_EXTCID_DESC = 0xFF,
};
enum BYTE_ENABLE_MASK {
ENABLE_ONE_BYTE = 0x1,
ENABLE_TWE_BYTE = 0x3,
ENABLE_THREE_BYTE = 0x7,
ENABLE_FOUR_BYTE = 0xF,
};
#define SPEED_MASK 0x1
enum USB_SPEED{
FULL_SPEED = 0x0, /* 0: full speed */
HIGH_SPEED = 0x1 /* 1: high speed */
};
enum _true_false{
FALSE = 0,
TRUE = 1
};
#define TS_MASK 0x6
enum TS_PORT{
NO_TS_PORT = 0x0, /* 2'b00: Neither port used. PCB not a Hybrid,
only offers Analog TV or Video */
TS1_PORT = 0x4, /* 2'b10: TS1 Input (Hybrid mode :
Digital or External Analog/Compressed source) */
TS1_TS2_PORT = 0x6, /* 2'b11: TS1 & TS2 Inputs
(Dual inputs from Digital and/or
External Analog/Compressed sources) */
TS1_EXT_CLOCK = 0x6, /* 2'b11: TS1 & TS2 as selector
to external clock */
TS1VIP_TS2_PORT = 0x2 /* 2'b01: TS1 used as 656/VIP Output,
TS2 Input (from Compressor) */
};
#define EAVP_MASK 0x8
enum EAV_PRESENT{
NO_EXTERNAL_AV = 0x0, /* 0: No External A/V inputs
(no need for i2s blcok),
Analog Tuner must be present */
EXTERNAL_AV = 0x8 /* 1: External A/V inputs
present (requires i2s blk) */
};
#define ATM_MASK 0x30
enum AT_MODE{
DIF_TUNER = 0x30, /* 2'b11: IF Tuner (requires use of DIF) */
BASEBAND_SOUND = 0x20, /* 2'b10: Baseband Composite &
Sound-IF Signals present */
NO_TUNER = 0x10 /* 2'b0x: No Analog Tuner present */
};
#define PWR_SEL_MASK 0x40
enum POWE_TYPE{
SELF_POWER = 0x0, /* 0: self power */
BUS_POWER = 0x40 /* 1: bus power */
};
enum USB_POWE_TYPE{
USB_SELF_POWER = 0,
USB_BUS_POWER
};
#define BO_0_MASK 0x80
enum AVDEC_STATUS{
AVDEC_DISABLE = 0x0, /* 0: A/V Decoder Disabled */
AVDEC_ENABLE = 0x80 /* 1: A/V Decoder Enabled */
};
#define BO_1_MASK 0x100
#define BUSPOWER_MASK 0xC4 /* for Polaris spec 0.8 */
#define SELFPOWER_MASK 0x86
/***************************************************************************/
#define NOT_DECIDE_YET 0xFE
#define NOT_SUPPORTED 0xFF
/***************************************************************************
* for mod field use *
***************************************************************************/
#define MOD_DIGITAL 0x1
#define MOD_ANALOG 0x2
#define MOD_DIF 0x4
#define MOD_EXTERNAL 0x8
#define CAP_ALL_MOD 0x0f
/***************************************************************************
* source define *
***************************************************************************/
#define SOURCE_DIGITAL 0x1
#define SOURCE_ANALOG 0x2
#define SOURCE_DIF 0x4
#define SOURCE_EXTERNAL 0x8
#define SOURCE_TS_BDA 0x10
#define SOURCE_TS_ENCODE 0x20
#define SOURCE_TS_EXTERNAL 0x40
/***************************************************************************
* interface information define *
***************************************************************************/
struct INTERFACE_INFO {
u8 interrupt_index;
u8 ts1_index;
u8 ts2_index;
u8 audio_index;
u8 video_index;
u8 vanc_index; /* VBI */
u8 hanc_index; /* Sliced CC */
u8 ir_index;
};
enum INDEX_INTERFACE_INFO{
INDEX_INTERRUPT = 0x0,
INDEX_TS1,
INDEX_TS2,
INDEX_AUDIO,
INDEX_VIDEO,
INDEX_VANC,
INDEX_HANC,
INDEX_IR,
};
/***************************************************************************
* configuration information define *
***************************************************************************/
struct CONFIG_INFO {
u8 config_index;
struct INTERFACE_INFO interface_info;
};
struct pcb_config {
u8 index;
u8 type; /* bus power or self power,
self power--0, bus_power--1 */
u8 speed; /* usb speed, 2.0--1, 1.1--0 */
u8 mode; /* digital , anlog, dif or external A/V */
u32 ts1_source; /* three source -- BDA,External,encode */
u32 ts2_source;
u32 analog_source;
u8 digital_index; /* bus-power used */
u8 analog_index; /* bus-power used */
u8 dif_index; /* bus-power used */
u8 external_index; /* bus-power used */
u8 config_num; /* current config num, 0,1,2,
for self-power, always 0 */
struct CONFIG_INFO hs_config_info[3];
struct CONFIG_INFO fs_config_info[3];
};
enum INDEX_PCB_CONFIG{
INDEX_SELFPOWER_DIGITAL_ONLY = 0x0,
INDEX_SELFPOWER_DUAL_DIGITAL,
INDEX_SELFPOWER_ANALOG_ONLY,
INDEX_SELFPOWER_DUAL,
INDEX_SELFPOWER_TRIPLE,
INDEX_SELFPOWER_COMPRESSOR,
INDEX_BUSPOWER_DIGITAL_ONLY,
INDEX_BUSPOWER_ANALOG_ONLY,
INDEX_BUSPOWER_DIF_ONLY,
INDEX_BUSPOWER_EXTERNAL_ONLY,
INDEX_BUSPOWER_EXTERNAL_ANALOG,
INDEX_BUSPOWER_EXTERNAL_DIF,
INDEX_BUSPOWER_EXTERNAL_DIGITAL,
INDEX_BUSPOWER_DIGITAL_ANALOG,
INDEX_BUSPOWER_DIGITAL_DIF,
INDEX_BUSPOWER_DIGITAL_ANALOG_EXTERNAL,
INDEX_BUSPOWER_DIGITAL_DIF_EXTERNAL,
};
/***************************************************************************/
struct cx231xx;
u32 initialize_cx231xx(struct cx231xx *p_dev);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,700 @@
/*
cx231xx_vbi.c - driver for Conexant Cx23100/101/102 USB video capture devices
Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
Based on cx88 driver
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/kernel.h>
#include <linux/bitmap.h>
#include <linux/usb.h>
#include <linux/i2c.h>
#include <linux/mm.h>
#include <linux/mutex.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-chip-ident.h>
#include <media/msp3400.h>
#include <media/tuner.h>
#include "cx231xx.h"
#include "cx231xx-vbi.h"
static inline void print_err_status(struct cx231xx *dev, int packet, int status)
{
char *errmsg = "Unknown";
switch (status) {
case -ENOENT:
errmsg = "unlinked synchronuously";
break;
case -ECONNRESET:
errmsg = "unlinked asynchronuously";
break;
case -ENOSR:
errmsg = "Buffer error (overrun)";
break;
case -EPIPE:
errmsg = "Stalled (device not responding)";
break;
case -EOVERFLOW:
errmsg = "Babble (bad cable?)";
break;
case -EPROTO:
errmsg = "Bit-stuff error (bad cable?)";
break;
case -EILSEQ:
errmsg = "CRC/Timeout (could be anything)";
break;
case -ETIME:
errmsg = "Device does not respond";
break;
}
if (packet < 0) {
cx231xx_err(DRIVER_NAME "URB status %d [%s].\n", status,
errmsg);
} else {
cx231xx_err(DRIVER_NAME "URB packet %d, status %d [%s].\n",
packet, status, errmsg);
}
}
/*
* Controls the isoc copy of each urb packet
*/
static inline int cx231xx_isoc_vbi_copy(struct cx231xx *dev, struct urb *urb)
{
struct cx231xx_buffer *buf;
struct cx231xx_dmaqueue *dma_q = urb->context;
int rc = 1;
unsigned char *p_buffer;
u32 bytes_parsed = 0, buffer_size = 0;
u8 sav_eav = 0;
if (!dev)
return 0;
if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED))
return 0;
if (urb->status < 0) {
print_err_status(dev, -1, urb->status);
if (urb->status == -ENOENT)
return 0;
}
buf = dev->vbi_mode.isoc_ctl.buf;
/* get buffer pointer and length */
p_buffer = urb->transfer_buffer;
buffer_size = urb->actual_length;
if (buffer_size > 0) {
bytes_parsed = 0;
if (dma_q->is_partial_line) {
/* Handle the case where we were working on a partial
line */
sav_eav = dma_q->last_sav;
} else {
/* Check for a SAV/EAV overlapping the
buffer boundary */
sav_eav = cx231xx_find_boundary_SAV_EAV(p_buffer,
dma_q->partial_buf,
&bytes_parsed);
}
sav_eav &= 0xF0;
/* Get the first line if we have some portion of an SAV/EAV from
the last buffer or a partial line */
if (sav_eav) {
bytes_parsed += cx231xx_get_vbi_line(dev, dma_q,
sav_eav, /* SAV/EAV */
p_buffer + bytes_parsed, /* p_buffer */
buffer_size - bytes_parsed); /* buffer size */
}
/* Now parse data that is completely in this buffer */
dma_q->is_partial_line = 0;
while (bytes_parsed < buffer_size) {
u32 bytes_used = 0;
sav_eav = cx231xx_find_next_SAV_EAV(
p_buffer + bytes_parsed, /* p_buffer */
buffer_size - bytes_parsed, /* buffer size */
&bytes_used); /* bytes used to get SAV/EAV */
bytes_parsed += bytes_used;
sav_eav &= 0xF0;
if (sav_eav && (bytes_parsed < buffer_size)) {
bytes_parsed += cx231xx_get_vbi_line(dev,
dma_q, sav_eav, /* SAV/EAV */
p_buffer+bytes_parsed, /* p_buffer */
buffer_size-bytes_parsed);/*buf size*/
}
}
/* Save the last four bytes of the buffer so we can
check the buffer boundary condition next time */
memcpy(dma_q->partial_buf, p_buffer + buffer_size - 4, 4);
bytes_parsed = 0;
}
return rc;
}
/* ------------------------------------------------------------------
Vbi buf operations
------------------------------------------------------------------*/
static int
vbi_buffer_setup(struct videobuf_queue *vq, unsigned int *count,
unsigned int *size)
{
struct cx231xx_fh *fh = vq->priv_data;
struct cx231xx *dev = fh->dev;
u32 height = 0;
height = ((dev->norm & V4L2_STD_625_50) ?
PAL_VBI_LINES : NTSC_VBI_LINES);
*size = (dev->width * height * 2);
if (0 == *count)
*count = CX231XX_DEF_VBI_BUF;
if (*count < CX231XX_MIN_BUF)
*count = CX231XX_MIN_BUF;
return 0;
}
/* This is called *without* dev->slock held; please keep it that way */
static void free_buffer(struct videobuf_queue *vq, struct cx231xx_buffer *buf)
{
struct cx231xx_fh *fh = vq->priv_data;
struct cx231xx *dev = fh->dev;
unsigned long flags = 0;
if (in_interrupt())
BUG();
/* We used to wait for the buffer to finish here, but this didn't work
because, as we were keeping the state as VIDEOBUF_QUEUED,
videobuf_queue_cancel marked it as finished for us.
(Also, it could wedge forever if the hardware was misconfigured.)
This should be safe; by the time we get here, the buffer isn't
queued anymore. If we ever start marking the buffers as
VIDEOBUF_ACTIVE, it won't be, though.
*/
spin_lock_irqsave(&dev->vbi_mode.slock, flags);
if (dev->vbi_mode.isoc_ctl.buf == buf)
dev->vbi_mode.isoc_ctl.buf = NULL;
spin_unlock_irqrestore(&dev->vbi_mode.slock, flags);
videobuf_vmalloc_free(&buf->vb);
buf->vb.state = VIDEOBUF_NEEDS_INIT;
}
static int
vbi_buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
enum v4l2_field field)
{
struct cx231xx_fh *fh = vq->priv_data;
struct cx231xx_buffer *buf =
container_of(vb, struct cx231xx_buffer, vb);
struct cx231xx *dev = fh->dev;
int rc = 0, urb_init = 0;
u32 height = 0;
height = ((dev->norm & V4L2_STD_625_50) ?
PAL_VBI_LINES : NTSC_VBI_LINES);
buf->vb.size = ((dev->width << 1) * height);
if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
return -EINVAL;
buf->vb.width = dev->width;
buf->vb.height = height;
buf->vb.field = field;
buf->vb.field = V4L2_FIELD_SEQ_TB;
if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
rc = videobuf_iolock(vq, &buf->vb, NULL);
if (rc < 0)
goto fail;
}
if (!dev->vbi_mode.isoc_ctl.num_bufs)
urb_init = 1;
if (urb_init) {
rc = cx231xx_init_vbi_isoc(dev, CX231XX_NUM_VBI_PACKETS,
CX231XX_NUM_VBI_BUFS,
dev->vbi_mode.alt_max_pkt_size[0],
cx231xx_isoc_vbi_copy);
if (rc < 0)
goto fail;
}
buf->vb.state = VIDEOBUF_PREPARED;
return 0;
fail:
free_buffer(vq, buf);
return rc;
}
static void
vbi_buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
{
struct cx231xx_buffer *buf =
container_of(vb, struct cx231xx_buffer, vb);
struct cx231xx_fh *fh = vq->priv_data;
struct cx231xx *dev = fh->dev;
struct cx231xx_dmaqueue *vidq = &dev->vbi_mode.vidq;
buf->vb.state = VIDEOBUF_QUEUED;
list_add_tail(&buf->vb.queue, &vidq->active);
}
static void vbi_buffer_release(struct videobuf_queue *vq,
struct videobuf_buffer *vb)
{
struct cx231xx_buffer *buf =
container_of(vb, struct cx231xx_buffer, vb);
free_buffer(vq, buf);
}
struct videobuf_queue_ops cx231xx_vbi_qops = {
.buf_setup = vbi_buffer_setup,
.buf_prepare = vbi_buffer_prepare,
.buf_queue = vbi_buffer_queue,
.buf_release = vbi_buffer_release,
};
/* ------------------------------------------------------------------
URB control
------------------------------------------------------------------*/
/*
* IRQ callback, called by URB callback
*/
static void cx231xx_irq_vbi_callback(struct urb *urb)
{
struct cx231xx_dmaqueue *dma_q = urb->context;
struct cx231xx_video_mode *vmode =
container_of(dma_q, struct cx231xx_video_mode, vidq);
struct cx231xx *dev = container_of(vmode, struct cx231xx, vbi_mode);
int rc;
switch (urb->status) {
case 0: /* success */
case -ETIMEDOUT: /* NAK */
break;
case -ECONNRESET: /* kill */
case -ENOENT:
case -ESHUTDOWN:
return;
default: /* error */
cx231xx_err(DRIVER_NAME "urb completition error %d.\n",
urb->status);
break;
}
/* Copy data from URB */
spin_lock(&dev->vbi_mode.slock);
rc = dev->vbi_mode.isoc_ctl.isoc_copy(dev, urb);
spin_unlock(&dev->vbi_mode.slock);
/* Reset status */
urb->status = 0;
urb->status = usb_submit_urb(urb, GFP_ATOMIC);
if (urb->status) {
cx231xx_err(DRIVER_NAME "urb resubmit failed (error=%i)\n",
urb->status);
}
}
/*
* Stop and Deallocate URBs
*/
void cx231xx_uninit_vbi_isoc(struct cx231xx *dev)
{
struct urb *urb;
int i;
cx231xx_info(DRIVER_NAME "cx231xx: called cx231xx_uninit_vbi_isoc\n");
dev->vbi_mode.isoc_ctl.nfields = -1;
for (i = 0; i < dev->vbi_mode.isoc_ctl.num_bufs; i++) {
urb = dev->vbi_mode.isoc_ctl.urb[i];
if (urb) {
if (!irqs_disabled())
usb_kill_urb(urb);
else
usb_unlink_urb(urb);
if (dev->vbi_mode.isoc_ctl.transfer_buffer[i]) {
kfree(dev->vbi_mode.isoc_ctl.
transfer_buffer[i]);
dev->vbi_mode.isoc_ctl.transfer_buffer[i] =
NULL;
}
usb_free_urb(urb);
dev->vbi_mode.isoc_ctl.urb[i] = NULL;
}
dev->vbi_mode.isoc_ctl.transfer_buffer[i] = NULL;
}
kfree(dev->vbi_mode.isoc_ctl.urb);
kfree(dev->vbi_mode.isoc_ctl.transfer_buffer);
dev->vbi_mode.isoc_ctl.urb = NULL;
dev->vbi_mode.isoc_ctl.transfer_buffer = NULL;
dev->vbi_mode.isoc_ctl.num_bufs = 0;
cx231xx_capture_start(dev, 0, Vbi);
}
EXPORT_SYMBOL_GPL(cx231xx_uninit_vbi_isoc);
/*
* Allocate URBs and start IRQ
*/
int cx231xx_init_vbi_isoc(struct cx231xx *dev, int max_packets,
int num_bufs, int max_pkt_size,
int (*isoc_copy) (struct cx231xx *dev,
struct urb *urb))
{
struct cx231xx_dmaqueue *dma_q = &dev->vbi_mode.vidq;
int i;
int sb_size, pipe;
struct urb *urb;
int rc;
cx231xx_info(DRIVER_NAME "cx231xx: called cx231xx_prepare_isoc\n");
/* De-allocates all pending stuff */
cx231xx_uninit_vbi_isoc(dev);
/* clear if any halt */
usb_clear_halt(dev->udev,
usb_rcvbulkpipe(dev->udev,
dev->vbi_mode.end_point_addr));
dev->vbi_mode.isoc_ctl.isoc_copy = isoc_copy;
dev->vbi_mode.isoc_ctl.num_bufs = num_bufs;
dma_q->pos = 0;
dma_q->is_partial_line = 0;
dma_q->last_sav = 0;
dma_q->current_field = -1;
dma_q->bytes_left_in_line = dev->width << 1;
dma_q->lines_per_field = ((dev->norm & V4L2_STD_625_50) ?
PAL_VBI_LINES : NTSC_VBI_LINES);
dma_q->lines_completed = 0;
for (i = 0; i < 8; i++)
dma_q->partial_buf[i] = 0;
dev->vbi_mode.isoc_ctl.urb = kzalloc(sizeof(void *) * num_bufs,
GFP_KERNEL);
if (!dev->vbi_mode.isoc_ctl.urb) {
cx231xx_errdev("cannot alloc memory for usb buffers\n");
return -ENOMEM;
}
dev->vbi_mode.isoc_ctl.transfer_buffer =
kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL);
if (!dev->vbi_mode.isoc_ctl.transfer_buffer) {
cx231xx_errdev("cannot allocate memory for usbtransfer\n");
kfree(dev->vbi_mode.isoc_ctl.urb);
return -ENOMEM;
}
dev->vbi_mode.isoc_ctl.max_pkt_size = max_pkt_size;
dev->vbi_mode.isoc_ctl.buf = NULL;
sb_size = max_packets * dev->vbi_mode.isoc_ctl.max_pkt_size;
/* allocate urbs and transfer buffers */
for (i = 0; i < dev->vbi_mode.isoc_ctl.num_bufs; i++) {
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb) {
cx231xx_err(DRIVER_NAME
": cannot alloc isoc_ctl.urb %i\n", i);
cx231xx_uninit_vbi_isoc(dev);
return -ENOMEM;
}
dev->vbi_mode.isoc_ctl.urb[i] = urb;
urb->transfer_flags = 0;
dev->vbi_mode.isoc_ctl.transfer_buffer[i] =
kzalloc(sb_size, GFP_KERNEL);
if (!dev->vbi_mode.isoc_ctl.transfer_buffer[i]) {
cx231xx_err(DRIVER_NAME
": unable to allocate %i bytes for transfer"
" buffer %i%s\n", sb_size, i,
in_interrupt() ? " while in int" : "");
cx231xx_uninit_vbi_isoc(dev);
return -ENOMEM;
}
pipe = usb_rcvbulkpipe(dev->udev, dev->vbi_mode.end_point_addr);
usb_fill_bulk_urb(urb, dev->udev, pipe,
dev->vbi_mode.isoc_ctl.transfer_buffer[i],
sb_size, cx231xx_irq_vbi_callback, dma_q);
}
init_waitqueue_head(&dma_q->wq);
/* submit urbs and enables IRQ */
for (i = 0; i < dev->vbi_mode.isoc_ctl.num_bufs; i++) {
rc = usb_submit_urb(dev->vbi_mode.isoc_ctl.urb[i], GFP_ATOMIC);
if (rc) {
cx231xx_err(DRIVER_NAME
": submit of urb %i failed (error=%i)\n", i,
rc);
cx231xx_uninit_vbi_isoc(dev);
return rc;
}
}
cx231xx_capture_start(dev, 1, Vbi);
return 0;
}
EXPORT_SYMBOL_GPL(cx231xx_init_vbi_isoc);
u32 cx231xx_get_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
u8 sav_eav, u8 *p_buffer, u32 buffer_size)
{
u32 bytes_copied = 0;
int current_field = -1;
switch (sav_eav) {
case SAV_VBI_FIELD1:
current_field = 1;
break;
case SAV_VBI_FIELD2:
current_field = 2;
break;
default:
break;
}
if (current_field < 0)
return bytes_copied;
dma_q->last_sav = sav_eav;
bytes_copied =
cx231xx_copy_vbi_line(dev, dma_q, p_buffer, buffer_size,
current_field);
return bytes_copied;
}
/*
* Announces that a buffer were filled and request the next
*/
static inline void vbi_buffer_filled(struct cx231xx *dev,
struct cx231xx_dmaqueue *dma_q,
struct cx231xx_buffer *buf)
{
/* Advice that buffer was filled */
/* cx231xx_info(DRIVER_NAME "[%p/%d] wakeup\n", buf, buf->vb.i); */
buf->vb.state = VIDEOBUF_DONE;
buf->vb.field_count++;
do_gettimeofday(&buf->vb.ts);
dev->vbi_mode.isoc_ctl.buf = NULL;
list_del(&buf->vb.queue);
wake_up(&buf->vb.done);
}
u32 cx231xx_copy_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
u8 *p_line, u32 length, int field_number)
{
u32 bytes_to_copy;
struct cx231xx_buffer *buf;
u32 _line_size = dev->width * 2;
if (dma_q->current_field != field_number)
cx231xx_reset_vbi_buffer(dev, dma_q);
/* get the buffer pointer */
buf = dev->vbi_mode.isoc_ctl.buf;
/* Remember the field number for next time */
dma_q->current_field = field_number;
bytes_to_copy = dma_q->bytes_left_in_line;
if (bytes_to_copy > length)
bytes_to_copy = length;
if (dma_q->lines_completed >= dma_q->lines_per_field) {
dma_q->bytes_left_in_line -= bytes_to_copy;
dma_q->is_partial_line =
(dma_q->bytes_left_in_line == 0) ? 0 : 1;
return 0;
}
dma_q->is_partial_line = 1;
/* If we don't have a buffer, just return the number of bytes we would
have copied if we had a buffer. */
if (!buf) {
dma_q->bytes_left_in_line -= bytes_to_copy;
dma_q->is_partial_line =
(dma_q->bytes_left_in_line == 0) ? 0 : 1;
return bytes_to_copy;
}
/* copy the data to video buffer */
cx231xx_do_vbi_copy(dev, dma_q, p_line, bytes_to_copy);
dma_q->pos += bytes_to_copy;
dma_q->bytes_left_in_line -= bytes_to_copy;
if (dma_q->bytes_left_in_line == 0) {
dma_q->bytes_left_in_line = _line_size;
dma_q->lines_completed++;
dma_q->is_partial_line = 0;
if (cx231xx_is_vbi_buffer_done(dev, dma_q) && buf) {
vbi_buffer_filled(dev, dma_q, buf);
dma_q->pos = 0;
buf = NULL;
dma_q->lines_completed = 0;
}
}
return bytes_to_copy;
}
/*
* video-buf generic routine to get the next available buffer
*/
static inline void get_next_vbi_buf(struct cx231xx_dmaqueue *dma_q,
struct cx231xx_buffer **buf)
{
struct cx231xx_video_mode *vmode =
container_of(dma_q, struct cx231xx_video_mode, vidq);
struct cx231xx *dev = container_of(vmode, struct cx231xx, vbi_mode);
char *outp;
if (list_empty(&dma_q->active)) {
cx231xx_err(DRIVER_NAME ": No active queue to serve\n");
dev->vbi_mode.isoc_ctl.buf = NULL;
*buf = NULL;
return;
}
/* Get the next buffer */
*buf = list_entry(dma_q->active.next, struct cx231xx_buffer, vb.queue);
/* Cleans up buffer - Usefull for testing for frame/URB loss */
outp = videobuf_to_vmalloc(&(*buf)->vb);
memset(outp, 0, (*buf)->vb.size);
dev->vbi_mode.isoc_ctl.buf = *buf;
return;
}
void cx231xx_reset_vbi_buffer(struct cx231xx *dev,
struct cx231xx_dmaqueue *dma_q)
{
struct cx231xx_buffer *buf;
buf = dev->vbi_mode.isoc_ctl.buf;
if (buf == NULL) {
/* first try to get the buffer */
get_next_vbi_buf(dma_q, &buf);
dma_q->pos = 0;
dma_q->current_field = -1;
}
dma_q->bytes_left_in_line = dev->width << 1;
dma_q->lines_completed = 0;
}
int cx231xx_do_vbi_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
u8 *p_buffer, u32 bytes_to_copy)
{
u8 *p_out_buffer = NULL;
u32 current_line_bytes_copied = 0;
struct cx231xx_buffer *buf;
u32 _line_size = dev->width << 1;
void *startwrite;
int offset, lencopy;
buf = dev->vbi_mode.isoc_ctl.buf;
if (buf == NULL)
return -EINVAL;
p_out_buffer = videobuf_to_vmalloc(&buf->vb);
if (dma_q->bytes_left_in_line != _line_size) {
current_line_bytes_copied =
_line_size - dma_q->bytes_left_in_line;
}
offset = (dma_q->lines_completed * _line_size) +
current_line_bytes_copied;
/* prepare destination address */
startwrite = p_out_buffer + offset;
lencopy = dma_q->bytes_left_in_line > bytes_to_copy ?
bytes_to_copy : dma_q->bytes_left_in_line;
memcpy(startwrite, p_buffer, lencopy);
return 0;
}
u8 cx231xx_is_vbi_buffer_done(struct cx231xx *dev,
struct cx231xx_dmaqueue *dma_q)
{
u32 height = 0;
height = ((dev->norm & V4L2_STD_625_50) ?
PAL_VBI_LINES : NTSC_VBI_LINES);
return (dma_q->lines_completed == height) ? 1 : 0;
}

View File

@@ -0,0 +1,65 @@
/*
cx231xx_vbi.h - driver for Conexant Cx23100/101/102 USB video capture devices
Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
Based on cx88 driver
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.
*/
#ifndef _CX231XX_VBI_H
#define _CX231XX_VBI_H
extern struct videobuf_queue_ops cx231xx_vbi_qops;
#define NTSC_VBI_START_LINE 10 /* line 10 - 21 */
#define NTSC_VBI_END_LINE 21
#define NTSC_VBI_LINES (NTSC_VBI_END_LINE-NTSC_VBI_START_LINE+1)
#define PAL_VBI_START_LINE 6
#define PAL_VBI_END_LINE 23
#define PAL_VBI_LINES (PAL_VBI_END_LINE-PAL_VBI_START_LINE+1)
#define VBI_STRIDE 1440
#define VBI_SAMPLES_PER_LINE 1440
#define CX231XX_NUM_VBI_PACKETS 4
#define CX231XX_NUM_VBI_BUFS 5
/* stream functions */
int cx231xx_init_vbi_isoc(struct cx231xx *dev, int max_packets,
int num_bufs, int max_pkt_size,
int (*isoc_copy) (struct cx231xx *dev,
struct urb *urb));
void cx231xx_uninit_vbi_isoc(struct cx231xx *dev);
/* vbi data copy functions */
u32 cx231xx_get_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
u8 sav_eav, u8 *p_buffer, u32 buffer_size);
u32 cx231xx_copy_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
u8 *p_line, u32 length, int field_number);
void cx231xx_reset_vbi_buffer(struct cx231xx *dev,
struct cx231xx_dmaqueue *dma_q);
int cx231xx_do_vbi_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
u8 *p_buffer, u32 bytes_to_copy);
u8 cx231xx_is_vbi_buffer_done(struct cx231xx *dev,
struct cx231xx_dmaqueue *dma_q);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,776 @@
/*
cx231xx.h - driver for Conexant Cx23100/101/102 USB video capture devices
Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
Based on em28xx driver
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.
*/
#ifndef _CX231XX_H
#define _CX231XX_H
#include <linux/videodev2.h>
#include <linux/types.h>
#include <linux/ioctl.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <linux/mutex.h>
#include <media/videobuf-vmalloc.h>
#include <media/v4l2-device.h>
#include <media/ir-kbd-i2c.h>
#if defined(CONFIG_VIDEO_CX231XX_DVB) || \
defined(CONFIG_VIDEO_CX231XX_DVB_MODULE)
#include <media/videobuf-dvb.h>
#endif
#include "cx231xx-reg.h"
#include "cx231xx-pcb-cfg.h"
#include "cx231xx-conf-reg.h"
#define DRIVER_NAME "cx231xx"
#define PWR_SLEEP_INTERVAL 5
/* I2C addresses for control block in Cx231xx */
#define AFE_DEVICE_ADDRESS 0x60
#define I2S_BLK_DEVICE_ADDRESS 0x98
#define VID_BLK_I2C_ADDRESS 0x88
#define DIF_USE_BASEBAND 0xFFFFFFFF
/* Boards supported by driver */
#define CX231XX_BOARD_UNKNOWN 0
#define CX231XX_BOARD_CNXT_RDE_250 1
#define CX231XX_BOARD_CNXT_RDU_250 2
/* Limits minimum and default number of buffers */
#define CX231XX_MIN_BUF 4
#define CX231XX_DEF_BUF 12
#define CX231XX_DEF_VBI_BUF 6
#define VBI_LINE_COUNT 17
#define VBI_LINE_LENGTH 1440
/*Limits the max URB message size */
#define URB_MAX_CTRL_SIZE 80
/* Params for validated field */
#define CX231XX_BOARD_NOT_VALIDATED 1
#define CX231XX_BOARD_VALIDATED 0
/* maximum number of cx231xx boards */
#define CX231XX_MAXBOARDS 8
/* maximum number of frames that can be queued */
#define CX231XX_NUM_FRAMES 5
/* number of buffers for isoc transfers */
#define CX231XX_NUM_BUFS 8
/* number of packets for each buffer
windows requests only 40 packets .. so we better do the same
this is what I found out for all alternate numbers there!
*/
#define CX231XX_NUM_PACKETS 40
/* default alternate; 0 means choose the best */
#define CX231XX_PINOUT 0
#define CX231XX_INTERLACED_DEFAULT 1
/* time to wait when stopping the isoc transfer */
#define CX231XX_URB_TIMEOUT \
msecs_to_jiffies(CX231XX_NUM_BUFS * CX231XX_NUM_PACKETS)
enum cx231xx_mode {
CX231XX_SUSPEND,
CX231XX_ANALOG_MODE,
CX231XX_DIGITAL_MODE,
};
enum cx231xx_std_mode {
CX231XX_TV_AIR = 0,
CX231XX_TV_CABLE
};
enum cx231xx_stream_state {
STREAM_OFF,
STREAM_INTERRUPT,
STREAM_ON,
};
struct cx231xx;
struct cx231xx_usb_isoc_ctl {
/* max packet size of isoc transaction */
int max_pkt_size;
/* number of allocated urbs */
int num_bufs;
/* urb for isoc transfers */
struct urb **urb;
/* transfer buffers for isoc transfer */
char **transfer_buffer;
/* Last buffer command and region */
u8 cmd;
int pos, size, pktsize;
/* Last field: ODD or EVEN? */
int field;
/* Stores incomplete commands */
u32 tmp_buf;
int tmp_buf_len;
/* Stores already requested buffers */
struct cx231xx_buffer *buf;
/* Stores the number of received fields */
int nfields;
/* isoc urb callback */
int (*isoc_copy) (struct cx231xx *dev, struct urb *urb);
};
struct cx231xx_fmt {
char *name;
u32 fourcc; /* v4l2 format id */
int depth;
int reg;
};
/* buffer for one video frame */
struct cx231xx_buffer {
/* common v4l buffer stuff -- must be first */
struct videobuf_buffer vb;
struct list_head frame;
int top_field;
int receiving;
};
struct cx231xx_dmaqueue {
struct list_head active;
struct list_head queued;
wait_queue_head_t wq;
/* Counters to control buffer fill */
int pos;
u8 is_partial_line;
u8 partial_buf[8];
u8 last_sav;
int current_field;
u32 bytes_left_in_line;
u32 lines_completed;
u8 field1_done;
u32 lines_per_field;
};
/* inputs */
#define MAX_CX231XX_INPUT 4
enum cx231xx_itype {
CX231XX_VMUX_COMPOSITE1 = 1,
CX231XX_VMUX_SVIDEO,
CX231XX_VMUX_TELEVISION,
CX231XX_VMUX_CABLE,
CX231XX_RADIO,
CX231XX_VMUX_DVB,
CX231XX_VMUX_DEBUG
};
enum cx231xx_v_input {
CX231XX_VIN_1_1 = 0x1,
CX231XX_VIN_2_1,
CX231XX_VIN_3_1,
CX231XX_VIN_4_1,
CX231XX_VIN_1_2 = 0x01,
CX231XX_VIN_2_2,
CX231XX_VIN_3_2,
CX231XX_VIN_1_3 = 0x1,
CX231XX_VIN_2_3,
CX231XX_VIN_3_3,
};
/* cx231xx has two audio inputs: tuner and line in */
enum cx231xx_amux {
/* This is the only entry for cx231xx tuner input */
CX231XX_AMUX_VIDEO, /* cx231xx tuner */
CX231XX_AMUX_LINE_IN, /* Line In */
};
struct cx231xx_reg_seq {
unsigned char bit;
unsigned char val;
int sleep;
};
struct cx231xx_input {
enum cx231xx_itype type;
unsigned int vmux;
enum cx231xx_amux amux;
struct cx231xx_reg_seq *gpio;
};
#define INPUT(nr) (&cx231xx_boards[dev->model].input[nr])
enum cx231xx_decoder {
CX231XX_NODECODER,
CX231XX_AVDECODER
};
enum CX231XX_I2C_MASTER_PORT {
I2C_0 = 0,
I2C_1 = 1,
I2C_2 = 2,
I2C_3 = 3
};
struct cx231xx_board {
char *name;
int vchannels;
int tuner_type;
int tuner_addr;
v4l2_std_id norm; /* tv norm */
/* demod related */
int demod_addr;
u8 demod_xfer_mode; /* 0 - Serial; 1 - parallel */
/* GPIO Pins */
struct cx231xx_reg_seq *dvb_gpio;
struct cx231xx_reg_seq *suspend_gpio;
struct cx231xx_reg_seq *tuner_gpio;
u8 tuner_sif_gpio;
u8 tuner_scl_gpio;
u8 tuner_sda_gpio;
/* PIN ctrl */
u32 ctl_pin_status_mask;
u8 agc_analog_digital_select_gpio;
u32 gpio_pin_status_mask;
/* i2c masters */
u8 tuner_i2c_master;
u8 demod_i2c_master;
unsigned int max_range_640_480:1;
unsigned int has_dvb:1;
unsigned int valid:1;
unsigned char xclk, i2c_speed;
enum cx231xx_decoder decoder;
struct cx231xx_input input[MAX_CX231XX_INPUT];
struct cx231xx_input radio;
struct ir_scancode_table *ir_codes;
};
/* device states */
enum cx231xx_dev_state {
DEV_INITIALIZED = 0x01,
DEV_DISCONNECTED = 0x02,
DEV_MISCONFIGURED = 0x04,
};
enum AFE_MODE {
AFE_MODE_LOW_IF,
AFE_MODE_BASEBAND,
AFE_MODE_EU_HI_IF,
AFE_MODE_US_HI_IF,
AFE_MODE_JAPAN_HI_IF
};
enum AUDIO_INPUT {
AUDIO_INPUT_MUTE,
AUDIO_INPUT_LINE,
AUDIO_INPUT_TUNER_TV,
AUDIO_INPUT_SPDIF,
AUDIO_INPUT_TUNER_FM
};
#define CX231XX_AUDIO_BUFS 5
#define CX231XX_NUM_AUDIO_PACKETS 64
#define CX231XX_CAPTURE_STREAM_EN 1
#define CX231XX_STOP_AUDIO 0
#define CX231XX_START_AUDIO 1
/* cx231xx extensions */
#define CX231XX_AUDIO 0x10
#define CX231XX_DVB 0x20
struct cx231xx_audio {
char name[50];
char *transfer_buffer[CX231XX_AUDIO_BUFS];
struct urb *urb[CX231XX_AUDIO_BUFS];
struct usb_device *udev;
unsigned int capture_transfer_done;
struct snd_pcm_substream *capture_pcm_substream;
unsigned int hwptr_done_capture;
struct snd_card *sndcard;
int users, shutdown;
enum cx231xx_stream_state capture_stream;
spinlock_t slock;
int alt; /* alternate */
int max_pkt_size; /* max packet size of isoc transaction */
int num_alt; /* Number of alternative settings */
unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */
u16 end_point_addr;
};
struct cx231xx;
struct cx231xx_fh {
struct cx231xx *dev;
unsigned int stream_on:1; /* Locks streams */
int radio;
struct videobuf_queue vb_vidq;
enum v4l2_buf_type type;
};
/*****************************************************************/
/* set/get i2c */
/* 00--1Mb/s, 01-400kb/s, 10--100kb/s, 11--5Mb/s */
#define I2C_SPEED_1M 0x0
#define I2C_SPEED_400K 0x1
#define I2C_SPEED_100K 0x2
#define I2C_SPEED_5M 0x3
/* 0-- STOP transaction */
#define I2C_STOP 0x0
/* 1-- do not transmit STOP at end of transaction */
#define I2C_NOSTOP 0x1
/* 1--alllow slave to insert clock wait states */
#define I2C_SYNC 0x1
struct cx231xx_i2c {
struct cx231xx *dev;
int nr;
/* i2c i/o */
struct i2c_adapter i2c_adap;
struct i2c_algo_bit_data i2c_algo;
struct i2c_client i2c_client;
u32 i2c_rc;
/* different settings for each bus */
u8 i2c_period;
u8 i2c_nostop;
u8 i2c_reserve;
};
struct cx231xx_i2c_xfer_data {
u8 dev_addr;
u8 direction; /* 1 - IN, 0 - OUT */
u8 saddr_len; /* sub address len */
u16 saddr_dat; /* sub addr data */
u8 buf_size; /* buffer size */
u8 *p_buffer; /* pointer to the buffer */
};
struct VENDOR_REQUEST_IN {
u8 bRequest;
u16 wValue;
u16 wIndex;
u16 wLength;
u8 direction;
u8 bData;
u8 *pBuff;
};
struct cx231xx_ctrl {
struct v4l2_queryctrl v;
u32 off;
u32 reg;
u32 mask;
u32 shift;
};
enum TRANSFER_TYPE {
Raw_Video = 0,
Audio,
Vbi, /* VANC */
Sliced_cc, /* HANC */
TS1_serial_mode,
TS2,
TS1_parallel_mode
} ;
struct cx231xx_video_mode {
/* Isoc control struct */
struct cx231xx_dmaqueue vidq;
struct cx231xx_usb_isoc_ctl isoc_ctl;
spinlock_t slock;
/* usb transfer */
int alt; /* alternate */
int max_pkt_size; /* max packet size of isoc transaction */
int num_alt; /* Number of alternative settings */
unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */
u16 end_point_addr;
};
/* main device struct */
struct cx231xx {
/* generic device properties */
char name[30]; /* name (including minor) of the device */
int model; /* index in the device_data struct */
int devno; /* marks the number of this device */
struct cx231xx_board board;
unsigned int stream_on:1; /* Locks streams */
unsigned int vbi_stream_on:1; /* Locks streams for VBI */
unsigned int has_audio_class:1;
unsigned int has_alsa_audio:1;
struct cx231xx_fmt *format;
struct v4l2_device v4l2_dev;
struct v4l2_subdev *sd_cx25840;
struct v4l2_subdev *sd_tuner;
struct cx231xx_IR *ir;
struct list_head devlist;
int tuner_type; /* type of the tuner */
int tuner_addr; /* tuner address */
/* I2C adapters: Master 1 & 2 (External) & Master 3 (Internal only) */
struct cx231xx_i2c i2c_bus[3];
unsigned int xc_fw_load_done:1;
struct mutex gpio_i2c_lock;
/* video for linux */
int users; /* user count for exclusive use */
struct video_device *vdev; /* video for linux device struct */
v4l2_std_id norm; /* selected tv norm */
int ctl_freq; /* selected frequency */
unsigned int ctl_ainput; /* selected audio input */
int mute;
int volume;
/* frame properties */
int width; /* current frame width */
int height; /* current frame height */
unsigned hscale; /* horizontal scale factor (see datasheet) */
unsigned vscale; /* vertical scale factor (see datasheet) */
int interlaced; /* 1=interlace fileds, 0=just top fileds */
struct cx231xx_audio adev;
/* states */
enum cx231xx_dev_state state;
struct work_struct request_module_wk;
/* locks */
struct mutex lock;
struct mutex ctrl_urb_lock; /* protects urb_buf */
struct list_head inqueue, outqueue;
wait_queue_head_t open, wait_frame, wait_stream;
struct video_device *vbi_dev;
struct video_device *radio_dev;
unsigned char eedata[256];
struct cx231xx_video_mode video_mode;
struct cx231xx_video_mode vbi_mode;
struct cx231xx_video_mode sliced_cc_mode;
struct cx231xx_video_mode ts1_mode;
struct usb_device *udev; /* the usb device */
char urb_buf[URB_MAX_CTRL_SIZE]; /* urb control msg buffer */
/* helper funcs that call usb_control_msg */
int (*cx231xx_read_ctrl_reg) (struct cx231xx *dev, u8 req, u16 reg,
char *buf, int len);
int (*cx231xx_write_ctrl_reg) (struct cx231xx *dev, u8 req, u16 reg,
char *buf, int len);
int (*cx231xx_send_usb_command) (struct cx231xx_i2c *i2c_bus,
struct cx231xx_i2c_xfer_data *req_data);
int (*cx231xx_gpio_i2c_read) (struct cx231xx *dev, u8 dev_addr,
u8 *buf, u8 len);
int (*cx231xx_gpio_i2c_write) (struct cx231xx *dev, u8 dev_addr,
u8 *buf, u8 len);
int (*cx231xx_set_analog_freq) (struct cx231xx *dev, u32 freq);
int (*cx231xx_reset_analog_tuner) (struct cx231xx *dev);
enum cx231xx_mode mode;
struct cx231xx_dvb *dvb;
/* Cx231xx supported PCB config's */
struct pcb_config current_pcb_config;
u8 current_scenario_idx;
u8 interface_count;
u8 max_iad_interface_count;
/* GPIO related register direction and values */
u32 gpio_dir;
u32 gpio_val;
/* Power Modes */
int power_mode;
/* afe parameters */
enum AFE_MODE afe_mode;
u32 afe_ref_count;
/* video related parameters */
u32 video_input;
u32 active_mode;
u8 vbi_or_sliced_cc_mode; /* 0 - vbi ; 1 - sliced cc mode */
enum cx231xx_std_mode std_mode; /* 0 - Air; 1 - cable */
};
#define cx25840_call(cx231xx, o, f, args...) \
v4l2_subdev_call(cx231xx->sd_cx25840, o, f, ##args)
#define tuner_call(cx231xx, o, f, args...) \
v4l2_subdev_call(cx231xx->sd_tuner, o, f, ##args)
#define call_all(dev, o, f, args...) \
v4l2_device_call_until_err(&dev->v4l2_dev, 0, o, f, ##args)
struct cx231xx_ops {
struct list_head next;
char *name;
int id;
int (*init) (struct cx231xx *);
int (*fini) (struct cx231xx *);
};
/* call back functions in dvb module */
int cx231xx_set_analog_freq(struct cx231xx *dev, u32 freq);
int cx231xx_reset_analog_tuner(struct cx231xx *dev);
/* Provided by cx231xx-i2c.c */
void cx231xx_do_i2c_scan(struct cx231xx *dev, struct i2c_client *c);
int cx231xx_i2c_register(struct cx231xx_i2c *bus);
int cx231xx_i2c_unregister(struct cx231xx_i2c *bus);
/* Internal block control functions */
int cx231xx_read_i2c_data(struct cx231xx *dev, u8 dev_addr,
u16 saddr, u8 saddr_len, u32 *data, u8 data_len);
int cx231xx_write_i2c_data(struct cx231xx *dev, u8 dev_addr,
u16 saddr, u8 saddr_len, u32 data, u8 data_len);
int cx231xx_reg_mask_write(struct cx231xx *dev, u8 dev_addr, u8 size,
u16 register_address, u8 bit_start, u8 bit_end,
u32 value);
int cx231xx_read_modify_write_i2c_dword(struct cx231xx *dev, u8 dev_addr,
u16 saddr, u32 mask, u32 value);
u32 cx231xx_set_field(u32 field_mask, u32 data);
/* afe related functions */
int cx231xx_afe_init_super_block(struct cx231xx *dev, u32 ref_count);
int cx231xx_afe_init_channels(struct cx231xx *dev);
int cx231xx_afe_setup_AFE_for_baseband(struct cx231xx *dev);
int cx231xx_afe_set_input_mux(struct cx231xx *dev, u32 input_mux);
int cx231xx_afe_set_mode(struct cx231xx *dev, enum AFE_MODE mode);
int cx231xx_afe_update_power_control(struct cx231xx *dev,
enum AV_MODE avmode);
int cx231xx_afe_adjust_ref_count(struct cx231xx *dev, u32 video_input);
/* i2s block related functions */
int cx231xx_i2s_blk_initialize(struct cx231xx *dev);
int cx231xx_i2s_blk_update_power_control(struct cx231xx *dev,
enum AV_MODE avmode);
int cx231xx_i2s_blk_set_audio_input(struct cx231xx *dev, u8 audio_input);
/* DIF related functions */
int cx231xx_dif_configure_C2HH_for_low_IF(struct cx231xx *dev, u32 mode,
u32 function_mode, u32 standard);
int cx231xx_dif_set_standard(struct cx231xx *dev, u32 standard);
int cx231xx_tuner_pre_channel_change(struct cx231xx *dev);
int cx231xx_tuner_post_channel_change(struct cx231xx *dev);
/* video parser functions */
u8 cx231xx_find_next_SAV_EAV(u8 *p_buffer, u32 buffer_size,
u32 *p_bytes_used);
u8 cx231xx_find_boundary_SAV_EAV(u8 *p_buffer, u8 *partial_buf,
u32 *p_bytes_used);
int cx231xx_do_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
u8 *p_buffer, u32 bytes_to_copy);
void cx231xx_reset_video_buffer(struct cx231xx *dev,
struct cx231xx_dmaqueue *dma_q);
u8 cx231xx_is_buffer_done(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q);
u32 cx231xx_copy_video_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
u8 *p_line, u32 length, int field_number);
u32 cx231xx_get_video_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
u8 sav_eav, u8 *p_buffer, u32 buffer_size);
void cx231xx_swab(u16 *from, u16 *to, u16 len);
/* Provided by cx231xx-core.c */
u32 cx231xx_request_buffers(struct cx231xx *dev, u32 count);
void cx231xx_queue_unusedframes(struct cx231xx *dev);
void cx231xx_release_buffers(struct cx231xx *dev);
/* read from control pipe */
int cx231xx_read_ctrl_reg(struct cx231xx *dev, u8 req, u16 reg,
char *buf, int len);
/* write to control pipe */
int cx231xx_write_ctrl_reg(struct cx231xx *dev, u8 req, u16 reg,
char *buf, int len);
int cx231xx_mode_register(struct cx231xx *dev, u16 address, u32 mode);
int cx231xx_send_vendor_cmd(struct cx231xx *dev,
struct VENDOR_REQUEST_IN *ven_req);
int cx231xx_send_usb_command(struct cx231xx_i2c *i2c_bus,
struct cx231xx_i2c_xfer_data *req_data);
/* Gpio related functions */
int cx231xx_send_gpio_cmd(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val,
u8 len, u8 request, u8 direction);
int cx231xx_set_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val);
int cx231xx_get_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val);
int cx231xx_set_gpio_value(struct cx231xx *dev, int pin_number, int pin_value);
int cx231xx_set_gpio_direction(struct cx231xx *dev, int pin_number,
int pin_value);
int cx231xx_gpio_i2c_start(struct cx231xx *dev);
int cx231xx_gpio_i2c_end(struct cx231xx *dev);
int cx231xx_gpio_i2c_write_byte(struct cx231xx *dev, u8 data);
int cx231xx_gpio_i2c_read_byte(struct cx231xx *dev, u8 *buf);
int cx231xx_gpio_i2c_read_ack(struct cx231xx *dev);
int cx231xx_gpio_i2c_write_ack(struct cx231xx *dev);
int cx231xx_gpio_i2c_write_nak(struct cx231xx *dev);
int cx231xx_gpio_i2c_read(struct cx231xx *dev, u8 dev_addr, u8 *buf, u8 len);
int cx231xx_gpio_i2c_write(struct cx231xx *dev, u8 dev_addr, u8 *buf, u8 len);
/* audio related functions */
int cx231xx_set_audio_decoder_input(struct cx231xx *dev,
enum AUDIO_INPUT audio_input);
int cx231xx_capture_start(struct cx231xx *dev, int start, u8 media_type);
int cx231xx_resolution_set(struct cx231xx *dev);
int cx231xx_set_video_alternate(struct cx231xx *dev);
int cx231xx_set_alt_setting(struct cx231xx *dev, u8 index, u8 alt);
int cx231xx_init_isoc(struct cx231xx *dev, int max_packets,
int num_bufs, int max_pkt_size,
int (*isoc_copy) (struct cx231xx *dev,
struct urb *urb));
void cx231xx_uninit_isoc(struct cx231xx *dev);
int cx231xx_set_mode(struct cx231xx *dev, enum cx231xx_mode set_mode);
int cx231xx_gpio_set(struct cx231xx *dev, struct cx231xx_reg_seq *gpio);
/* Device list functions */
void cx231xx_release_resources(struct cx231xx *dev);
void cx231xx_release_analog_resources(struct cx231xx *dev);
int cx231xx_register_analog_devices(struct cx231xx *dev);
void cx231xx_remove_from_devlist(struct cx231xx *dev);
void cx231xx_add_into_devlist(struct cx231xx *dev);
struct cx231xx *cx231xx_get_device(int minor,
enum v4l2_buf_type *fh_type, int *has_radio);
void cx231xx_init_extension(struct cx231xx *dev);
void cx231xx_close_extension(struct cx231xx *dev);
/* hardware init functions */
int cx231xx_dev_init(struct cx231xx *dev);
void cx231xx_dev_uninit(struct cx231xx *dev);
void cx231xx_config_i2c(struct cx231xx *dev);
int cx231xx_config(struct cx231xx *dev);
/* Stream control functions */
int cx231xx_start_stream(struct cx231xx *dev, u32 ep_mask);
int cx231xx_stop_stream(struct cx231xx *dev, u32 ep_mask);
int cx231xx_initialize_stream_xfer(struct cx231xx *dev, u32 media_type);
/* Power control functions */
int cx231xx_set_power_mode(struct cx231xx *dev, enum AV_MODE mode);
int cx231xx_power_suspend(struct cx231xx *dev);
/* chip specific control functions */
int cx231xx_init_ctrl_pin_status(struct cx231xx *dev);
int cx231xx_set_agc_analog_digital_mux_select(struct cx231xx *dev,
u8 analog_or_digital);
int cx231xx_enable_i2c_for_tuner(struct cx231xx *dev, u8 I2CIndex);
/* video audio decoder related functions */
void video_mux(struct cx231xx *dev, int index);
int cx231xx_set_video_input_mux(struct cx231xx *dev, u8 input);
int cx231xx_set_decoder_video_input(struct cx231xx *dev, u8 pin_type, u8 input);
int cx231xx_do_mode_ctrl_overrides(struct cx231xx *dev);
int cx231xx_set_audio_input(struct cx231xx *dev, u8 input);
/* Provided by cx231xx-video.c */
int cx231xx_register_extension(struct cx231xx_ops *dev);
void cx231xx_unregister_extension(struct cx231xx_ops *dev);
void cx231xx_init_extension(struct cx231xx *dev);
void cx231xx_close_extension(struct cx231xx *dev);
/* Provided by cx231xx-cards.c */
extern void cx231xx_pre_card_setup(struct cx231xx *dev);
extern void cx231xx_card_setup(struct cx231xx *dev);
extern struct cx231xx_board cx231xx_boards[];
extern struct usb_device_id cx231xx_id_table[];
extern const unsigned int cx231xx_bcount;
void cx231xx_register_i2c_ir(struct cx231xx *dev);
int cx231xx_tuner_callback(void *ptr, int component, int command, int arg);
/* Provided by cx231xx-input.c */
int cx231xx_ir_init(struct cx231xx *dev);
int cx231xx_ir_fini(struct cx231xx *dev);
/* printk macros */
#define cx231xx_err(fmt, arg...) do {\
printk(KERN_ERR fmt , ##arg); } while (0)
#define cx231xx_errdev(fmt, arg...) do {\
printk(KERN_ERR "%s: "fmt,\
dev->name , ##arg); } while (0)
#define cx231xx_info(fmt, arg...) do {\
printk(KERN_INFO "%s: "fmt,\
dev->name , ##arg); } while (0)
#define cx231xx_warn(fmt, arg...) do {\
printk(KERN_WARNING "%s: "fmt,\
dev->name , ##arg); } while (0)
static inline unsigned int norm_maxw(struct cx231xx *dev)
{
if (dev->board.max_range_640_480)
return 640;
else
return 720;
}
static inline unsigned int norm_maxh(struct cx231xx *dev)
{
if (dev->board.max_range_640_480)
return 480;
else
return (dev->norm & V4L2_STD_625_50) ? 576 : 480;
}
#endif