[media] rename most media/video usb drivers to media/usb
Rename all USB drivers with their own directory under drivers/media/video into drivers/media/usb and update the building system. Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
51
drivers/media/usb/cx231xx/Kconfig
Normal file
51
drivers/media/usb/cx231xx/Kconfig
Normal file
@@ -0,0 +1,51 @@
|
||||
config VIDEO_CX231XX
|
||||
tristate "Conexant cx231xx USB video capture support"
|
||||
depends on VIDEO_DEV && I2C
|
||||
select VIDEO_TUNER
|
||||
select VIDEO_TVEEPROM
|
||||
depends on RC_CORE
|
||||
select VIDEOBUF_VMALLOC
|
||||
select VIDEO_CX25840
|
||||
select VIDEO_CX2341X
|
||||
|
||||
---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_RC
|
||||
bool "Conexant cx231xx Remote Controller additional support"
|
||||
depends on RC_CORE
|
||||
depends on VIDEO_CX231XX
|
||||
default y
|
||||
---help---
|
||||
cx231xx hardware has a builtin RX/TX support. However, a few
|
||||
designs opted to not use it, but, instead, some other hardware.
|
||||
This module enables the usage of those other hardware, like the
|
||||
ones used with ISDB-T boards.
|
||||
|
||||
On most cases, all you need for IR is mceusb module.
|
||||
|
||||
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 && DVB_CAPTURE_DRIVERS
|
||||
select VIDEOBUF_DVB
|
||||
select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE
|
||||
select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE
|
||||
select DVB_MB86A20S if !DVB_FE_CUSTOMISE
|
||||
|
||||
---help---
|
||||
This adds support for DVB cards based on the
|
||||
Conexant cx231xx chips.
|
15
drivers/media/usb/cx231xx/Makefile
Normal file
15
drivers/media/usb/cx231xx/Makefile
Normal file
@@ -0,0 +1,15 @@
|
||||
cx231xx-y += cx231xx-video.o cx231xx-i2c.o cx231xx-cards.o cx231xx-core.o
|
||||
cx231xx-y += cx231xx-avcore.o cx231xx-417.o cx231xx-pcb-cfg.o cx231xx-vbi.o
|
||||
cx231xx-$(CONFIG_VIDEO_CX231XX_RC) += cx231xx-input.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
|
||||
|
||||
ccflags-y += -Idrivers/media/video
|
||||
ccflags-y += -Idrivers/media/tuners
|
||||
ccflags-y += -Idrivers/media/dvb-core
|
||||
ccflags-y += -Idrivers/media/dvb-frontends
|
||||
ccflags-y += -Idrivers/media/usb/dvb-usb
|
2197
drivers/media/usb/cx231xx/cx231xx-417.c
Normal file
2197
drivers/media/usb/cx231xx/cx231xx-417.c
Normal file
File diff suppressed because it is too large
Load Diff
780
drivers/media/usb/cx231xx/cx231xx-audio.c
Normal file
780
drivers/media/usb/cx231xx/cx231xx-audio.c
Normal file
@@ -0,0 +1,780 @@
|
||||
/*
|
||||
* 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 int cx231xx_bulk_audio_deinit(struct cx231xx *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
dprintk("Stopping bulk\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;
|
||||
|
||||
if (dev->state & DEV_DISCONNECTED)
|
||||
return;
|
||||
|
||||
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 (atomic_read(&dev->stream_started) == 0)
|
||||
return;
|
||||
|
||||
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 void cx231xx_audio_bulkirq(struct urb *urb)
|
||||
{
|
||||
struct cx231xx *dev = urb->context;
|
||||
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;
|
||||
|
||||
if (dev->state & DEV_DISCONNECTED)
|
||||
return;
|
||||
|
||||
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 (atomic_read(&dev->stream_started) == 0)
|
||||
return;
|
||||
|
||||
if (dev->adev.capture_pcm_substream) {
|
||||
substream = dev->adev.capture_pcm_substream;
|
||||
runtime = substream->runtime;
|
||||
stride = runtime->frame_bits >> 3;
|
||||
|
||||
if (1) {
|
||||
int length = urb->actual_length /
|
||||
stride;
|
||||
cp = (unsigned char *)urb->transfer_buffer;
|
||||
|
||||
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 ISO AUDIO transfers\n", __func__);
|
||||
|
||||
if (dev->state & DEV_DISCONNECTED)
|
||||
return -ENODEV;
|
||||
|
||||
sb_size = CX231XX_ISO_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_ISO_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_ISO_NUM_AUDIO_PACKETS;
|
||||
urb->transfer_buffer_length = sb_size;
|
||||
|
||||
for (j = k = 0; j < CX231XX_ISO_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_init_audio_bulk(struct cx231xx *dev)
|
||||
{
|
||||
int i, errCode;
|
||||
int sb_size;
|
||||
|
||||
cx231xx_info("%s: Starting BULK AUDIO transfers\n", __func__);
|
||||
|
||||
if (dev->state & DEV_DISCONNECTED)
|
||||
return -ENODEV;
|
||||
|
||||
sb_size = CX231XX_NUM_AUDIO_PACKETS * dev->adev.max_pkt_size;
|
||||
|
||||
for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
|
||||
struct urb *urb;
|
||||
int j;
|
||||
|
||||
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_rcvbulkpipe(dev->udev,
|
||||
dev->adev.end_point_addr);
|
||||
urb->transfer_flags = 0;
|
||||
urb->transfer_buffer = dev->adev.transfer_buffer[i];
|
||||
urb->complete = cx231xx_audio_bulkirq;
|
||||
urb->transfer_buffer_length = sb_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_bulk_audio_deinit(dev);
|
||||
return errCode;
|
||||
}
|
||||
}
|
||||
|
||||
return errCode;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (dev->state & DEV_DISCONNECTED) {
|
||||
cx231xx_errdev("Can't open. the device was removed.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Sets volume, mute, etc */
|
||||
dev->mute = 0;
|
||||
|
||||
/* set alternate setting for audio interface */
|
||||
/* 1 - 48000 samples per sec */
|
||||
mutex_lock(&dev->lock);
|
||||
if (dev->USE_ISO)
|
||||
ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 1);
|
||||
else
|
||||
ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 0);
|
||||
mutex_unlock(&dev->lock);
|
||||
if (ret < 0) {
|
||||
cx231xx_errdev("failed to set alternate setting !\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
runtime->hw = snd_cx231xx_hw_capture;
|
||||
|
||||
mutex_lock(&dev->lock);
|
||||
/* inform hardware to start streaming */
|
||||
ret = cx231xx_capture_start(dev, 1, Audio);
|
||||
|
||||
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");
|
||||
|
||||
/* inform hardware to stop streaming */
|
||||
mutex_lock(&dev->lock);
|
||||
ret = cx231xx_capture_start(dev, 0, Audio);
|
||||
|
||||
/* 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");
|
||||
|
||||
mutex_unlock(&dev->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev->mute = 1;
|
||||
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");
|
||||
if (atomic_read(&dev->stream_started) > 0) {
|
||||
atomic_set(&dev->stream_started, 0);
|
||||
schedule_work(&dev->wq_trigger);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_cx231xx_hw_capture_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
int ret;
|
||||
|
||||
dprintk("Setting capture parameters\n");
|
||||
|
||||
ret = snd_pcm_alloc_vmalloc_buffer(substream,
|
||||
params_buffer_bytes(hw_params));
|
||||
#if 0
|
||||
/* 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 */
|
||||
unsigned int channels, rate, format;
|
||||
|
||||
format = params_format(hw_params);
|
||||
rate = params_rate(hw_params);
|
||||
channels = params_channels(hw_params);
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
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 (atomic_read(&dev->stream_started) > 0) {
|
||||
atomic_set(&dev->stream_started, 0);
|
||||
schedule_work(&dev->wq_trigger);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_cx231xx_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct cx231xx *dev = snd_pcm_substream_chip(substream);
|
||||
|
||||
dev->adev.hwptr_done_capture = 0;
|
||||
dev->adev.capture_transfer_done = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void audio_trigger(struct work_struct *work)
|
||||
{
|
||||
struct cx231xx *dev = container_of(work, struct cx231xx, wq_trigger);
|
||||
|
||||
if (atomic_read(&dev->stream_started)) {
|
||||
dprintk("starting capture");
|
||||
if (is_fw_load(dev) == 0)
|
||||
cx25840_call(dev, core, load_fw);
|
||||
if (dev->USE_ISO)
|
||||
cx231xx_init_audio_isoc(dev);
|
||||
else
|
||||
cx231xx_init_audio_bulk(dev);
|
||||
} else {
|
||||
dprintk("stopping capture");
|
||||
cx231xx_isoc_audio_deinit(dev);
|
||||
}
|
||||
}
|
||||
|
||||
static int snd_cx231xx_capture_trigger(struct snd_pcm_substream *substream,
|
||||
int cmd)
|
||||
{
|
||||
struct cx231xx *dev = snd_pcm_substream_chip(substream);
|
||||
int retval = 0;
|
||||
|
||||
if (dev->state & DEV_DISCONNECTED)
|
||||
return -ENODEV;
|
||||
|
||||
spin_lock(&dev->adev.slock);
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
atomic_set(&dev->stream_started, 1);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
atomic_set(&dev->stream_started, 0);
|
||||
break;
|
||||
default:
|
||||
retval = -EINVAL;
|
||||
break;
|
||||
}
|
||||
spin_unlock(&dev->adev.slock);
|
||||
|
||||
schedule_work(&dev->wq_trigger);
|
||||
|
||||
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");
|
||||
snd_card_set_dev(card, &dev->udev->dev);
|
||||
strcpy(card->driver, "Cx231xx-Audio");
|
||||
strcpy(card->shortname, "Cx231xx Audio");
|
||||
strcpy(card->longname, "Conexant cx231xx Audio");
|
||||
|
||||
INIT_WORK(&dev->wq_trigger, audio_trigger);
|
||||
|
||||
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);
|
3087
drivers/media/usb/cx231xx/cx231xx-avcore.c
Normal file
3087
drivers/media/usb/cx231xx/cx231xx-avcore.c
Normal file
File diff suppressed because it is too large
Load Diff
1370
drivers/media/usb/cx231xx/cx231xx-cards.c
Normal file
1370
drivers/media/usb/cx231xx/cx231xx-cards.c
Normal file
File diff suppressed because it is too large
Load Diff
495
drivers/media/usb/cx231xx/cx231xx-conf-reg.h
Normal file
495
drivers/media/usb/cx231xx/cx231xx-conf-reg.h
Normal file
@@ -0,0 +1,495 @@
|
||||
/*
|
||||
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 GBULK_BIT_EN 0x68
|
||||
#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
|
1736
drivers/media/usb/cx231xx/cx231xx-core.c
Normal file
1736
drivers/media/usb/cx231xx/cx231xx-core.c
Normal file
File diff suppressed because it is too large
Load Diff
3178
drivers/media/usb/cx231xx/cx231xx-dif.h
Normal file
3178
drivers/media/usb/cx231xx/cx231xx-dif.h
Normal file
File diff suppressed because it is too large
Load Diff
796
drivers/media/usb/cx231xx/cx231xx-dvb.c
Normal file
796
drivers/media/usb/cx231xx/cx231xx-dvb.c
Normal file
@@ -0,0 +1,796 @@
|
||||
/*
|
||||
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/slab.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include "cx231xx.h"
|
||||
#include <media/v4l2-common.h>
|
||||
#include <media/videobuf-vmalloc.h>
|
||||
|
||||
#include "xc5000.h"
|
||||
#include "s5h1432.h"
|
||||
#include "tda18271.h"
|
||||
#include "s5h1411.h"
|
||||
#include "lgdt3305.h"
|
||||
#include "mb86a20s.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 struct s5h1432_config dvico_s5h1432_config = {
|
||||
.output_mode = S5H1432_SERIAL_OUTPUT,
|
||||
.gpio = S5H1432_GPIO_ON,
|
||||
.qam_if = S5H1432_IF_4000,
|
||||
.vsb_if = S5H1432_IF_4000,
|
||||
.inversion = S5H1432_INVERSION_OFF,
|
||||
.status_mode = S5H1432_DEMODLOCKING,
|
||||
.mpeg_timing = S5H1432_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
|
||||
};
|
||||
|
||||
static struct tda18271_std_map cnxt_rde253s_tda18271_std_map = {
|
||||
.dvbt_6 = { .if_freq = 4000, .agc_mode = 3, .std = 4,
|
||||
.if_lvl = 1, .rfagc_top = 0x37, },
|
||||
.dvbt_7 = { .if_freq = 4000, .agc_mode = 3, .std = 5,
|
||||
.if_lvl = 1, .rfagc_top = 0x37, },
|
||||
.dvbt_8 = { .if_freq = 4000, .agc_mode = 3, .std = 6,
|
||||
.if_lvl = 1, .rfagc_top = 0x37, },
|
||||
};
|
||||
|
||||
static struct tda18271_std_map mb86a20s_tda18271_config = {
|
||||
.dvbt_6 = { .if_freq = 3300, .agc_mode = 3, .std = 4,
|
||||
.if_lvl = 7, .rfagc_top = 0x37, },
|
||||
};
|
||||
|
||||
static struct tda18271_config cnxt_rde253s_tunerconfig = {
|
||||
.std_map = &cnxt_rde253s_tda18271_std_map,
|
||||
.gate = TDA18271_GATE_ANALOG,
|
||||
};
|
||||
|
||||
static struct s5h1411_config tda18271_s5h1411_config = {
|
||||
.output_mode = S5H1411_SERIAL_OUTPUT,
|
||||
.gpio = S5H1411_GPIO_OFF,
|
||||
.vsb_if = S5H1411_IF_3250,
|
||||
.qam_if = S5H1411_IF_4000,
|
||||
.inversion = S5H1411_INVERSION_ON,
|
||||
.status_mode = S5H1411_DEMODLOCKING,
|
||||
.mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
|
||||
};
|
||||
static struct s5h1411_config xc5000_s5h1411_config = {
|
||||
.output_mode = S5H1411_SERIAL_OUTPUT,
|
||||
.gpio = S5H1411_GPIO_OFF,
|
||||
.vsb_if = S5H1411_IF_3250,
|
||||
.qam_if = S5H1411_IF_3250,
|
||||
.inversion = S5H1411_INVERSION_OFF,
|
||||
.status_mode = S5H1411_DEMODLOCKING,
|
||||
.mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
|
||||
};
|
||||
|
||||
static struct lgdt3305_config hcw_lgdt3305_config = {
|
||||
.i2c_addr = 0x0e,
|
||||
.mpeg_mode = LGDT3305_MPEG_SERIAL,
|
||||
.tpclk_edge = LGDT3305_TPCLK_FALLING_EDGE,
|
||||
.tpvalid_polarity = LGDT3305_TP_VALID_HIGH,
|
||||
.deny_i2c_rptr = 1,
|
||||
.spectral_inversion = 1,
|
||||
.qam_if_khz = 4000,
|
||||
.vsb_if_khz = 3250,
|
||||
};
|
||||
|
||||
static struct tda18271_std_map hauppauge_tda18271_std_map = {
|
||||
.atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 4,
|
||||
.if_lvl = 1, .rfagc_top = 0x58, },
|
||||
.qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 5,
|
||||
.if_lvl = 1, .rfagc_top = 0x58, },
|
||||
};
|
||||
|
||||
static struct tda18271_config hcw_tda18271_config = {
|
||||
.std_map = &hauppauge_tda18271_std_map,
|
||||
.gate = TDA18271_GATE_DIGITAL,
|
||||
};
|
||||
|
||||
static const struct mb86a20s_config pv_mb86a20s_config = {
|
||||
.demod_address = 0x10,
|
||||
.is_serial = true,
|
||||
};
|
||||
|
||||
static struct tda18271_config pv_tda18271_config = {
|
||||
.std_map = &mb86a20s_tda18271_config,
|
||||
.gate = TDA18271_GATE_DIGITAL,
|
||||
.small_i2c = TDA18271_03_BYTE_CHUNK_INIT,
|
||||
};
|
||||
|
||||
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)
|
||||
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 inline int dvb_bulk_copy(struct cx231xx *dev, struct urb *urb)
|
||||
{
|
||||
if (!dev)
|
||||
return 0;
|
||||
|
||||
if (dev->state & DEV_DISCONNECTED)
|
||||
return 0;
|
||||
|
||||
if (urb->status < 0) {
|
||||
print_err_status(dev, -1, urb->status);
|
||||
if (urb->status == -ENOENT)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Feed the transport payload into the kernel demux */
|
||||
dvb_dmx_swfilter(&dev->dvb->demux,
|
||||
urb->transfer_buffer, urb->actual_length);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int start_streaming(struct cx231xx_dvb *dvb)
|
||||
{
|
||||
int rc;
|
||||
struct cx231xx *dev = dvb->adapter.priv;
|
||||
|
||||
if (dev->USE_ISO) {
|
||||
cx231xx_info("DVB transfer mode is ISO.\n");
|
||||
mutex_lock(&dev->i2c_lock);
|
||||
cx231xx_enable_i2c_port_3(dev, false);
|
||||
cx231xx_set_alt_setting(dev, INDEX_TS1, 4);
|
||||
cx231xx_enable_i2c_port_3(dev, true);
|
||||
mutex_unlock(&dev->i2c_lock);
|
||||
rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
dev->mode_tv = 1;
|
||||
return cx231xx_init_isoc(dev, CX231XX_DVB_MAX_PACKETS,
|
||||
CX231XX_DVB_NUM_BUFS,
|
||||
dev->ts1_mode.max_pkt_size,
|
||||
dvb_isoc_copy);
|
||||
} else {
|
||||
cx231xx_info("DVB transfer mode is BULK.\n");
|
||||
cx231xx_set_alt_setting(dev, INDEX_TS1, 0);
|
||||
rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
dev->mode_tv = 1;
|
||||
return cx231xx_init_bulk(dev, CX231XX_DVB_MAX_PACKETS,
|
||||
CX231XX_DVB_NUM_BUFS,
|
||||
dev->ts1_mode.max_pkt_size,
|
||||
dvb_bulk_copy);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int stop_streaming(struct cx231xx_dvb *dvb)
|
||||
{
|
||||
struct cx231xx *dev = dvb->adapter.priv;
|
||||
|
||||
if (dev->USE_ISO)
|
||||
cx231xx_uninit_isoc(dev);
|
||||
else
|
||||
cx231xx_uninit_bulk(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 = 4000,
|
||||
};
|
||||
static struct xc5000_config cnxt_rdu250_tunerconfig = {
|
||||
.i2c_address = 0x61,
|
||||
.if_khz = 3250,
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
#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[dev->board.tuner_i2c_master].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 */
|
||||
dops->set_analog_params(dev->dvb->frontend, ¶ms);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
mutex_lock(&dev->lock);
|
||||
cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
|
||||
cx231xx_demod_reset(dev);
|
||||
/* init frontend */
|
||||
switch (dev->model) {
|
||||
case CX231XX_BOARD_CNXT_CARRAERA:
|
||||
case CX231XX_BOARD_CNXT_RDE_250:
|
||||
|
||||
dev->dvb->frontend = dvb_attach(s5h1432_attach,
|
||||
&dvico_s5h1432_config,
|
||||
&dev->i2c_bus[dev->board.demod_i2c_master].i2c_adap);
|
||||
|
||||
if (dev->dvb->frontend == NULL) {
|
||||
printk(DRIVER_NAME
|
||||
": Failed to attach s5h1432 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[dev->board.tuner_i2c_master].i2c_adap,
|
||||
&cnxt_rde250_tunerconfig)) {
|
||||
result = -EINVAL;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
break;
|
||||
case CX231XX_BOARD_CNXT_SHELBY:
|
||||
case CX231XX_BOARD_CNXT_RDU_250:
|
||||
|
||||
dev->dvb->frontend = dvb_attach(s5h1411_attach,
|
||||
&xc5000_s5h1411_config,
|
||||
&dev->i2c_bus[dev->board.demod_i2c_master].i2c_adap);
|
||||
|
||||
if (dev->dvb->frontend == NULL) {
|
||||
printk(DRIVER_NAME
|
||||
": Failed to attach s5h1411 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[dev->board.tuner_i2c_master].i2c_adap,
|
||||
&cnxt_rdu250_tunerconfig)) {
|
||||
result = -EINVAL;
|
||||
goto out_free;
|
||||
}
|
||||
break;
|
||||
case CX231XX_BOARD_CNXT_RDE_253S:
|
||||
|
||||
dev->dvb->frontend = dvb_attach(s5h1432_attach,
|
||||
&dvico_s5h1432_config,
|
||||
&dev->i2c_bus[dev->board.demod_i2c_master].i2c_adap);
|
||||
|
||||
if (dev->dvb->frontend == NULL) {
|
||||
printk(DRIVER_NAME
|
||||
": Failed to attach s5h1432 front end\n");
|
||||
result = -EINVAL;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
/* define general-purpose callback pointer */
|
||||
dvb->frontend->callback = cx231xx_tuner_callback;
|
||||
|
||||
if (!dvb_attach(tda18271_attach, dev->dvb->frontend,
|
||||
0x60, &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap,
|
||||
&cnxt_rde253s_tunerconfig)) {
|
||||
result = -EINVAL;
|
||||
goto out_free;
|
||||
}
|
||||
break;
|
||||
case CX231XX_BOARD_CNXT_RDU_253S:
|
||||
|
||||
dev->dvb->frontend = dvb_attach(s5h1411_attach,
|
||||
&tda18271_s5h1411_config,
|
||||
&dev->i2c_bus[dev->board.demod_i2c_master].i2c_adap);
|
||||
|
||||
if (dev->dvb->frontend == NULL) {
|
||||
printk(DRIVER_NAME
|
||||
": Failed to attach s5h1411 front end\n");
|
||||
result = -EINVAL;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
/* define general-purpose callback pointer */
|
||||
dvb->frontend->callback = cx231xx_tuner_callback;
|
||||
|
||||
if (!dvb_attach(tda18271_attach, dev->dvb->frontend,
|
||||
0x60, &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap,
|
||||
&cnxt_rde253s_tunerconfig)) {
|
||||
result = -EINVAL;
|
||||
goto out_free;
|
||||
}
|
||||
break;
|
||||
case CX231XX_BOARD_HAUPPAUGE_EXETER:
|
||||
|
||||
printk(KERN_INFO "%s: looking for tuner / demod on i2c bus: %d\n",
|
||||
__func__, i2c_adapter_id(&dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap));
|
||||
|
||||
dev->dvb->frontend = dvb_attach(lgdt3305_attach,
|
||||
&hcw_lgdt3305_config,
|
||||
&dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap);
|
||||
|
||||
if (dev->dvb->frontend == NULL) {
|
||||
printk(DRIVER_NAME
|
||||
": Failed to attach LG3305 front end\n");
|
||||
result = -EINVAL;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
/* define general-purpose callback pointer */
|
||||
dvb->frontend->callback = cx231xx_tuner_callback;
|
||||
|
||||
dvb_attach(tda18271_attach, dev->dvb->frontend,
|
||||
0x60, &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap,
|
||||
&hcw_tda18271_config);
|
||||
break;
|
||||
|
||||
case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID:
|
||||
case CX231XX_BOARD_KWORLD_UB430_USB_HYBRID:
|
||||
|
||||
printk(KERN_INFO "%s: looking for demod on i2c bus: %d\n",
|
||||
__func__, i2c_adapter_id(&dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap));
|
||||
|
||||
dev->dvb->frontend = dvb_attach(mb86a20s_attach,
|
||||
&pv_mb86a20s_config,
|
||||
&dev->i2c_bus[dev->board.demod_i2c_master].i2c_adap);
|
||||
|
||||
if (dev->dvb->frontend == NULL) {
|
||||
printk(DRIVER_NAME
|
||||
": Failed to attach mb86a20s demod\n");
|
||||
result = -EINVAL;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
/* define general-purpose callback pointer */
|
||||
dvb->frontend->callback = cx231xx_tuner_callback;
|
||||
|
||||
dvb_attach(tda18271_attach, dev->dvb->frontend,
|
||||
0x60, &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap,
|
||||
&pv_tda18271_config);
|
||||
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;
|
||||
|
||||
|
||||
printk(KERN_INFO "Successfully loaded cx231xx-dvb\n");
|
||||
|
||||
ret:
|
||||
cx231xx_set_mode(dev, CX231XX_SUSPEND);
|
||||
mutex_unlock(&dev->lock);
|
||||
return result;
|
||||
|
||||
out_free:
|
||||
kfree(dvb);
|
||||
dev->dvb = NULL;
|
||||
goto ret;
|
||||
}
|
||||
|
||||
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);
|
532
drivers/media/usb/cx231xx/cx231xx-i2c.c
Normal file
532
drivers/media/usb/cx231xx/cx231xx-i2c.c
Normal file
@@ -0,0 +1,532 @@
|
||||
/*
|
||||
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)
|
||||
|
||||
static inline bool is_tuner(struct cx231xx *dev, struct cx231xx_i2c *bus,
|
||||
const struct i2c_msg *msg, int tuner_type)
|
||||
{
|
||||
if (bus->nr != dev->board.tuner_i2c_master)
|
||||
return false;
|
||||
|
||||
if (msg->addr != dev->board.tuner_addr)
|
||||
return false;
|
||||
|
||||
if (dev->tuner_type != tuner_type)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 (is_tuner(dev, bus, msg, 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 (is_tuner(dev, bus, msg, 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 (is_tuner(dev, bus, msg2, 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;
|
||||
mutex_lock(&dev->i2c_lock);
|
||||
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");
|
||||
mutex_unlock(&dev->i2c_lock);
|
||||
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 < 3)) {
|
||||
/* 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");
|
||||
}
|
||||
mutex_unlock(&dev->i2c_lock);
|
||||
return num;
|
||||
err:
|
||||
dprintk2(2, " ERROR: %i\n", rc);
|
||||
mutex_unlock(&dev->i2c_lock);
|
||||
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",
|
||||
[0xc2 >> 1] = "tuner",
|
||||
};
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
bus->i2c_adap = cx231xx_adap_template;
|
||||
bus->i2c_client = cx231xx_client_template;
|
||||
bus->i2c_adap.dev.parent = &dev->udev->dev;
|
||||
|
||||
strlcpy(bus->i2c_adap.name, bus->dev->name, sizeof(bus->i2c_adap.name));
|
||||
|
||||
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);
|
||||
} 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;
|
||||
}
|
119
drivers/media/usb/cx231xx/cx231xx-input.c
Normal file
119
drivers/media/usb/cx231xx/cx231xx-input.c
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* cx231xx IR glue driver
|
||||
*
|
||||
* Copyright (C) 2010 Mauro Carvalho Chehab <mchehab@redhat.com>
|
||||
*
|
||||
* Polaris (cx231xx) has its support for IR's with a design close to MCE.
|
||||
* however, a few designs are using an external I2C chip for IR, instead
|
||||
* of using the one provided by the chip.
|
||||
* This driver provides support for those extra devices
|
||||
*
|
||||
* 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 version 2.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "cx231xx.h"
|
||||
#include <linux/usb.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define MODULE_NAME "cx231xx-input"
|
||||
|
||||
static int get_key_isdbt(struct IR_i2c *ir, u32 *ir_key,
|
||||
u32 *ir_raw)
|
||||
{
|
||||
int rc;
|
||||
u8 cmd, scancode;
|
||||
|
||||
dev_dbg(&ir->rc->input_dev->dev, "%s\n", __func__);
|
||||
|
||||
/* poll IR chip */
|
||||
rc = i2c_master_recv(ir->c, &cmd, 1);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
if (rc != 1)
|
||||
return -EIO;
|
||||
|
||||
/* it seems that 0xFE indicates that a button is still hold
|
||||
down, while 0xff indicates that no button is hold
|
||||
down. 0xfe sequences are sometimes interrupted by 0xFF */
|
||||
|
||||
if (cmd == 0xff)
|
||||
return 0;
|
||||
|
||||
scancode =
|
||||
((cmd & 0x01) ? 0x80 : 0) |
|
||||
((cmd & 0x02) ? 0x40 : 0) |
|
||||
((cmd & 0x04) ? 0x20 : 0) |
|
||||
((cmd & 0x08) ? 0x10 : 0) |
|
||||
((cmd & 0x10) ? 0x08 : 0) |
|
||||
((cmd & 0x20) ? 0x04 : 0) |
|
||||
((cmd & 0x40) ? 0x02 : 0) |
|
||||
((cmd & 0x80) ? 0x01 : 0);
|
||||
|
||||
dev_dbg(&ir->rc->input_dev->dev, "cmd %02x, scan = %02x\n",
|
||||
cmd, scancode);
|
||||
|
||||
*ir_key = scancode;
|
||||
*ir_raw = scancode;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cx231xx_ir_init(struct cx231xx *dev)
|
||||
{
|
||||
struct i2c_board_info info;
|
||||
u8 ir_i2c_bus;
|
||||
|
||||
dev_dbg(&dev->udev->dev, "%s\n", __func__);
|
||||
|
||||
/* Only initialize if a rc keycode map is defined */
|
||||
if (!cx231xx_boards[dev->model].rc_map_name)
|
||||
return -ENODEV;
|
||||
|
||||
request_module("ir-kbd-i2c");
|
||||
|
||||
memset(&info, 0, sizeof(struct i2c_board_info));
|
||||
memset(&dev->init_data, 0, sizeof(dev->init_data));
|
||||
dev->init_data.rc_dev = rc_allocate_device();
|
||||
if (!dev->init_data.rc_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
dev->init_data.name = cx231xx_boards[dev->model].name;
|
||||
|
||||
strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
|
||||
info.platform_data = &dev->init_data;
|
||||
|
||||
/*
|
||||
* Board-dependent values
|
||||
*
|
||||
* For now, there's just one type of hardware design using
|
||||
* an i2c device.
|
||||
*/
|
||||
dev->init_data.get_key = get_key_isdbt;
|
||||
dev->init_data.ir_codes = cx231xx_boards[dev->model].rc_map_name;
|
||||
/* The i2c micro-controller only outputs the cmd part of NEC protocol */
|
||||
dev->init_data.rc_dev->scanmask = 0xff;
|
||||
dev->init_data.rc_dev->driver_name = "cx231xx";
|
||||
dev->init_data.type = RC_TYPE_NEC;
|
||||
info.addr = 0x30;
|
||||
|
||||
/* Load and bind ir-kbd-i2c */
|
||||
ir_i2c_bus = cx231xx_boards[dev->model].ir_i2c_master;
|
||||
dev_dbg(&dev->udev->dev, "Trying to bind ir at bus %d, addr 0x%02x\n",
|
||||
ir_i2c_bus, info.addr);
|
||||
dev->ir_i2c_client = i2c_new_device(&dev->i2c_bus[ir_i2c_bus].i2c_adap, &info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cx231xx_ir_exit(struct cx231xx *dev)
|
||||
{
|
||||
if (dev->ir_i2c_client)
|
||||
i2c_unregister_device(dev->ir_i2c_client);
|
||||
dev->ir_i2c_client = NULL;
|
||||
}
|
795
drivers/media/usb/cx231xx/cx231xx-pcb-cfg.c
Normal file
795
drivers/media/usb/cx231xx/cx231xx-pcb-cfg.c
Normal 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;
|
||||
}
|
231
drivers/media/usb/cx231xx/cx231xx-pcb-cfg.h
Normal file
231
drivers/media/usb/cx231xx/cx231xx-pcb-cfg.h
Normal 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
|
1564
drivers/media/usb/cx231xx/cx231xx-reg.h
Normal file
1564
drivers/media/usb/cx231xx/cx231xx-reg.h
Normal file
File diff suppressed because it is too large
Load Diff
710
drivers/media/usb/cx231xx/cx231xx-vbi.c
Normal file
710
drivers/media/usb/cx231xx/cx231xx-vbi.c
Normal file
@@ -0,0 +1,710 @@
|
||||
/*
|
||||
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 <linux/slab.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_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)
|
||||
return 0;
|
||||
|
||||
if (urb->status < 0) {
|
||||
print_err_status(dev, -1, urb->status);
|
||||
if (urb->status == -ENOENT)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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 * 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.bulk_ctl.buf == buf)
|
||||
dev->vbi_mode.bulk_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 * 2);
|
||||
|
||||
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.bulk_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);
|
||||
|
||||
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);
|
||||
dev->vbi_mode.bulk_ctl.bulk_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.bulk_ctl.nfields = -1;
|
||||
for (i = 0; i < dev->vbi_mode.bulk_ctl.num_bufs; i++) {
|
||||
urb = dev->vbi_mode.bulk_ctl.urb[i];
|
||||
if (urb) {
|
||||
if (!irqs_disabled())
|
||||
usb_kill_urb(urb);
|
||||
else
|
||||
usb_unlink_urb(urb);
|
||||
|
||||
if (dev->vbi_mode.bulk_ctl.transfer_buffer[i]) {
|
||||
|
||||
kfree(dev->vbi_mode.bulk_ctl.
|
||||
transfer_buffer[i]);
|
||||
dev->vbi_mode.bulk_ctl.transfer_buffer[i] =
|
||||
NULL;
|
||||
}
|
||||
usb_free_urb(urb);
|
||||
dev->vbi_mode.bulk_ctl.urb[i] = NULL;
|
||||
}
|
||||
dev->vbi_mode.bulk_ctl.transfer_buffer[i] = NULL;
|
||||
}
|
||||
|
||||
kfree(dev->vbi_mode.bulk_ctl.urb);
|
||||
kfree(dev->vbi_mode.bulk_ctl.transfer_buffer);
|
||||
|
||||
dev->vbi_mode.bulk_ctl.urb = NULL;
|
||||
dev->vbi_mode.bulk_ctl.transfer_buffer = NULL;
|
||||
dev->vbi_mode.bulk_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 (*bulk_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.bulk_ctl.bulk_copy = bulk_copy;
|
||||
dev->vbi_mode.bulk_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.bulk_ctl.urb = kzalloc(sizeof(void *) * num_bufs,
|
||||
GFP_KERNEL);
|
||||
if (!dev->vbi_mode.bulk_ctl.urb) {
|
||||
cx231xx_errdev("cannot alloc memory for usb buffers\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dev->vbi_mode.bulk_ctl.transfer_buffer =
|
||||
kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL);
|
||||
if (!dev->vbi_mode.bulk_ctl.transfer_buffer) {
|
||||
cx231xx_errdev("cannot allocate memory for usbtransfer\n");
|
||||
kfree(dev->vbi_mode.bulk_ctl.urb);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dev->vbi_mode.bulk_ctl.max_pkt_size = max_pkt_size;
|
||||
dev->vbi_mode.bulk_ctl.buf = NULL;
|
||||
|
||||
sb_size = max_packets * dev->vbi_mode.bulk_ctl.max_pkt_size;
|
||||
|
||||
/* allocate urbs and transfer buffers */
|
||||
for (i = 0; i < dev->vbi_mode.bulk_ctl.num_bufs; i++) {
|
||||
|
||||
urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!urb) {
|
||||
cx231xx_err(DRIVER_NAME
|
||||
": cannot alloc bulk_ctl.urb %i\n", i);
|
||||
cx231xx_uninit_vbi_isoc(dev);
|
||||
return -ENOMEM;
|
||||
}
|
||||
dev->vbi_mode.bulk_ctl.urb[i] = urb;
|
||||
urb->transfer_flags = 0;
|
||||
|
||||
dev->vbi_mode.bulk_ctl.transfer_buffer[i] =
|
||||
kzalloc(sb_size, GFP_KERNEL);
|
||||
if (!dev->vbi_mode.bulk_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.bulk_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.bulk_ctl.num_bufs; i++) {
|
||||
rc = usb_submit_urb(dev->vbi_mode.bulk_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.bulk_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 == -1) {
|
||||
/* Just starting up */
|
||||
cx231xx_reset_vbi_buffer(dev, dma_q);
|
||||
}
|
||||
|
||||
if (dma_q->current_field != field_number)
|
||||
dma_q->lines_completed = 0;
|
||||
|
||||
/* get the buffer pointer */
|
||||
buf = dev->vbi_mode.bulk_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;
|
||||
dma_q->lines_completed = 0;
|
||||
cx231xx_reset_vbi_buffer(dev, dma_q);
|
||||
}
|
||||
}
|
||||
|
||||
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.bulk_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 - Useful for testing for frame/URB loss */
|
||||
outp = videobuf_to_vmalloc(&(*buf)->vb);
|
||||
memset(outp, 0, (*buf)->vb.size);
|
||||
|
||||
dev->vbi_mode.bulk_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.bulk_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.bulk_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;
|
||||
|
||||
if (dma_q->current_field == 2) {
|
||||
/* Populate the second half of the frame */
|
||||
offset += (dev->width * 2 * dma_q->lines_per_field);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
if (dma_q->lines_completed == height && dma_q->current_field == 2)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
65
drivers/media/usb/cx231xx/cx231xx-vbi.h
Normal file
65
drivers/media/usb/cx231xx/cx231xx-vbi.h
Normal 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 (*bulk_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
|
2670
drivers/media/usb/cx231xx/cx231xx-video.c
Normal file
2670
drivers/media/usb/cx231xx/cx231xx-video.c
Normal file
File diff suppressed because it is too large
Load Diff
1012
drivers/media/usb/cx231xx/cx231xx.h
Normal file
1012
drivers/media/usb/cx231xx/cx231xx.h
Normal file
File diff suppressed because it is too large
Load Diff
Viittaa uudesa ongelmassa
Block a user