Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

View file

@ -0,0 +1,93 @@
config VIDEO_CX88
tristate "Conexant 2388x (bt878 successor) support"
depends on VIDEO_DEV && PCI && I2C && RC_CORE
select I2C_ALGOBIT
select VIDEO_BTCX
select VIDEOBUF_DMA_SG
select VIDEO_TUNER
select VIDEO_TVEEPROM
select VIDEO_WM8775 if MEDIA_SUBDRV_AUTOSELECT
---help---
This is a video4linux driver for Conexant 2388x based
TV cards.
To compile this driver as a module, choose M here: the
module will be called cx8800
config VIDEO_CX88_ALSA
tristate "Conexant 2388x DMA audio support"
depends on VIDEO_CX88 && SND
select SND_PCM
---help---
This is a video4linux driver for direct (DMA) audio on
Conexant 2388x based TV cards using ALSA.
It only works with boards with function 01 enabled.
To check if your board supports, use lspci -n.
If supported, you should see 14f1:8801 or 14f1:8811
PCI device.
To compile this driver as a module, choose M here: the
module will be called cx88-alsa.
config VIDEO_CX88_BLACKBIRD
tristate "Blackbird MPEG encoder support (cx2388x + cx23416)"
depends on VIDEO_CX88
select VIDEO_CX2341X
---help---
This adds support for MPEG encoder cards based on the
Blackbird reference design, using the Conexant 2388x
and 23416 chips.
To compile this driver as a module, choose M here: the
module will be called cx88-blackbird.
config VIDEO_CX88_DVB
tristate "DVB/ATSC Support for cx2388x based TV cards"
depends on VIDEO_CX88 && DVB_CORE
select VIDEOBUF_DVB
select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT
select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT
select DVB_OR51132 if MEDIA_SUBDRV_AUTOSELECT
select DVB_CX22702 if MEDIA_SUBDRV_AUTOSELECT
select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT
select DVB_NXT200X if MEDIA_SUBDRV_AUTOSELECT
select DVB_CX24123 if MEDIA_SUBDRV_AUTOSELECT
select DVB_ISL6421 if MEDIA_SUBDRV_AUTOSELECT
select DVB_S5H1411 if MEDIA_SUBDRV_AUTOSELECT
select DVB_CX24116 if MEDIA_SUBDRV_AUTOSELECT
select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT
select DVB_STB6000 if MEDIA_SUBDRV_AUTOSELECT
select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT
select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT
select DVB_DS3000 if MEDIA_SUBDRV_AUTOSELECT
select DVB_TS2020 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT
---help---
This adds support for DVB/ATSC cards based on the
Conexant 2388x chip.
To compile this driver as a module, choose M here: the
module will be called cx88-dvb.
config VIDEO_CX88_ENABLE_VP3054
bool "VP-3054 Secondary I2C Bus Support"
default y
depends on VIDEO_CX88_DVB && DVB_MT352
---help---
This adds DVB-T support for cards based on the
Conexant 2388x chip and the MT352 demodulator,
which also require support for the VP-3054
Secondary I2C bus, such at DNTV Live! DVB-T Pro.
config VIDEO_CX88_VP3054
tristate
depends on VIDEO_CX88_DVB && VIDEO_CX88_ENABLE_VP3054
default y
config VIDEO_CX88_MPEG
tristate
depends on VIDEO_CX88_DVB || VIDEO_CX88_BLACKBIRD
default y

View file

@ -0,0 +1,17 @@
cx88xx-objs := cx88-cards.o cx88-core.o cx88-i2c.o cx88-tvaudio.o \
cx88-dsp.o cx88-input.o
cx8800-objs := cx88-video.o cx88-vbi.o
cx8802-objs := cx88-mpeg.o
obj-$(CONFIG_VIDEO_CX88) += cx88xx.o cx8800.o
obj-$(CONFIG_VIDEO_CX88_MPEG) += cx8802.o
obj-$(CONFIG_VIDEO_CX88_ALSA) += cx88-alsa.o
obj-$(CONFIG_VIDEO_CX88_BLACKBIRD) += cx88-blackbird.o
obj-$(CONFIG_VIDEO_CX88_DVB) += cx88-dvb.o
obj-$(CONFIG_VIDEO_CX88_VP3054) += cx88-vp3054-i2c.o
ccflags-y += -Idrivers/media/i2c
ccflags-y += -Idrivers/media/common
ccflags-y += -Idrivers/media/tuners
ccflags-y += -Idrivers/media/dvb-core
ccflags-y += -Idrivers/media/dvb-frontends

View file

@ -0,0 +1,950 @@
/*
*
* Support for audio capture
* PCI function #1 of the cx2388x.
*
* (c) 2007 Trent Piepho <xyzzy@speakeasy.org>
* (c) 2005,2006 Ricardo Cerqueira <v4l@cerqueira.org>
* (c) 2005 Mauro Carvalho Chehab <mchehab@infradead.org>
* Based on a dummy cx88 module by Gerd Knorr <kraxel@bytesex.org>
* Based on dummy.c by Jaroslav Kysela <perex@perex.cz>
*
* 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/init.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/vmalloc.h>
#include <linux/dma-mapping.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <asm/delay.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/control.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include <media/wm8775.h>
#include "cx88.h"
#include "cx88-reg.h"
#define dprintk(level, fmt, arg...) do { \
if (debug + 1 > level) \
printk(KERN_INFO "%s/1: " fmt, chip->core->name , ## arg);\
} while(0)
#define dprintk_core(level, fmt, arg...) do { \
if (debug + 1 > level) \
printk(KERN_DEBUG "%s/1: " fmt, chip->core->name , ## arg);\
} while(0)
/****************************************************************************
Data type declarations - Can be moded to a header file later
****************************************************************************/
struct cx88_audio_buffer {
unsigned int bpl;
struct btcx_riscmem risc;
struct videobuf_dmabuf dma;
};
struct cx88_audio_dev {
struct cx88_core *core;
struct cx88_dmaqueue q;
/* pci i/o */
struct pci_dev *pci;
/* audio controls */
int irq;
struct snd_card *card;
spinlock_t reg_lock;
atomic_t count;
unsigned int dma_size;
unsigned int period_size;
unsigned int num_periods;
struct videobuf_dmabuf *dma_risc;
struct cx88_audio_buffer *buf;
struct snd_pcm_substream *substream;
};
typedef struct cx88_audio_dev snd_cx88_card_t;
/****************************************************************************
Module global static vars
****************************************************************************/
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static const char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
static bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 1};
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable cx88x soundcard. default enabled.");
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for cx88x capture interface(s).");
/****************************************************************************
Module macros
****************************************************************************/
MODULE_DESCRIPTION("ALSA driver module for cx2388x based TV cards");
MODULE_AUTHOR("Ricardo Cerqueira");
MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
MODULE_LICENSE("GPL");
MODULE_VERSION(CX88_VERSION);
MODULE_SUPPORTED_DEVICE("{{Conexant,23881},"
"{{Conexant,23882},"
"{{Conexant,23883}");
static unsigned int debug;
module_param(debug,int,0644);
MODULE_PARM_DESC(debug,"enable debug messages");
/****************************************************************************
Module specific funtions
****************************************************************************/
/*
* BOARD Specific: Sets audio DMA
*/
static int _cx88_start_audio_dma(snd_cx88_card_t *chip)
{
struct cx88_audio_buffer *buf = chip->buf;
struct cx88_core *core=chip->core;
const struct sram_channel *audio_ch = &cx88_sram_channels[SRAM_CH25];
/* Make sure RISC/FIFO are off before changing FIFO/RISC settings */
cx_clear(MO_AUD_DMACNTRL, 0x11);
/* setup fifo + format - out channel */
cx88_sram_channel_setup(chip->core, audio_ch, buf->bpl, buf->risc.dma);
/* sets bpl size */
cx_write(MO_AUDD_LNGTH, buf->bpl);
/* reset counter */
cx_write(MO_AUDD_GPCNTRL, GP_COUNT_CONTROL_RESET);
atomic_set(&chip->count, 0);
dprintk(1, "Start audio DMA, %d B/line, %d lines/FIFO, %d periods, %d "
"byte buffer\n", buf->bpl, cx_read(audio_ch->cmds_start + 8)>>1,
chip->num_periods, buf->bpl * chip->num_periods);
/* Enables corresponding bits at AUD_INT_STAT */
cx_write(MO_AUD_INTMSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC |
AUD_INT_DN_RISCI2 | AUD_INT_DN_RISCI1);
/* Clean any pending interrupt bits already set */
cx_write(MO_AUD_INTSTAT, ~0);
/* enable audio irqs */
cx_set(MO_PCI_INTMSK, chip->core->pci_irqmask | PCI_INT_AUDINT);
/* start dma */
cx_set(MO_DEV_CNTRL2, (1<<5)); /* Enables Risc Processor */
cx_set(MO_AUD_DMACNTRL, 0x11); /* audio downstream FIFO and RISC enable */
if (debug)
cx88_sram_channel_dump(chip->core, audio_ch);
return 0;
}
/*
* BOARD Specific: Resets audio DMA
*/
static int _cx88_stop_audio_dma(snd_cx88_card_t *chip)
{
struct cx88_core *core=chip->core;
dprintk(1, "Stopping audio DMA\n");
/* stop dma */
cx_clear(MO_AUD_DMACNTRL, 0x11);
/* disable irqs */
cx_clear(MO_PCI_INTMSK, PCI_INT_AUDINT);
cx_clear(MO_AUD_INTMSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC |
AUD_INT_DN_RISCI2 | AUD_INT_DN_RISCI1);
if (debug)
cx88_sram_channel_dump(chip->core, &cx88_sram_channels[SRAM_CH25]);
return 0;
}
#define MAX_IRQ_LOOP 50
/*
* BOARD Specific: IRQ dma bits
*/
static const char *cx88_aud_irqs[32] = {
"dn_risci1", "up_risci1", "rds_dn_risc1", /* 0-2 */
NULL, /* reserved */
"dn_risci2", "up_risci2", "rds_dn_risc2", /* 4-6 */
NULL, /* reserved */
"dnf_of", "upf_uf", "rds_dnf_uf", /* 8-10 */
NULL, /* reserved */
"dn_sync", "up_sync", "rds_dn_sync", /* 12-14 */
NULL, /* reserved */
"opc_err", "par_err", "rip_err", /* 16-18 */
"pci_abort", "ber_irq", "mchg_irq" /* 19-21 */
};
/*
* BOARD Specific: Threats IRQ audio specific calls
*/
static void cx8801_aud_irq(snd_cx88_card_t *chip)
{
struct cx88_core *core = chip->core;
u32 status, mask;
status = cx_read(MO_AUD_INTSTAT);
mask = cx_read(MO_AUD_INTMSK);
if (0 == (status & mask))
return;
cx_write(MO_AUD_INTSTAT, status);
if (debug > 1 || (status & mask & ~0xff))
cx88_print_irqbits(core->name, "irq aud",
cx88_aud_irqs, ARRAY_SIZE(cx88_aud_irqs),
status, mask);
/* risc op code error */
if (status & AUD_INT_OPC_ERR) {
printk(KERN_WARNING "%s/1: Audio risc op code error\n",core->name);
cx_clear(MO_AUD_DMACNTRL, 0x11);
cx88_sram_channel_dump(core, &cx88_sram_channels[SRAM_CH25]);
}
if (status & AUD_INT_DN_SYNC) {
dprintk(1, "Downstream sync error\n");
cx_write(MO_AUDD_GPCNTRL, GP_COUNT_CONTROL_RESET);
return;
}
/* risc1 downstream */
if (status & AUD_INT_DN_RISCI1) {
atomic_set(&chip->count, cx_read(MO_AUDD_GPCNT));
snd_pcm_period_elapsed(chip->substream);
}
/* FIXME: Any other status should deserve a special handling? */
}
/*
* BOARD Specific: Handles IRQ calls
*/
static irqreturn_t cx8801_irq(int irq, void *dev_id)
{
snd_cx88_card_t *chip = dev_id;
struct cx88_core *core = chip->core;
u32 status;
int loop, handled = 0;
for (loop = 0; loop < MAX_IRQ_LOOP; loop++) {
status = cx_read(MO_PCI_INTSTAT) &
(core->pci_irqmask | PCI_INT_AUDINT);
if (0 == status)
goto out;
dprintk(3, "cx8801_irq loop %d/%d, status %x\n",
loop, MAX_IRQ_LOOP, status);
handled = 1;
cx_write(MO_PCI_INTSTAT, status);
if (status & core->pci_irqmask)
cx88_core_irq(core, status);
if (status & PCI_INT_AUDINT)
cx8801_aud_irq(chip);
}
if (MAX_IRQ_LOOP == loop) {
printk(KERN_ERR
"%s/1: IRQ loop detected, disabling interrupts\n",
core->name);
cx_clear(MO_PCI_INTMSK, PCI_INT_AUDINT);
}
out:
return IRQ_RETVAL(handled);
}
static int dsp_buffer_free(snd_cx88_card_t *chip)
{
BUG_ON(!chip->dma_size);
dprintk(2,"Freeing buffer\n");
videobuf_dma_unmap(&chip->pci->dev, chip->dma_risc);
videobuf_dma_free(chip->dma_risc);
btcx_riscmem_free(chip->pci,&chip->buf->risc);
kfree(chip->buf);
chip->dma_risc = NULL;
chip->dma_size = 0;
return 0;
}
/****************************************************************************
ALSA PCM Interface
****************************************************************************/
/*
* Digital hardware definition
*/
#define DEFAULT_FIFO_SIZE 4096
static const struct snd_pcm_hardware snd_cx88_digital_hw = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_48000,
.rate_min = 48000,
.rate_max = 48000,
.channels_min = 2,
.channels_max = 2,
/* Analog audio output will be full of clicks and pops if there
are not exactly four lines in the SRAM FIFO buffer. */
.period_bytes_min = DEFAULT_FIFO_SIZE/4,
.period_bytes_max = DEFAULT_FIFO_SIZE/4,
.periods_min = 1,
.periods_max = 1024,
.buffer_bytes_max = (1024*1024),
};
/*
* audio pcm capture open callback
*/
static int snd_cx88_pcm_open(struct snd_pcm_substream *substream)
{
snd_cx88_card_t *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
if (!chip) {
printk(KERN_ERR "BUG: cx88 can't find device struct."
" Can't proceed with open\n");
return -ENODEV;
}
err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIODS);
if (err < 0)
goto _error;
chip->substream = substream;
runtime->hw = snd_cx88_digital_hw;
if (cx88_sram_channels[SRAM_CH25].fifo_size != DEFAULT_FIFO_SIZE) {
unsigned int bpl = cx88_sram_channels[SRAM_CH25].fifo_size / 4;
bpl &= ~7; /* must be multiple of 8 */
runtime->hw.period_bytes_min = bpl;
runtime->hw.period_bytes_max = bpl;
}
return 0;
_error:
dprintk(1,"Error opening PCM!\n");
return err;
}
/*
* audio close callback
*/
static int snd_cx88_close(struct snd_pcm_substream *substream)
{
return 0;
}
/*
* hw_params callback
*/
static int snd_cx88_hw_params(struct snd_pcm_substream * substream,
struct snd_pcm_hw_params * hw_params)
{
snd_cx88_card_t *chip = snd_pcm_substream_chip(substream);
struct videobuf_dmabuf *dma;
struct cx88_audio_buffer *buf;
int ret;
if (substream->runtime->dma_area) {
dsp_buffer_free(chip);
substream->runtime->dma_area = NULL;
}
chip->period_size = params_period_bytes(hw_params);
chip->num_periods = params_periods(hw_params);
chip->dma_size = chip->period_size * params_periods(hw_params);
BUG_ON(!chip->dma_size);
BUG_ON(chip->num_periods & (chip->num_periods-1));
buf = kzalloc(sizeof(*buf), GFP_KERNEL);
if (NULL == buf)
return -ENOMEM;
buf->bpl = chip->period_size;
dma = &buf->dma;
videobuf_dma_init(dma);
ret = videobuf_dma_init_kernel(dma, PCI_DMA_FROMDEVICE,
(PAGE_ALIGN(chip->dma_size) >> PAGE_SHIFT));
if (ret < 0)
goto error;
ret = videobuf_dma_map(&chip->pci->dev, dma);
if (ret < 0)
goto error;
ret = cx88_risc_databuffer(chip->pci, &buf->risc, dma->sglist,
chip->period_size, chip->num_periods, 1);
if (ret < 0)
goto error;
/* Loop back to start of program */
buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP|RISC_IRQ1|RISC_CNT_INC);
buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
chip->buf = buf;
chip->dma_risc = dma;
substream->runtime->dma_area = chip->dma_risc->vaddr;
substream->runtime->dma_bytes = chip->dma_size;
substream->runtime->dma_addr = 0;
return 0;
error:
kfree(buf);
return ret;
}
/*
* hw free callback
*/
static int snd_cx88_hw_free(struct snd_pcm_substream * substream)
{
snd_cx88_card_t *chip = snd_pcm_substream_chip(substream);
if (substream->runtime->dma_area) {
dsp_buffer_free(chip);
substream->runtime->dma_area = NULL;
}
return 0;
}
/*
* prepare callback
*/
static int snd_cx88_prepare(struct snd_pcm_substream *substream)
{
return 0;
}
/*
* trigger callback
*/
static int snd_cx88_card_trigger(struct snd_pcm_substream *substream, int cmd)
{
snd_cx88_card_t *chip = snd_pcm_substream_chip(substream);
int err;
/* Local interrupts are already disabled by ALSA */
spin_lock(&chip->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
err=_cx88_start_audio_dma(chip);
break;
case SNDRV_PCM_TRIGGER_STOP:
err=_cx88_stop_audio_dma(chip);
break;
default:
err=-EINVAL;
break;
}
spin_unlock(&chip->reg_lock);
return err;
}
/*
* pointer callback
*/
static snd_pcm_uframes_t snd_cx88_pointer(struct snd_pcm_substream *substream)
{
snd_cx88_card_t *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
u16 count;
count = atomic_read(&chip->count);
// dprintk(2, "%s - count %d (+%u), period %d, frame %lu\n", __func__,
// count, new, count & (runtime->periods-1),
// runtime->period_size * (count & (runtime->periods-1)));
return runtime->period_size * (count & (runtime->periods-1));
}
/*
* page callback (needed for mmap)
*/
static struct page *snd_cx88_page(struct snd_pcm_substream *substream,
unsigned long offset)
{
void *pageptr = substream->runtime->dma_area + offset;
return vmalloc_to_page(pageptr);
}
/*
* operators
*/
static struct snd_pcm_ops snd_cx88_pcm_ops = {
.open = snd_cx88_pcm_open,
.close = snd_cx88_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_cx88_hw_params,
.hw_free = snd_cx88_hw_free,
.prepare = snd_cx88_prepare,
.trigger = snd_cx88_card_trigger,
.pointer = snd_cx88_pointer,
.page = snd_cx88_page,
};
/*
* create a PCM device
*/
static int snd_cx88_pcm(snd_cx88_card_t *chip, int device, const char *name)
{
int err;
struct snd_pcm *pcm;
err = snd_pcm_new(chip->card, name, device, 0, 1, &pcm);
if (err < 0)
return err;
pcm->private_data = chip;
strcpy(pcm->name, name);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cx88_pcm_ops);
return 0;
}
/****************************************************************************
CONTROL INTERFACE
****************************************************************************/
static int snd_cx88_volume_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *info)
{
info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
info->count = 2;
info->value.integer.min = 0;
info->value.integer.max = 0x3f;
return 0;
}
static int snd_cx88_volume_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *value)
{
snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
struct cx88_core *core=chip->core;
int vol = 0x3f - (cx_read(AUD_VOL_CTL) & 0x3f),
bal = cx_read(AUD_BAL_CTL);
value->value.integer.value[(bal & 0x40) ? 0 : 1] = vol;
vol -= (bal & 0x3f);
value->value.integer.value[(bal & 0x40) ? 1 : 0] = vol < 0 ? 0 : vol;
return 0;
}
static void snd_cx88_wm8775_volume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *value)
{
snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
struct cx88_core *core = chip->core;
int left = value->value.integer.value[0];
int right = value->value.integer.value[1];
int v, b;
/* Pass volume & balance onto any WM8775 */
if (left >= right) {
v = left << 10;
b = left ? (0x8000 * right) / left : 0x8000;
} else {
v = right << 10;
b = right ? 0xffff - (0x8000 * left) / right : 0x8000;
}
wm8775_s_ctrl(core, V4L2_CID_AUDIO_VOLUME, v);
wm8775_s_ctrl(core, V4L2_CID_AUDIO_BALANCE, b);
}
/* OK - TODO: test it */
static int snd_cx88_volume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *value)
{
snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
struct cx88_core *core=chip->core;
int left, right, v, b;
int changed = 0;
u32 old;
if (core->sd_wm8775)
snd_cx88_wm8775_volume_put(kcontrol, value);
left = value->value.integer.value[0] & 0x3f;
right = value->value.integer.value[1] & 0x3f;
b = right - left;
if (b < 0) {
v = 0x3f - left;
b = (-b) | 0x40;
} else {
v = 0x3f - right;
}
/* Do we really know this will always be called with IRQs on? */
spin_lock_irq(&chip->reg_lock);
old = cx_read(AUD_VOL_CTL);
if (v != (old & 0x3f)) {
cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, (old & ~0x3f) | v);
changed = 1;
}
if ((cx_read(AUD_BAL_CTL) & 0x7f) != b) {
cx_write(AUD_BAL_CTL, b);
changed = 1;
}
spin_unlock_irq(&chip->reg_lock);
return changed;
}
static const DECLARE_TLV_DB_SCALE(snd_cx88_db_scale, -6300, 100, 0);
static const struct snd_kcontrol_new snd_cx88_volume = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
.name = "Analog-TV Volume",
.info = snd_cx88_volume_info,
.get = snd_cx88_volume_get,
.put = snd_cx88_volume_put,
.tlv.p = snd_cx88_db_scale,
};
static int snd_cx88_switch_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *value)
{
snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
struct cx88_core *core = chip->core;
u32 bit = kcontrol->private_value;
value->value.integer.value[0] = !(cx_read(AUD_VOL_CTL) & bit);
return 0;
}
static int snd_cx88_switch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *value)
{
snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
struct cx88_core *core = chip->core;
u32 bit = kcontrol->private_value;
int ret = 0;
u32 vol;
spin_lock_irq(&chip->reg_lock);
vol = cx_read(AUD_VOL_CTL);
if (value->value.integer.value[0] != !(vol & bit)) {
vol ^= bit;
cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, vol);
/* Pass mute onto any WM8775 */
if (core->sd_wm8775 && ((1<<6) == bit))
wm8775_s_ctrl(core, V4L2_CID_AUDIO_MUTE, 0 != (vol & bit));
ret = 1;
}
spin_unlock_irq(&chip->reg_lock);
return ret;
}
static const struct snd_kcontrol_new snd_cx88_dac_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Audio-Out Switch",
.info = snd_ctl_boolean_mono_info,
.get = snd_cx88_switch_get,
.put = snd_cx88_switch_put,
.private_value = (1<<8),
};
static const struct snd_kcontrol_new snd_cx88_source_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Analog-TV Switch",
.info = snd_ctl_boolean_mono_info,
.get = snd_cx88_switch_get,
.put = snd_cx88_switch_put,
.private_value = (1<<6),
};
static int snd_cx88_alc_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *value)
{
snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
struct cx88_core *core = chip->core;
s32 val;
val = wm8775_g_ctrl(core, V4L2_CID_AUDIO_LOUDNESS);
value->value.integer.value[0] = val ? 1 : 0;
return 0;
}
static int snd_cx88_alc_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *value)
{
snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
struct cx88_core *core = chip->core;
struct v4l2_control client_ctl;
memset(&client_ctl, 0, sizeof(client_ctl));
client_ctl.value = 0 != value->value.integer.value[0];
client_ctl.id = V4L2_CID_AUDIO_LOUDNESS;
call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl);
return 0;
}
static struct snd_kcontrol_new snd_cx88_alc_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Line-In ALC Switch",
.info = snd_ctl_boolean_mono_info,
.get = snd_cx88_alc_get,
.put = snd_cx88_alc_put,
};
/****************************************************************************
Basic Flow for Sound Devices
****************************************************************************/
/*
* PCI ID Table - 14f1:8801 and 14f1:8811 means function 1: Audio
* Only boards with eeprom and byte 1 at eeprom=1 have it
*/
static const struct pci_device_id cx88_audio_pci_tbl[] = {
{0x14f1,0x8801,PCI_ANY_ID,PCI_ANY_ID,0,0,0},
{0x14f1,0x8811,PCI_ANY_ID,PCI_ANY_ID,0,0,0},
{0, }
};
MODULE_DEVICE_TABLE(pci, cx88_audio_pci_tbl);
/*
* Chip-specific destructor
*/
static int snd_cx88_free(snd_cx88_card_t *chip)
{
if (chip->irq >= 0)
free_irq(chip->irq, chip);
cx88_core_put(chip->core,chip->pci);
pci_disable_device(chip->pci);
return 0;
}
/*
* Component Destructor
*/
static void snd_cx88_dev_free(struct snd_card * card)
{
snd_cx88_card_t *chip = card->private_data;
snd_cx88_free(chip);
}
/*
* Alsa Constructor - Component probe
*/
static int devno;
static int snd_cx88_create(struct snd_card *card, struct pci_dev *pci,
snd_cx88_card_t **rchip,
struct cx88_core **core_ptr)
{
snd_cx88_card_t *chip;
struct cx88_core *core;
int err;
unsigned char pci_lat;
*rchip = NULL;
err = pci_enable_device(pci);
if (err < 0)
return err;
pci_set_master(pci);
chip = card->private_data;
core = cx88_core_get(pci);
if (NULL == core) {
err = -EINVAL;
return err;
}
if (!pci_dma_supported(pci,DMA_BIT_MASK(32))) {
dprintk(0, "%s/1: Oops: no 32bit PCI DMA ???\n",core->name);
err = -EIO;
cx88_core_put(core, pci);
return err;
}
/* pci init */
chip->card = card;
chip->pci = pci;
chip->irq = -1;
spin_lock_init(&chip->reg_lock);
chip->core = core;
/* get irq */
err = request_irq(chip->pci->irq, cx8801_irq,
IRQF_SHARED, chip->core->name, chip);
if (err < 0) {
dprintk(0, "%s: can't get IRQ %d\n",
chip->core->name, chip->pci->irq);
return err;
}
/* print pci info */
pci_read_config_byte(pci, PCI_LATENCY_TIMER, &pci_lat);
dprintk(1,"ALSA %s/%i: found at %s, rev: %d, irq: %d, "
"latency: %d, mmio: 0x%llx\n", core->name, devno,
pci_name(pci), pci->revision, pci->irq,
pci_lat, (unsigned long long)pci_resource_start(pci,0));
chip->irq = pci->irq;
synchronize_irq(chip->irq);
*rchip = chip;
*core_ptr = core;
return 0;
}
static int cx88_audio_initdev(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
struct snd_card *card;
snd_cx88_card_t *chip;
struct cx88_core *core = NULL;
int err;
if (devno >= SNDRV_CARDS)
return (-ENODEV);
if (!enable[devno]) {
++devno;
return (-ENOENT);
}
err = snd_card_new(&pci->dev, index[devno], id[devno], THIS_MODULE,
sizeof(snd_cx88_card_t), &card);
if (err < 0)
return err;
card->private_free = snd_cx88_dev_free;
err = snd_cx88_create(card, pci, &chip, &core);
if (err < 0)
goto error;
err = snd_cx88_pcm(chip, 0, "CX88 Digital");
if (err < 0)
goto error;
err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_volume, chip));
if (err < 0)
goto error;
err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_dac_switch, chip));
if (err < 0)
goto error;
err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_source_switch, chip));
if (err < 0)
goto error;
/* If there's a wm8775 then add a Line-In ALC switch */
if (core->sd_wm8775)
snd_ctl_add(card, snd_ctl_new1(&snd_cx88_alc_switch, chip));
strcpy (card->driver, "CX88x");
sprintf(card->shortname, "Conexant CX%x", pci->device);
sprintf(card->longname, "%s at %#llx",
card->shortname,(unsigned long long)pci_resource_start(pci, 0));
strcpy (card->mixername, "CX88");
dprintk (0, "%s/%i: ALSA support for cx2388x boards\n",
card->driver,devno);
err = snd_card_register(card);
if (err < 0)
goto error;
pci_set_drvdata(pci,card);
devno++;
return 0;
error:
snd_card_free(card);
return err;
}
/*
* ALSA destructor
*/
static void cx88_audio_finidev(struct pci_dev *pci)
{
struct snd_card *card = pci_get_drvdata(pci);
snd_card_free(card);
devno--;
}
/*
* PCI driver definition
*/
static struct pci_driver cx88_audio_pci_driver = {
.name = "cx88_audio",
.id_table = cx88_audio_pci_tbl,
.probe = cx88_audio_initdev,
.remove = cx88_audio_finidev,
};
module_pci_driver(cx88_audio_pci_driver);

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,322 @@
/*
*
* Stereo and SAP detection for cx88
*
* Copyright (c) 2009 Marton Balint <cus@fazekas.hu>
*
* 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/slab.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/jiffies.h>
#include <asm/div64.h>
#include "cx88.h"
#include "cx88-reg.h"
#define INT_PI ((s32)(3.141592653589 * 32768.0))
#define compat_remainder(a, b) \
((float)(((s32)((a)*100))%((s32)((b)*100)))/100.0)
#define baseband_freq(carrier, srate, tone) ((s32)( \
(compat_remainder(carrier + tone, srate)) / srate * 2 * INT_PI))
/* We calculate the baseband frequencies of the carrier and the pilot tones
* based on the the sampling rate of the audio rds fifo. */
#define FREQ_A2_CARRIER baseband_freq(54687.5, 2689.36, 0.0)
#define FREQ_A2_DUAL baseband_freq(54687.5, 2689.36, 274.1)
#define FREQ_A2_STEREO baseband_freq(54687.5, 2689.36, 117.5)
/* The frequencies below are from the reference driver. They probably need
* further adjustments, because they are not tested at all. You may even need
* to play a bit with the registers of the chip to select the proper signal
* for the input of the audio rds fifo, and measure it's sampling rate to
* calculate the proper baseband frequencies... */
#define FREQ_A2M_CARRIER ((s32)(2.114516 * 32768.0))
#define FREQ_A2M_DUAL ((s32)(2.754916 * 32768.0))
#define FREQ_A2M_STEREO ((s32)(2.462326 * 32768.0))
#define FREQ_EIAJ_CARRIER ((s32)(1.963495 * 32768.0)) /* 5pi/8 */
#define FREQ_EIAJ_DUAL ((s32)(2.562118 * 32768.0))
#define FREQ_EIAJ_STEREO ((s32)(2.601053 * 32768.0))
#define FREQ_BTSC_DUAL ((s32)(1.963495 * 32768.0)) /* 5pi/8 */
#define FREQ_BTSC_DUAL_REF ((s32)(1.374446 * 32768.0)) /* 7pi/16 */
#define FREQ_BTSC_SAP ((s32)(2.471532 * 32768.0))
#define FREQ_BTSC_SAP_REF ((s32)(1.730072 * 32768.0))
/* The spectrum of the signal should be empty between these frequencies. */
#define FREQ_NOISE_START ((s32)(0.100000 * 32768.0))
#define FREQ_NOISE_END ((s32)(1.200000 * 32768.0))
static unsigned int dsp_debug;
module_param(dsp_debug, int, 0644);
MODULE_PARM_DESC(dsp_debug, "enable audio dsp debug messages");
#define dprintk(level, fmt, arg...) if (dsp_debug >= level) \
printk(KERN_DEBUG "%s/0: " fmt, core->name , ## arg)
static s32 int_cos(u32 x)
{
u32 t2, t4, t6, t8;
s32 ret;
u16 period = x / INT_PI;
if (period % 2)
return -int_cos(x - INT_PI);
x = x % INT_PI;
if (x > INT_PI/2)
return -int_cos(INT_PI/2 - (x % (INT_PI/2)));
/* Now x is between 0 and INT_PI/2.
* To calculate cos(x) we use it's Taylor polinom. */
t2 = x*x/32768/2;
t4 = t2*x/32768*x/32768/3/4;
t6 = t4*x/32768*x/32768/5/6;
t8 = t6*x/32768*x/32768/7/8;
ret = 32768-t2+t4-t6+t8;
return ret;
}
static u32 int_goertzel(s16 x[], u32 N, u32 freq)
{
/* We use the Goertzel algorithm to determine the power of the
* given frequency in the signal */
s32 s_prev = 0;
s32 s_prev2 = 0;
s32 coeff = 2*int_cos(freq);
u32 i;
u64 tmp;
u32 divisor;
for (i = 0; i < N; i++) {
s32 s = x[i] + ((s64)coeff*s_prev/32768) - s_prev2;
s_prev2 = s_prev;
s_prev = s;
}
tmp = (s64)s_prev2 * s_prev2 + (s64)s_prev * s_prev -
(s64)coeff * s_prev2 * s_prev / 32768;
/* XXX: N must be low enough so that N*N fits in s32.
* Else we need two divisions. */
divisor = N * N;
do_div(tmp, divisor);
return (u32) tmp;
}
static u32 freq_magnitude(s16 x[], u32 N, u32 freq)
{
u32 sum = int_goertzel(x, N, freq);
return (u32)int_sqrt(sum);
}
static u32 noise_magnitude(s16 x[], u32 N, u32 freq_start, u32 freq_end)
{
int i;
u32 sum = 0;
u32 freq_step;
int samples = 5;
if (N > 192) {
/* The last 192 samples are enough for noise detection */
x += (N-192);
N = 192;
}
freq_step = (freq_end - freq_start) / (samples - 1);
for (i = 0; i < samples; i++) {
sum += int_goertzel(x, N, freq_start);
freq_start += freq_step;
}
return (u32)int_sqrt(sum / samples);
}
static s32 detect_a2_a2m_eiaj(struct cx88_core *core, s16 x[], u32 N)
{
s32 carrier, stereo, dual, noise;
s32 carrier_freq, stereo_freq, dual_freq;
s32 ret;
switch (core->tvaudio) {
case WW_BG:
case WW_DK:
carrier_freq = FREQ_A2_CARRIER;
stereo_freq = FREQ_A2_STEREO;
dual_freq = FREQ_A2_DUAL;
break;
case WW_M:
carrier_freq = FREQ_A2M_CARRIER;
stereo_freq = FREQ_A2M_STEREO;
dual_freq = FREQ_A2M_DUAL;
break;
case WW_EIAJ:
carrier_freq = FREQ_EIAJ_CARRIER;
stereo_freq = FREQ_EIAJ_STEREO;
dual_freq = FREQ_EIAJ_DUAL;
break;
default:
printk(KERN_WARNING "%s/0: unsupported audio mode %d for %s\n",
core->name, core->tvaudio, __func__);
return UNSET;
}
carrier = freq_magnitude(x, N, carrier_freq);
stereo = freq_magnitude(x, N, stereo_freq);
dual = freq_magnitude(x, N, dual_freq);
noise = noise_magnitude(x, N, FREQ_NOISE_START, FREQ_NOISE_END);
dprintk(1, "detect a2/a2m/eiaj: carrier=%d, stereo=%d, dual=%d, "
"noise=%d\n", carrier, stereo, dual, noise);
if (stereo > dual)
ret = V4L2_TUNER_SUB_STEREO;
else
ret = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
if (core->tvaudio == WW_EIAJ) {
/* EIAJ checks may need adjustments */
if ((carrier > max(stereo, dual)*2) &&
(carrier < max(stereo, dual)*6) &&
(carrier > 20 && carrier < 200) &&
(max(stereo, dual) > min(stereo, dual))) {
/* For EIAJ the carrier is always present,
so we probably don't need noise detection */
return ret;
}
} else {
if ((carrier > max(stereo, dual)*2) &&
(carrier < max(stereo, dual)*8) &&
(carrier > 20 && carrier < 200) &&
(noise < 10) &&
(max(stereo, dual) > min(stereo, dual)*2)) {
return ret;
}
}
return V4L2_TUNER_SUB_MONO;
}
static s32 detect_btsc(struct cx88_core *core, s16 x[], u32 N)
{
s32 sap_ref = freq_magnitude(x, N, FREQ_BTSC_SAP_REF);
s32 sap = freq_magnitude(x, N, FREQ_BTSC_SAP);
s32 dual_ref = freq_magnitude(x, N, FREQ_BTSC_DUAL_REF);
s32 dual = freq_magnitude(x, N, FREQ_BTSC_DUAL);
dprintk(1, "detect btsc: dual_ref=%d, dual=%d, sap_ref=%d, sap=%d"
"\n", dual_ref, dual, sap_ref, sap);
/* FIXME: Currently not supported */
return UNSET;
}
static s16 *read_rds_samples(struct cx88_core *core, u32 *N)
{
const struct sram_channel *srch = &cx88_sram_channels[SRAM_CH27];
s16 *samples;
unsigned int i;
unsigned int bpl = srch->fifo_size/AUD_RDS_LINES;
unsigned int spl = bpl/4;
unsigned int sample_count = spl*(AUD_RDS_LINES-1);
u32 current_address = cx_read(srch->ptr1_reg);
u32 offset = (current_address - srch->fifo_start + bpl);
dprintk(1, "read RDS samples: current_address=%08x (offset=%08x), "
"sample_count=%d, aud_intstat=%08x\n", current_address,
current_address - srch->fifo_start, sample_count,
cx_read(MO_AUD_INTSTAT));
samples = kmalloc(sizeof(s16)*sample_count, GFP_KERNEL);
if (!samples)
return NULL;
*N = sample_count;
for (i = 0; i < sample_count; i++) {
offset = offset % (AUD_RDS_LINES*bpl);
samples[i] = cx_read(srch->fifo_start + offset);
offset += 4;
}
if (dsp_debug >= 2) {
dprintk(2, "RDS samples dump: ");
for (i = 0; i < sample_count; i++)
printk("%hd ", samples[i]);
printk(".\n");
}
return samples;
}
s32 cx88_dsp_detect_stereo_sap(struct cx88_core *core)
{
s16 *samples;
u32 N = 0;
s32 ret = UNSET;
/* If audio RDS fifo is disabled, we can't read the samples */
if (!(cx_read(MO_AUD_DMACNTRL) & 0x04))
return ret;
if (!(cx_read(AUD_CTL) & EN_FMRADIO_EN_RDS))
return ret;
/* Wait at least 500 ms after an audio standard change */
if (time_before(jiffies, core->last_change + msecs_to_jiffies(500)))
return ret;
samples = read_rds_samples(core, &N);
if (!samples)
return ret;
switch (core->tvaudio) {
case WW_BG:
case WW_DK:
case WW_EIAJ:
case WW_M:
ret = detect_a2_a2m_eiaj(core, samples, N);
break;
case WW_BTSC:
ret = detect_btsc(core, samples, N);
break;
case WW_NONE:
case WW_I:
case WW_L:
case WW_I2SPT:
case WW_FM:
case WW_I2SADC:
break;
}
kfree(samples);
if (UNSET != ret)
dprintk(1, "stereo/sap detection result:%s%s%s\n",
(ret & V4L2_TUNER_SUB_MONO) ? " mono" : "",
(ret & V4L2_TUNER_SUB_STEREO) ? " stereo" : "",
(ret & V4L2_TUNER_SUB_LANG2) ? " dual" : "");
return ret;
}
EXPORT_SYMBOL(cx88_dsp_detect_stereo_sap);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,183 @@
/*
cx88-i2c.c -- all the i2c code is here
Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
& Marcus Metzler (mocm@thp.uni-koeln.de)
(c) 2002 Yurij Sysoev <yurij@naturesoft.net>
(c) 1999-2003 Gerd Knorr <kraxel@bytesex.org>
(c) 2005 Mauro Carvalho Chehab <mchehab@infradead.org>
- Multituner support and i2c address binding
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/init.h>
#include <asm/io.h>
#include "cx88.h"
#include <media/v4l2-common.h>
static unsigned int i2c_debug;
module_param(i2c_debug, int, 0644);
MODULE_PARM_DESC(i2c_debug,"enable debug messages [i2c]");
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_udelay = 5;
module_param(i2c_udelay, int, 0644);
MODULE_PARM_DESC(i2c_udelay,"i2c delay at insmod time, in usecs "
"(should be 5 or higher). Lower value means higher bus speed.");
#define dprintk(level,fmt, arg...) if (i2c_debug >= level) \
printk(KERN_DEBUG "%s: " fmt, core->name , ## arg)
/* ----------------------------------------------------------------------- */
static void cx8800_bit_setscl(void *data, int state)
{
struct cx88_core *core = data;
if (state)
core->i2c_state |= 0x02;
else
core->i2c_state &= ~0x02;
cx_write(MO_I2C, core->i2c_state);
cx_read(MO_I2C);
}
static void cx8800_bit_setsda(void *data, int state)
{
struct cx88_core *core = data;
if (state)
core->i2c_state |= 0x01;
else
core->i2c_state &= ~0x01;
cx_write(MO_I2C, core->i2c_state);
cx_read(MO_I2C);
}
static int cx8800_bit_getscl(void *data)
{
struct cx88_core *core = data;
u32 state;
state = cx_read(MO_I2C);
return state & 0x02 ? 1 : 0;
}
static int cx8800_bit_getsda(void *data)
{
struct cx88_core *core = data;
u32 state;
state = cx_read(MO_I2C);
return state & 0x01;
}
/* ----------------------------------------------------------------------- */
static const struct i2c_algo_bit_data cx8800_i2c_algo_template = {
.setsda = cx8800_bit_setsda,
.setscl = cx8800_bit_setscl,
.getsda = cx8800_bit_getsda,
.getscl = cx8800_bit_getscl,
.udelay = 16,
.timeout = 200,
};
/* ----------------------------------------------------------------------- */
static const char * const i2c_devs[128] = {
[ 0x1c >> 1 ] = "lgdt330x",
[ 0x86 >> 1 ] = "tda9887/cx22702",
[ 0xa0 >> 1 ] = "eeprom",
[ 0xc0 >> 1 ] = "tuner (analog)",
[ 0xc2 >> 1 ] = "tuner (analog/dvb)",
[ 0xc8 >> 1 ] = "xc5000",
};
static void do_i2c_scan(const char *name, struct i2c_client *c)
{
unsigned char buf;
int i,rc;
for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) {
c->addr = i;
rc = i2c_master_recv(c,&buf,0);
if (rc < 0)
continue;
printk("%s: i2c scan: found device @ 0x%x [%s]\n",
name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???");
}
}
/* init + register i2c adapter */
int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci)
{
/* Prevents usage of invalid delay values */
if (i2c_udelay<5)
i2c_udelay=5;
core->i2c_algo = cx8800_i2c_algo_template;
core->i2c_adap.dev.parent = &pci->dev;
strlcpy(core->i2c_adap.name,core->name,sizeof(core->i2c_adap.name));
core->i2c_adap.owner = THIS_MODULE;
core->i2c_algo.udelay = i2c_udelay;
core->i2c_algo.data = core;
i2c_set_adapdata(&core->i2c_adap, &core->v4l2_dev);
core->i2c_adap.algo_data = &core->i2c_algo;
core->i2c_client.adapter = &core->i2c_adap;
strlcpy(core->i2c_client.name, "cx88xx internal", I2C_NAME_SIZE);
cx8800_bit_setscl(core,1);
cx8800_bit_setsda(core,1);
core->i2c_rc = i2c_bit_add_bus(&core->i2c_adap);
if (0 == core->i2c_rc) {
static u8 tuner_data[] =
{ 0x0b, 0xdc, 0x86, 0x52 };
static struct i2c_msg tuner_msg =
{ .flags = 0, .addr = 0xc2 >> 1, .buf = tuner_data, .len = 4 };
dprintk(1, "i2c register ok\n");
switch( core->boardnr ) {
case CX88_BOARD_HAUPPAUGE_HVR1300:
case CX88_BOARD_HAUPPAUGE_HVR3000:
case CX88_BOARD_HAUPPAUGE_HVR4000:
printk("%s: i2c init: enabling analog demod on HVR1300/3000/4000 tuner\n",
core->name);
i2c_transfer(core->i2c_client.adapter, &tuner_msg, 1);
break;
default:
break;
}
if (i2c_scan)
do_i2c_scan(core->name,&core->i2c_client);
} else
printk("%s: i2c register FAILED\n", core->name);
return core->i2c_rc;
}

View file

@ -0,0 +1,653 @@
/*
*
* Device driver for GPIO attached remote control interfaces
* on Conexant 2388x based TV/DVB cards.
*
* Copyright (c) 2003 Pavel Machek
* Copyright (c) 2004 Gerd Knorr
* Copyright (c) 2004, 2005 Chris Pascoe
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
#include <linux/hrtimer.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/module.h>
#include "cx88.h"
#include <media/rc-core.h>
#define MODULE_NAME "cx88xx"
/* ---------------------------------------------------------------------- */
struct cx88_IR {
struct cx88_core *core;
struct rc_dev *dev;
int users;
char name[32];
char phys[32];
/* sample from gpio pin 16 */
u32 sampling;
/* poll external decoder */
int polling;
struct hrtimer timer;
u32 gpio_addr;
u32 last_gpio;
u32 mask_keycode;
u32 mask_keydown;
u32 mask_keyup;
};
static unsigned ir_samplerate = 4;
module_param(ir_samplerate, uint, 0444);
MODULE_PARM_DESC(ir_samplerate, "IR samplerate in kHz, 1 - 20, default 4");
static int ir_debug;
module_param(ir_debug, int, 0644); /* debug level [IR] */
MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]");
#define ir_dprintk(fmt, arg...) if (ir_debug) \
printk(KERN_DEBUG "%s IR: " fmt , ir->core->name , ##arg)
#define dprintk(fmt, arg...) if (ir_debug) \
printk(KERN_DEBUG "cx88 IR: " fmt , ##arg)
/* ---------------------------------------------------------------------- */
static void cx88_ir_handle_key(struct cx88_IR *ir)
{
struct cx88_core *core = ir->core;
u32 gpio, data, auxgpio;
/* read gpio value */
gpio = cx_read(ir->gpio_addr);
switch (core->boardnr) {
case CX88_BOARD_NPGTECH_REALTV_TOP10FM:
/* This board apparently uses a combination of 2 GPIO
to represent the keys. Additionally, the second GPIO
can be used for parity.
Example:
for key "5"
gpio = 0x758, auxgpio = 0xe5 or 0xf5
for key "Power"
gpio = 0x758, auxgpio = 0xed or 0xfd
*/
auxgpio = cx_read(MO_GP1_IO);
/* Take out the parity part */
gpio=(gpio & 0x7fd) + (auxgpio & 0xef);
break;
case CX88_BOARD_WINFAST_DTV1000:
case CX88_BOARD_WINFAST_DTV1800H:
case CX88_BOARD_WINFAST_DTV1800H_XC4000:
case CX88_BOARD_WINFAST_DTV2000H_PLUS:
case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL:
case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36:
case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43:
gpio = (gpio & 0x6ff) | ((cx_read(MO_GP1_IO) << 8) & 0x900);
auxgpio = gpio;
break;
default:
auxgpio = gpio;
}
if (ir->polling) {
if (ir->last_gpio == auxgpio)
return;
ir->last_gpio = auxgpio;
}
/* extract data */
data = ir_extract_bits(gpio, ir->mask_keycode);
ir_dprintk("irq gpio=0x%x code=%d | %s%s%s\n",
gpio, data,
ir->polling ? "poll" : "irq",
(gpio & ir->mask_keydown) ? " down" : "",
(gpio & ir->mask_keyup) ? " up" : "");
if (ir->core->boardnr == CX88_BOARD_NORWOOD_MICRO) {
u32 gpio_key = cx_read(MO_GP0_IO);
data = (data << 4) | ((gpio_key & 0xf0) >> 4);
rc_keydown(ir->dev, RC_TYPE_UNKNOWN, data, 0);
} else if (ir->core->boardnr == CX88_BOARD_PROLINK_PLAYTVPVR ||
ir->core->boardnr == CX88_BOARD_PIXELVIEW_PLAYTV_ULTRA_PRO) {
/* bit cleared on keydown, NEC scancode, 0xAAAACC, A = 0x866b */
u16 addr;
u8 cmd;
u32 scancode;
addr = (data >> 8) & 0xffff;
cmd = (data >> 0) & 0x00ff;
scancode = RC_SCANCODE_NECX(addr, cmd);
if (0 == (gpio & ir->mask_keyup))
rc_keydown_notimeout(ir->dev, RC_TYPE_NEC, scancode, 0);
else
rc_keyup(ir->dev);
} else if (ir->mask_keydown) {
/* bit set on keydown */
if (gpio & ir->mask_keydown)
rc_keydown_notimeout(ir->dev, RC_TYPE_UNKNOWN, data, 0);
else
rc_keyup(ir->dev);
} else if (ir->mask_keyup) {
/* bit cleared on keydown */
if (0 == (gpio & ir->mask_keyup))
rc_keydown_notimeout(ir->dev, RC_TYPE_UNKNOWN, data, 0);
else
rc_keyup(ir->dev);
} else {
/* can't distinguish keydown/up :-/ */
rc_keydown_notimeout(ir->dev, RC_TYPE_UNKNOWN, data, 0);
rc_keyup(ir->dev);
}
}
static enum hrtimer_restart cx88_ir_work(struct hrtimer *timer)
{
unsigned long missed;
struct cx88_IR *ir = container_of(timer, struct cx88_IR, timer);
cx88_ir_handle_key(ir);
missed = hrtimer_forward_now(&ir->timer,
ktime_set(0, ir->polling * 1000000));
if (missed > 1)
ir_dprintk("Missed ticks %ld\n", missed - 1);
return HRTIMER_RESTART;
}
static int __cx88_ir_start(void *priv)
{
struct cx88_core *core = priv;
struct cx88_IR *ir;
if (!core || !core->ir)
return -EINVAL;
ir = core->ir;
if (ir->polling) {
hrtimer_init(&ir->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
ir->timer.function = cx88_ir_work;
hrtimer_start(&ir->timer,
ktime_set(0, ir->polling * 1000000),
HRTIMER_MODE_REL);
}
if (ir->sampling) {
core->pci_irqmask |= PCI_INT_IR_SMPINT;
cx_write(MO_DDS_IO, 0x33F286 * ir_samplerate); /* samplerate */
cx_write(MO_DDSCFG_IO, 0x5); /* enable */
}
return 0;
}
static void __cx88_ir_stop(void *priv)
{
struct cx88_core *core = priv;
struct cx88_IR *ir;
if (!core || !core->ir)
return;
ir = core->ir;
if (ir->sampling) {
cx_write(MO_DDSCFG_IO, 0x0);
core->pci_irqmask &= ~PCI_INT_IR_SMPINT;
}
if (ir->polling)
hrtimer_cancel(&ir->timer);
}
int cx88_ir_start(struct cx88_core *core)
{
if (core->ir->users)
return __cx88_ir_start(core);
return 0;
}
void cx88_ir_stop(struct cx88_core *core)
{
if (core->ir->users)
__cx88_ir_stop(core);
}
static int cx88_ir_open(struct rc_dev *rc)
{
struct cx88_core *core = rc->priv;
core->ir->users++;
return __cx88_ir_start(core);
}
static void cx88_ir_close(struct rc_dev *rc)
{
struct cx88_core *core = rc->priv;
core->ir->users--;
if (!core->ir->users)
__cx88_ir_stop(core);
}
/* ---------------------------------------------------------------------- */
int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
{
struct cx88_IR *ir;
struct rc_dev *dev;
char *ir_codes = NULL;
u64 rc_type = RC_BIT_OTHER;
int err = -ENOMEM;
u32 hardware_mask = 0; /* For devices with a hardware mask, when
* used with a full-code IR table
*/
ir = kzalloc(sizeof(*ir), GFP_KERNEL);
dev = rc_allocate_device();
if (!ir || !dev)
goto err_out_free;
ir->dev = dev;
/* detect & configure */
switch (core->boardnr) {
case CX88_BOARD_DNTV_LIVE_DVB_T:
case CX88_BOARD_KWORLD_DVB_T:
case CX88_BOARD_KWORLD_DVB_T_CX22702:
ir_codes = RC_MAP_DNTV_LIVE_DVB_T;
ir->gpio_addr = MO_GP1_IO;
ir->mask_keycode = 0x1f;
ir->mask_keyup = 0x60;
ir->polling = 50; /* ms */
break;
case CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1:
ir_codes = RC_MAP_CINERGY_1400;
ir->sampling = 0xeb04; /* address */
break;
case CX88_BOARD_HAUPPAUGE:
case CX88_BOARD_HAUPPAUGE_DVB_T1:
case CX88_BOARD_HAUPPAUGE_NOVASE2_S1:
case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1:
case CX88_BOARD_HAUPPAUGE_HVR1100:
case CX88_BOARD_HAUPPAUGE_HVR3000:
case CX88_BOARD_HAUPPAUGE_HVR4000:
case CX88_BOARD_HAUPPAUGE_HVR4000LITE:
case CX88_BOARD_PCHDTV_HD3000:
case CX88_BOARD_PCHDTV_HD5500:
case CX88_BOARD_HAUPPAUGE_IRONLY:
ir_codes = RC_MAP_HAUPPAUGE;
ir->sampling = 1;
break;
case CX88_BOARD_WINFAST_DTV2000H:
case CX88_BOARD_WINFAST_DTV2000H_J:
case CX88_BOARD_WINFAST_DTV1800H:
case CX88_BOARD_WINFAST_DTV1800H_XC4000:
case CX88_BOARD_WINFAST_DTV2000H_PLUS:
ir_codes = RC_MAP_WINFAST;
ir->gpio_addr = MO_GP0_IO;
ir->mask_keycode = 0x8f8;
ir->mask_keyup = 0x100;
ir->polling = 50; /* ms */
break;
case CX88_BOARD_WINFAST2000XP_EXPERT:
case CX88_BOARD_WINFAST_DTV1000:
case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL:
case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36:
case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43:
ir_codes = RC_MAP_WINFAST;
ir->gpio_addr = MO_GP0_IO;
ir->mask_keycode = 0x8f8;
ir->mask_keyup = 0x100;
ir->polling = 1; /* ms */
break;
case CX88_BOARD_IODATA_GVBCTV7E:
ir_codes = RC_MAP_IODATA_BCTV7E;
ir->gpio_addr = MO_GP0_IO;
ir->mask_keycode = 0xfd;
ir->mask_keydown = 0x02;
ir->polling = 5; /* ms */
break;
case CX88_BOARD_PROLINK_PLAYTVPVR:
case CX88_BOARD_PIXELVIEW_PLAYTV_ULTRA_PRO:
/*
* It seems that this hardware is paired with NEC extended
* address 0x866b. So, unfortunately, its usage with other
* IR's with different address won't work. Still, there are
* other IR's from the same manufacturer that works, like the
* 002-T mini RC, provided with newer PV hardware
*/
ir_codes = RC_MAP_PIXELVIEW_MK12;
rc_type = RC_BIT_NEC;
ir->gpio_addr = MO_GP1_IO;
ir->mask_keyup = 0x80;
ir->polling = 10; /* ms */
hardware_mask = 0x3f; /* Hardware returns only 6 bits from command part */
break;
case CX88_BOARD_PROLINK_PV_8000GT:
case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME:
ir_codes = RC_MAP_PIXELVIEW_NEW;
ir->gpio_addr = MO_GP1_IO;
ir->mask_keycode = 0x3f;
ir->mask_keyup = 0x80;
ir->polling = 1; /* ms */
break;
case CX88_BOARD_KWORLD_LTV883:
ir_codes = RC_MAP_PIXELVIEW;
ir->gpio_addr = MO_GP1_IO;
ir->mask_keycode = 0x1f;
ir->mask_keyup = 0x60;
ir->polling = 1; /* ms */
break;
case CX88_BOARD_ADSTECH_DVB_T_PCI:
ir_codes = RC_MAP_ADSTECH_DVB_T_PCI;
ir->gpio_addr = MO_GP1_IO;
ir->mask_keycode = 0xbf;
ir->mask_keyup = 0x40;
ir->polling = 50; /* ms */
break;
case CX88_BOARD_MSI_TVANYWHERE_MASTER:
ir_codes = RC_MAP_MSI_TVANYWHERE;
ir->gpio_addr = MO_GP1_IO;
ir->mask_keycode = 0x1f;
ir->mask_keyup = 0x40;
ir->polling = 1; /* ms */
break;
case CX88_BOARD_AVERTV_303:
case CX88_BOARD_AVERTV_STUDIO_303:
ir_codes = RC_MAP_AVERTV_303;
ir->gpio_addr = MO_GP2_IO;
ir->mask_keycode = 0xfb;
ir->mask_keydown = 0x02;
ir->polling = 50; /* ms */
break;
case CX88_BOARD_OMICOM_SS4_PCI:
case CX88_BOARD_SATTRADE_ST4200:
case CX88_BOARD_TBS_8920:
case CX88_BOARD_TBS_8910:
case CX88_BOARD_PROF_7300:
case CX88_BOARD_PROF_7301:
case CX88_BOARD_PROF_6200:
ir_codes = RC_MAP_TBS_NEC;
ir->sampling = 0xff00; /* address */
break;
case CX88_BOARD_TEVII_S464:
case CX88_BOARD_TEVII_S460:
case CX88_BOARD_TEVII_S420:
ir_codes = RC_MAP_TEVII_NEC;
ir->sampling = 0xff00; /* address */
break;
case CX88_BOARD_DNTV_LIVE_DVB_T_PRO:
ir_codes = RC_MAP_DNTV_LIVE_DVBT_PRO;
ir->sampling = 0xff00; /* address */
break;
case CX88_BOARD_NORWOOD_MICRO:
ir_codes = RC_MAP_NORWOOD;
ir->gpio_addr = MO_GP1_IO;
ir->mask_keycode = 0x0e;
ir->mask_keyup = 0x80;
ir->polling = 50; /* ms */
break;
case CX88_BOARD_NPGTECH_REALTV_TOP10FM:
ir_codes = RC_MAP_NPGTECH;
ir->gpio_addr = MO_GP0_IO;
ir->mask_keycode = 0xfa;
ir->polling = 50; /* ms */
break;
case CX88_BOARD_PINNACLE_PCTV_HD_800i:
ir_codes = RC_MAP_PINNACLE_PCTV_HD;
ir->sampling = 1;
break;
case CX88_BOARD_POWERCOLOR_REAL_ANGEL:
ir_codes = RC_MAP_POWERCOLOR_REAL_ANGEL;
ir->gpio_addr = MO_GP2_IO;
ir->mask_keycode = 0x7e;
ir->polling = 100; /* ms */
break;
case CX88_BOARD_TWINHAN_VP1027_DVBS:
ir_codes = RC_MAP_TWINHAN_VP1027_DVBS;
ir->sampling = 0xff00; /* address */
break;
}
if (!ir_codes) {
err = -ENODEV;
goto err_out_free;
}
/*
* The usage of mask_keycode were very convenient, due to several
* reasons. Among others, the scancode tables were using the scancode
* as the index elements. So, the less bits it was used, the smaller
* the table were stored. After the input changes, the better is to use
* the full scancodes, since it allows replacing the IR remote by
* another one. Unfortunately, there are still some hardware, like
* Pixelview Ultra Pro, where only part of the scancode is sent via
* GPIO. So, there's no way to get the full scancode. Due to that,
* hardware_mask were introduced here: it represents those hardware
* that has such limits.
*/
if (hardware_mask && !ir->mask_keycode)
ir->mask_keycode = hardware_mask;
/* init input device */
snprintf(ir->name, sizeof(ir->name), "cx88 IR (%s)", core->board.name);
snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(pci));
dev->input_name = ir->name;
dev->input_phys = ir->phys;
dev->input_id.bustype = BUS_PCI;
dev->input_id.version = 1;
if (pci->subsystem_vendor) {
dev->input_id.vendor = pci->subsystem_vendor;
dev->input_id.product = pci->subsystem_device;
} else {
dev->input_id.vendor = pci->vendor;
dev->input_id.product = pci->device;
}
dev->dev.parent = &pci->dev;
dev->map_name = ir_codes;
dev->driver_name = MODULE_NAME;
dev->priv = core;
dev->open = cx88_ir_open;
dev->close = cx88_ir_close;
dev->scancode_mask = hardware_mask;
if (ir->sampling) {
dev->driver_type = RC_DRIVER_IR_RAW;
dev->timeout = 10 * 1000 * 1000; /* 10 ms */
} else {
dev->driver_type = RC_DRIVER_SCANCODE;
dev->allowed_protocols = rc_type;
}
ir->core = core;
core->ir = ir;
/* all done */
err = rc_register_device(dev);
if (err)
goto err_out_free;
return 0;
err_out_free:
rc_free_device(dev);
core->ir = NULL;
kfree(ir);
return err;
}
int cx88_ir_fini(struct cx88_core *core)
{
struct cx88_IR *ir = core->ir;
/* skip detach on non attached boards */
if (NULL == ir)
return 0;
cx88_ir_stop(core);
rc_unregister_device(ir->dev);
kfree(ir);
/* done */
core->ir = NULL;
return 0;
}
/* ---------------------------------------------------------------------- */
void cx88_ir_irq(struct cx88_core *core)
{
struct cx88_IR *ir = core->ir;
u32 samples;
unsigned todo, bits;
struct ir_raw_event ev;
if (!ir || !ir->sampling)
return;
/*
* Samples are stored in a 32 bit register, oldest sample in
* the msb. A set bit represents space and an unset bit
* represents a pulse.
*/
samples = cx_read(MO_SAMPLE_IO);
if (samples == 0xff && ir->dev->idle)
return;
init_ir_raw_event(&ev);
for (todo = 32; todo > 0; todo -= bits) {
ev.pulse = samples & 0x80000000 ? false : true;
bits = min(todo, 32U - fls(ev.pulse ? samples : ~samples));
ev.duration = (bits * (NSEC_PER_SEC / 1000)) / ir_samplerate;
ir_raw_event_store_with_filter(ir->dev, &ev);
samples <<= bits;
}
ir_raw_event_handle(ir->dev);
}
static int get_key_pvr2000(struct IR_i2c *ir, enum rc_type *protocol,
u32 *scancode, u8 *toggle)
{
int flags, code;
/* poll IR chip */
flags = i2c_smbus_read_byte_data(ir->c, 0x10);
if (flags < 0) {
dprintk("read error\n");
return 0;
}
/* key pressed ? */
if (0 == (flags & 0x80))
return 0;
/* read actual key code */
code = i2c_smbus_read_byte_data(ir->c, 0x00);
if (code < 0) {
dprintk("read error\n");
return 0;
}
dprintk("IR Key/Flags: (0x%02x/0x%02x)\n",
code & 0xff, flags & 0xff);
*protocol = RC_TYPE_UNKNOWN;
*scancode = code & 0xff;
*toggle = 0;
return 1;
}
void cx88_i2c_init_ir(struct cx88_core *core)
{
struct i2c_board_info info;
const unsigned short default_addr_list[] = {
0x18, 0x6b, 0x71,
I2C_CLIENT_END
};
const unsigned short pvr2000_addr_list[] = {
0x18, 0x1a,
I2C_CLIENT_END
};
const unsigned short *addr_list = default_addr_list;
const unsigned short *addrp;
/* Instantiate the IR receiver device, if present */
if (0 != core->i2c_rc)
return;
memset(&info, 0, sizeof(struct i2c_board_info));
strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
switch (core->boardnr) {
case CX88_BOARD_LEADTEK_PVR2000:
addr_list = pvr2000_addr_list;
core->init_data.name = "cx88 Leadtek PVR 2000 remote";
core->init_data.type = RC_BIT_UNKNOWN;
core->init_data.get_key = get_key_pvr2000;
core->init_data.ir_codes = RC_MAP_EMPTY;
break;
}
/*
* We can't call i2c_new_probed_device() because it uses
* quick writes for probing and at least some RC receiver
* devices only reply to reads.
* Also, Hauppauge XVR needs to be specified, as address 0x71
* conflicts with another remote type used with saa7134
*/
for (addrp = addr_list; *addrp != I2C_CLIENT_END; addrp++) {
info.platform_data = NULL;
memset(&core->init_data, 0, sizeof(core->init_data));
if (*addrp == 0x71) {
/* Hauppauge XVR */
core->init_data.name = "cx88 Hauppauge XVR remote";
core->init_data.ir_codes = RC_MAP_HAUPPAUGE;
core->init_data.type = RC_BIT_RC5;
core->init_data.internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR;
info.platform_data = &core->init_data;
}
if (i2c_smbus_xfer(&core->i2c_adap, *addrp, 0,
I2C_SMBUS_READ, 0,
I2C_SMBUS_QUICK, NULL) >= 0) {
info.addr = *addrp;
i2c_new_device(&core->i2c_adap, &info);
break;
}
}
}
/* ---------------------------------------------------------------------- */
MODULE_AUTHOR("Gerd Knorr, Pavel Machek, Chris Pascoe");
MODULE_DESCRIPTION("input driver for cx88 GPIO-based IR remote controls");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,920 @@
/*
*
* Support for the mpeg transport stream transfers
* PCI function #2 of the cx2388x.
*
* (c) 2004 Jelle Foks <jelle@foks.us>
* (c) 2004 Chris Pascoe <c.pascoe@itee.uq.edu.au>
* (c) 2004 Gerd Knorr <kraxel@bytesex.org>
*
* 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/slab.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <asm/delay.h>
#include "cx88.h"
/* ------------------------------------------------------------------ */
MODULE_DESCRIPTION("mpeg driver for cx2388x based TV cards");
MODULE_AUTHOR("Jelle Foks <jelle@foks.us>");
MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
MODULE_LICENSE("GPL");
MODULE_VERSION(CX88_VERSION);
static unsigned int debug;
module_param(debug,int,0644);
MODULE_PARM_DESC(debug,"enable debug messages [mpeg]");
#define dprintk(level, fmt, arg...) do { \
if (debug + 1 > level) \
printk(KERN_DEBUG "%s/2-mpeg: " fmt, dev->core->name, ## arg); \
} while(0)
#define mpeg_dbg(level, fmt, arg...) do { \
if (debug + 1 > level) \
printk(KERN_DEBUG "%s/2-mpeg: " fmt, core->name, ## arg); \
} while(0)
#if defined(CONFIG_MODULES) && defined(MODULE)
static void request_module_async(struct work_struct *work)
{
struct cx8802_dev *dev=container_of(work, struct cx8802_dev, request_module_wk);
if (dev->core->board.mpeg & CX88_MPEG_DVB)
request_module("cx88-dvb");
if (dev->core->board.mpeg & CX88_MPEG_BLACKBIRD)
request_module("cx88-blackbird");
}
static void request_modules(struct cx8802_dev *dev)
{
INIT_WORK(&dev->request_module_wk, request_module_async);
schedule_work(&dev->request_module_wk);
}
static void flush_request_modules(struct cx8802_dev *dev)
{
flush_work(&dev->request_module_wk);
}
#else
#define request_modules(dev)
#define flush_request_modules(dev)
#endif /* CONFIG_MODULES */
static LIST_HEAD(cx8802_devlist);
static DEFINE_MUTEX(cx8802_mutex);
/* ------------------------------------------------------------------ */
static int cx8802_start_dma(struct cx8802_dev *dev,
struct cx88_dmaqueue *q,
struct cx88_buffer *buf)
{
struct cx88_core *core = dev->core;
dprintk(1, "cx8802_start_dma w: %d, h: %d, f: %d\n",
buf->vb.width, buf->vb.height, buf->vb.field);
/* setup fifo + format */
cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH28],
dev->ts_packet_size, buf->risc.dma);
/* write TS length to chip */
cx_write(MO_TS_LNGTH, buf->vb.width);
/* FIXME: this needs a review.
* also: move to cx88-blackbird + cx88-dvb source files? */
dprintk( 1, "core->active_type_id = 0x%08x\n", core->active_type_id);
if ( (core->active_type_id == CX88_MPEG_DVB) &&
(core->board.mpeg & CX88_MPEG_DVB) ) {
dprintk( 1, "cx8802_start_dma doing .dvb\n");
/* negedge driven & software reset */
cx_write(TS_GEN_CNTRL, 0x0040 | dev->ts_gen_cntrl);
udelay(100);
cx_write(MO_PINMUX_IO, 0x00);
cx_write(TS_HW_SOP_CNTRL, 0x47<<16|188<<4|0x01);
switch (core->boardnr) {
case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q:
case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T:
case CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD:
case CX88_BOARD_PCHDTV_HD5500:
cx_write(TS_SOP_STAT, 1<<13);
break;
case CX88_BOARD_SAMSUNG_SMT_7020:
cx_write(TS_SOP_STAT, 0x00);
break;
case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1:
case CX88_BOARD_HAUPPAUGE_NOVASE2_S1:
cx_write(MO_PINMUX_IO, 0x88); /* Enable MPEG parallel IO and video signal pins */
udelay(100);
break;
case CX88_BOARD_HAUPPAUGE_HVR1300:
/* Enable MPEG parallel IO and video signal pins */
cx_write(MO_PINMUX_IO, 0x88);
cx_write(TS_SOP_STAT, 0);
cx_write(TS_VALERR_CNTRL, 0);
break;
case CX88_BOARD_PINNACLE_PCTV_HD_800i:
/* Enable MPEG parallel IO and video signal pins */
cx_write(MO_PINMUX_IO, 0x88);
cx_write(TS_HW_SOP_CNTRL, (0x47 << 16) | (188 << 4));
dev->ts_gen_cntrl = 5;
cx_write(TS_SOP_STAT, 0);
cx_write(TS_VALERR_CNTRL, 0);
udelay(100);
break;
default:
cx_write(TS_SOP_STAT, 0x00);
break;
}
cx_write(TS_GEN_CNTRL, dev->ts_gen_cntrl);
udelay(100);
} else if ( (core->active_type_id == CX88_MPEG_BLACKBIRD) &&
(core->board.mpeg & CX88_MPEG_BLACKBIRD) ) {
dprintk( 1, "cx8802_start_dma doing .blackbird\n");
cx_write(MO_PINMUX_IO, 0x88); /* enable MPEG parallel IO */
cx_write(TS_GEN_CNTRL, 0x46); /* punctured clock TS & posedge driven & software reset */
udelay(100);
cx_write(TS_HW_SOP_CNTRL, 0x408); /* mpeg start byte */
cx_write(TS_VALERR_CNTRL, 0x2000);
cx_write(TS_GEN_CNTRL, 0x06); /* punctured clock TS & posedge driven */
udelay(100);
} else {
printk( "%s() Failed. Unsupported value in .mpeg (0x%08x)\n", __func__,
core->board.mpeg );
return -EINVAL;
}
/* reset counter */
cx_write(MO_TS_GPCNTRL, GP_COUNT_CONTROL_RESET);
q->count = 1;
/* enable irqs */
dprintk( 1, "setting the interrupt mask\n" );
cx_set(MO_PCI_INTMSK, core->pci_irqmask | PCI_INT_TSINT);
cx_set(MO_TS_INTMSK, 0x1f0011);
/* start dma */
cx_set(MO_DEV_CNTRL2, (1<<5));
cx_set(MO_TS_DMACNTRL, 0x11);
return 0;
}
static int cx8802_stop_dma(struct cx8802_dev *dev)
{
struct cx88_core *core = dev->core;
dprintk( 1, "cx8802_stop_dma\n" );
/* stop dma */
cx_clear(MO_TS_DMACNTRL, 0x11);
/* disable irqs */
cx_clear(MO_PCI_INTMSK, PCI_INT_TSINT);
cx_clear(MO_TS_INTMSK, 0x1f0011);
/* Reset the controller */
cx_write(TS_GEN_CNTRL, 0xcd);
return 0;
}
static int cx8802_restart_queue(struct cx8802_dev *dev,
struct cx88_dmaqueue *q)
{
struct cx88_buffer *buf;
dprintk( 1, "cx8802_restart_queue\n" );
if (list_empty(&q->active))
{
struct cx88_buffer *prev;
prev = NULL;
dprintk(1, "cx8802_restart_queue: queue is empty\n" );
for (;;) {
if (list_empty(&q->queued))
return 0;
buf = list_entry(q->queued.next, struct cx88_buffer, vb.queue);
if (NULL == prev) {
list_move_tail(&buf->vb.queue, &q->active);
cx8802_start_dma(dev, q, buf);
buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
dprintk(1,"[%p/%d] restart_queue - first active\n",
buf,buf->vb.i);
} else if (prev->vb.width == buf->vb.width &&
prev->vb.height == buf->vb.height &&
prev->fmt == buf->fmt) {
list_move_tail(&buf->vb.queue, &q->active);
buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
dprintk(1,"[%p/%d] restart_queue - move to active\n",
buf,buf->vb.i);
} else {
return 0;
}
prev = buf;
}
return 0;
}
buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
dprintk(2,"restart_queue [%p/%d]: restart dma\n",
buf, buf->vb.i);
cx8802_start_dma(dev, q, buf);
list_for_each_entry(buf, &q->active, vb.queue)
buf->count = q->count++;
mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
return 0;
}
/* ------------------------------------------------------------------ */
int cx8802_buf_prepare(struct videobuf_queue *q, struct cx8802_dev *dev,
struct cx88_buffer *buf, enum v4l2_field field)
{
int size = dev->ts_packet_size * dev->ts_packet_count;
struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
int rc;
dprintk(1, "%s: %p\n", __func__, buf);
if (0 != buf->vb.baddr && buf->vb.bsize < size)
return -EINVAL;
if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
buf->vb.width = dev->ts_packet_size;
buf->vb.height = dev->ts_packet_count;
buf->vb.size = size;
buf->vb.field = field /*V4L2_FIELD_TOP*/;
if (0 != (rc = videobuf_iolock(q,&buf->vb,NULL)))
goto fail;
cx88_risc_databuffer(dev->pci, &buf->risc,
dma->sglist,
buf->vb.width, buf->vb.height, 0);
}
buf->vb.state = VIDEOBUF_PREPARED;
return 0;
fail:
cx88_free_buffer(q,buf);
return rc;
}
void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf)
{
struct cx88_buffer *prev;
struct cx88_dmaqueue *cx88q = &dev->mpegq;
dprintk( 1, "cx8802_buf_queue\n" );
/* add jump to stopper */
buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
buf->risc.jmp[1] = cpu_to_le32(cx88q->stopper.dma);
if (list_empty(&cx88q->active)) {
dprintk( 1, "queue is empty - first active\n" );
list_add_tail(&buf->vb.queue,&cx88q->active);
cx8802_start_dma(dev, cx88q, buf);
buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = cx88q->count++;
mod_timer(&cx88q->timeout, jiffies+BUFFER_TIMEOUT);
dprintk(1,"[%p/%d] %s - first active\n",
buf, buf->vb.i, __func__);
} else {
dprintk( 1, "queue is not empty - append to active\n" );
prev = list_entry(cx88q->active.prev, struct cx88_buffer, vb.queue);
list_add_tail(&buf->vb.queue,&cx88q->active);
buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = cx88q->count++;
prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
dprintk( 1, "[%p/%d] %s - append to active\n",
buf, buf->vb.i, __func__);
}
}
/* ----------------------------------------------------------- */
static void do_cancel_buffers(struct cx8802_dev *dev, const char *reason, int restart)
{
struct cx88_dmaqueue *q = &dev->mpegq;
struct cx88_buffer *buf;
unsigned long flags;
spin_lock_irqsave(&dev->slock,flags);
while (!list_empty(&q->active)) {
buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
list_del(&buf->vb.queue);
buf->vb.state = VIDEOBUF_ERROR;
wake_up(&buf->vb.done);
dprintk(1,"[%p/%d] %s - dma=0x%08lx\n",
buf, buf->vb.i, reason, (unsigned long)buf->risc.dma);
}
if (restart)
{
dprintk(1, "restarting queue\n" );
cx8802_restart_queue(dev,q);
}
spin_unlock_irqrestore(&dev->slock,flags);
}
void cx8802_cancel_buffers(struct cx8802_dev *dev)
{
struct cx88_dmaqueue *q = &dev->mpegq;
dprintk( 1, "cx8802_cancel_buffers" );
del_timer_sync(&q->timeout);
cx8802_stop_dma(dev);
do_cancel_buffers(dev,"cancel",0);
}
static void cx8802_timeout(unsigned long data)
{
struct cx8802_dev *dev = (struct cx8802_dev*)data;
dprintk(1, "%s\n",__func__);
if (debug)
cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH28]);
cx8802_stop_dma(dev);
do_cancel_buffers(dev,"timeout",1);
}
static const char * cx88_mpeg_irqs[32] = {
"ts_risci1", NULL, NULL, NULL,
"ts_risci2", NULL, NULL, NULL,
"ts_oflow", NULL, NULL, NULL,
"ts_sync", NULL, NULL, NULL,
"opc_err", "par_err", "rip_err", "pci_abort",
"ts_err?",
};
static void cx8802_mpeg_irq(struct cx8802_dev *dev)
{
struct cx88_core *core = dev->core;
u32 status, mask, count;
dprintk( 1, "cx8802_mpeg_irq\n" );
status = cx_read(MO_TS_INTSTAT);
mask = cx_read(MO_TS_INTMSK);
if (0 == (status & mask))
return;
cx_write(MO_TS_INTSTAT, status);
if (debug || (status & mask & ~0xff))
cx88_print_irqbits(core->name, "irq mpeg ",
cx88_mpeg_irqs, ARRAY_SIZE(cx88_mpeg_irqs),
status, mask);
/* risc op code error */
if (status & (1 << 16)) {
printk(KERN_WARNING "%s: mpeg risc op code error\n",core->name);
cx_clear(MO_TS_DMACNTRL, 0x11);
cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH28]);
}
/* risc1 y */
if (status & 0x01) {
dprintk( 1, "wake up\n" );
spin_lock(&dev->slock);
count = cx_read(MO_TS_GPCNT);
cx88_wakeup(dev->core, &dev->mpegq, count);
spin_unlock(&dev->slock);
}
/* risc2 y */
if (status & 0x10) {
spin_lock(&dev->slock);
cx8802_restart_queue(dev,&dev->mpegq);
spin_unlock(&dev->slock);
}
/* other general errors */
if (status & 0x1f0100) {
dprintk( 0, "general errors: 0x%08x\n", status & 0x1f0100 );
spin_lock(&dev->slock);
cx8802_stop_dma(dev);
cx8802_restart_queue(dev,&dev->mpegq);
spin_unlock(&dev->slock);
}
}
#define MAX_IRQ_LOOP 10
static irqreturn_t cx8802_irq(int irq, void *dev_id)
{
struct cx8802_dev *dev = dev_id;
struct cx88_core *core = dev->core;
u32 status;
int loop, handled = 0;
for (loop = 0; loop < MAX_IRQ_LOOP; loop++) {
status = cx_read(MO_PCI_INTSTAT) &
(core->pci_irqmask | PCI_INT_TSINT);
if (0 == status)
goto out;
dprintk( 1, "cx8802_irq\n" );
dprintk( 1, " loop: %d/%d\n", loop, MAX_IRQ_LOOP );
dprintk( 1, " status: %d\n", status );
handled = 1;
cx_write(MO_PCI_INTSTAT, status);
if (status & core->pci_irqmask)
cx88_core_irq(core,status);
if (status & PCI_INT_TSINT)
cx8802_mpeg_irq(dev);
}
if (MAX_IRQ_LOOP == loop) {
dprintk( 0, "clearing mask\n" );
printk(KERN_WARNING "%s/0: irq loop -- clearing mask\n",
core->name);
cx_write(MO_PCI_INTMSK,0);
}
out:
return IRQ_RETVAL(handled);
}
static int cx8802_init_common(struct cx8802_dev *dev)
{
struct cx88_core *core = dev->core;
int err;
/* pci init */
if (pci_enable_device(dev->pci))
return -EIO;
pci_set_master(dev->pci);
if (!pci_dma_supported(dev->pci,DMA_BIT_MASK(32))) {
printk("%s/2: Oops: no 32bit PCI DMA ???\n",dev->core->name);
return -EIO;
}
dev->pci_rev = dev->pci->revision;
pci_read_config_byte(dev->pci, PCI_LATENCY_TIMER, &dev->pci_lat);
printk(KERN_INFO "%s/2: found at %s, rev: %d, irq: %d, "
"latency: %d, mmio: 0x%llx\n", dev->core->name,
pci_name(dev->pci), dev->pci_rev, dev->pci->irq,
dev->pci_lat,(unsigned long long)pci_resource_start(dev->pci,0));
/* initialize driver struct */
spin_lock_init(&dev->slock);
/* init dma queue */
INIT_LIST_HEAD(&dev->mpegq.active);
INIT_LIST_HEAD(&dev->mpegq.queued);
dev->mpegq.timeout.function = cx8802_timeout;
dev->mpegq.timeout.data = (unsigned long)dev;
init_timer(&dev->mpegq.timeout);
cx88_risc_stopper(dev->pci,&dev->mpegq.stopper,
MO_TS_DMACNTRL,0x11,0x00);
/* get irq */
err = request_irq(dev->pci->irq, cx8802_irq,
IRQF_SHARED, dev->core->name, dev);
if (err < 0) {
printk(KERN_ERR "%s: can't get IRQ %d\n",
dev->core->name, dev->pci->irq);
return err;
}
cx_set(MO_PCI_INTMSK, core->pci_irqmask);
/* everything worked */
pci_set_drvdata(dev->pci,dev);
return 0;
}
static void cx8802_fini_common(struct cx8802_dev *dev)
{
dprintk( 2, "cx8802_fini_common\n" );
cx8802_stop_dma(dev);
pci_disable_device(dev->pci);
/* unregister stuff */
free_irq(dev->pci->irq, dev);
/* free memory */
btcx_riscmem_free(dev->pci,&dev->mpegq.stopper);
}
/* ----------------------------------------------------------- */
static int cx8802_suspend_common(struct pci_dev *pci_dev, pm_message_t state)
{
struct cx8802_dev *dev = pci_get_drvdata(pci_dev);
struct cx88_core *core = dev->core;
unsigned long flags;
/* stop mpeg dma */
spin_lock_irqsave(&dev->slock, flags);
if (!list_empty(&dev->mpegq.active)) {
dprintk( 2, "suspend\n" );
printk("%s: suspend mpeg\n", core->name);
cx8802_stop_dma(dev);
del_timer(&dev->mpegq.timeout);
}
spin_unlock_irqrestore(&dev->slock, flags);
/* FIXME -- shutdown device */
cx88_shutdown(dev->core);
pci_save_state(pci_dev);
if (0 != pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state))) {
pci_disable_device(pci_dev);
dev->state.disabled = 1;
}
return 0;
}
static int cx8802_resume_common(struct pci_dev *pci_dev)
{
struct cx8802_dev *dev = pci_get_drvdata(pci_dev);
struct cx88_core *core = dev->core;
unsigned long flags;
int err;
if (dev->state.disabled) {
err=pci_enable_device(pci_dev);
if (err) {
printk(KERN_ERR "%s: can't enable device\n",
dev->core->name);
return err;
}
dev->state.disabled = 0;
}
err=pci_set_power_state(pci_dev, PCI_D0);
if (err) {
printk(KERN_ERR "%s: can't enable device\n",
dev->core->name);
pci_disable_device(pci_dev);
dev->state.disabled = 1;
return err;
}
pci_restore_state(pci_dev);
/* FIXME: re-initialize hardware */
cx88_reset(dev->core);
/* restart video+vbi capture */
spin_lock_irqsave(&dev->slock, flags);
if (!list_empty(&dev->mpegq.active)) {
printk("%s: resume mpeg\n", core->name);
cx8802_restart_queue(dev,&dev->mpegq);
}
spin_unlock_irqrestore(&dev->slock, flags);
return 0;
}
struct cx8802_driver * cx8802_get_driver(struct cx8802_dev *dev, enum cx88_board_type btype)
{
struct cx8802_driver *d;
list_for_each_entry(d, &dev->drvlist, drvlist)
if (d->type_id == btype)
return d;
return NULL;
}
/* Driver asked for hardware access. */
static int cx8802_request_acquire(struct cx8802_driver *drv)
{
struct cx88_core *core = drv->core;
unsigned int i;
/* Fail a request for hardware if the device is busy. */
if (core->active_type_id != CX88_BOARD_NONE &&
core->active_type_id != drv->type_id)
return -EBUSY;
if (drv->type_id == CX88_MPEG_DVB) {
/* When switching to DVB, always set the input to the tuner */
core->last_analog_input = core->input;
core->input = 0;
for (i = 0;
i < (sizeof(core->board.input) / sizeof(struct cx88_input));
i++) {
if (core->board.input[i].type == CX88_VMUX_DVB) {
core->input = i;
break;
}
}
}
if (drv->advise_acquire)
{
core->active_ref++;
if (core->active_type_id == CX88_BOARD_NONE) {
core->active_type_id = drv->type_id;
drv->advise_acquire(drv);
}
mpeg_dbg(1,"%s() Post acquire GPIO=%x\n", __func__, cx_read(MO_GP0_IO));
}
return 0;
}
/* Driver asked to release hardware. */
static int cx8802_request_release(struct cx8802_driver *drv)
{
struct cx88_core *core = drv->core;
if (drv->advise_release && --core->active_ref == 0)
{
if (drv->type_id == CX88_MPEG_DVB) {
/* If the DVB driver is releasing, reset the input
state to the last configured analog input */
core->input = core->last_analog_input;
}
drv->advise_release(drv);
core->active_type_id = CX88_BOARD_NONE;
mpeg_dbg(1,"%s() Post release GPIO=%x\n", __func__, cx_read(MO_GP0_IO));
}
return 0;
}
static int cx8802_check_driver(struct cx8802_driver *drv)
{
if (drv == NULL)
return -ENODEV;
if ((drv->type_id != CX88_MPEG_DVB) &&
(drv->type_id != CX88_MPEG_BLACKBIRD))
return -EINVAL;
if ((drv->hw_access != CX8802_DRVCTL_SHARED) &&
(drv->hw_access != CX8802_DRVCTL_EXCLUSIVE))
return -EINVAL;
if ((drv->probe == NULL) ||
(drv->remove == NULL) ||
(drv->advise_acquire == NULL) ||
(drv->advise_release == NULL))
return -EINVAL;
return 0;
}
int cx8802_register_driver(struct cx8802_driver *drv)
{
struct cx8802_dev *dev;
struct cx8802_driver *driver;
int err, i = 0;
printk(KERN_INFO
"cx88/2: registering cx8802 driver, type: %s access: %s\n",
drv->type_id == CX88_MPEG_DVB ? "dvb" : "blackbird",
drv->hw_access == CX8802_DRVCTL_SHARED ? "shared" : "exclusive");
if ((err = cx8802_check_driver(drv)) != 0) {
printk(KERN_ERR "cx88/2: cx8802_driver is invalid\n");
return err;
}
mutex_lock(&cx8802_mutex);
list_for_each_entry(dev, &cx8802_devlist, devlist) {
printk(KERN_INFO
"%s/2: subsystem: %04x:%04x, board: %s [card=%d]\n",
dev->core->name, dev->pci->subsystem_vendor,
dev->pci->subsystem_device, dev->core->board.name,
dev->core->boardnr);
/* Bring up a new struct for each driver instance */
driver = kzalloc(sizeof(*drv),GFP_KERNEL);
if (driver == NULL) {
err = -ENOMEM;
goto out;
}
/* Snapshot of the driver registration data */
drv->core = dev->core;
drv->suspend = cx8802_suspend_common;
drv->resume = cx8802_resume_common;
drv->request_acquire = cx8802_request_acquire;
drv->request_release = cx8802_request_release;
memcpy(driver, drv, sizeof(*driver));
mutex_lock(&drv->core->lock);
err = drv->probe(driver);
if (err == 0) {
i++;
list_add_tail(&driver->drvlist, &dev->drvlist);
} else {
printk(KERN_ERR
"%s/2: cx8802 probe failed, err = %d\n",
dev->core->name, err);
}
mutex_unlock(&drv->core->lock);
}
err = i ? 0 : -ENODEV;
out:
mutex_unlock(&cx8802_mutex);
return err;
}
int cx8802_unregister_driver(struct cx8802_driver *drv)
{
struct cx8802_dev *dev;
struct cx8802_driver *d, *dtmp;
int err = 0;
printk(KERN_INFO
"cx88/2: unregistering cx8802 driver, type: %s access: %s\n",
drv->type_id == CX88_MPEG_DVB ? "dvb" : "blackbird",
drv->hw_access == CX8802_DRVCTL_SHARED ? "shared" : "exclusive");
mutex_lock(&cx8802_mutex);
list_for_each_entry(dev, &cx8802_devlist, devlist) {
printk(KERN_INFO
"%s/2: subsystem: %04x:%04x, board: %s [card=%d]\n",
dev->core->name, dev->pci->subsystem_vendor,
dev->pci->subsystem_device, dev->core->board.name,
dev->core->boardnr);
mutex_lock(&dev->core->lock);
list_for_each_entry_safe(d, dtmp, &dev->drvlist, drvlist) {
/* only unregister the correct driver type */
if (d->type_id != drv->type_id)
continue;
err = d->remove(d);
if (err == 0) {
list_del(&d->drvlist);
kfree(d);
} else
printk(KERN_ERR "%s/2: cx8802 driver remove "
"failed (%d)\n", dev->core->name, err);
}
mutex_unlock(&dev->core->lock);
}
mutex_unlock(&cx8802_mutex);
return err;
}
/* ----------------------------------------------------------- */
static int cx8802_probe(struct pci_dev *pci_dev,
const struct pci_device_id *pci_id)
{
struct cx8802_dev *dev;
struct cx88_core *core;
int err;
/* general setup */
core = cx88_core_get(pci_dev);
if (NULL == core)
return -EINVAL;
printk("%s/2: cx2388x 8802 Driver Manager\n", core->name);
err = -ENODEV;
if (!core->board.mpeg)
goto fail_core;
err = -ENOMEM;
dev = kzalloc(sizeof(*dev),GFP_KERNEL);
if (NULL == dev)
goto fail_core;
dev->pci = pci_dev;
dev->core = core;
/* Maintain a reference so cx88-video can query the 8802 device. */
core->dvbdev = dev;
err = cx8802_init_common(dev);
if (err != 0)
goto fail_free;
INIT_LIST_HEAD(&dev->drvlist);
mutex_lock(&cx8802_mutex);
list_add_tail(&dev->devlist,&cx8802_devlist);
mutex_unlock(&cx8802_mutex);
/* now autoload cx88-dvb or cx88-blackbird */
request_modules(dev);
return 0;
fail_free:
kfree(dev);
fail_core:
core->dvbdev = NULL;
cx88_core_put(core,pci_dev);
return err;
}
static void cx8802_remove(struct pci_dev *pci_dev)
{
struct cx8802_dev *dev;
dev = pci_get_drvdata(pci_dev);
dprintk( 1, "%s\n", __func__);
flush_request_modules(dev);
mutex_lock(&dev->core->lock);
if (!list_empty(&dev->drvlist)) {
struct cx8802_driver *drv, *tmp;
int err;
printk(KERN_WARNING "%s/2: Trying to remove cx8802 driver "
"while cx8802 sub-drivers still loaded?!\n",
dev->core->name);
list_for_each_entry_safe(drv, tmp, &dev->drvlist, drvlist) {
err = drv->remove(drv);
if (err == 0) {
list_del(&drv->drvlist);
} else
printk(KERN_ERR "%s/2: cx8802 driver remove "
"failed (%d)\n", dev->core->name, err);
kfree(drv);
}
}
mutex_unlock(&dev->core->lock);
/* Destroy any 8802 reference. */
dev->core->dvbdev = NULL;
/* common */
cx8802_fini_common(dev);
cx88_core_put(dev->core,dev->pci);
kfree(dev);
}
static const struct pci_device_id cx8802_pci_tbl[] = {
{
.vendor = 0x14f1,
.device = 0x8802,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},{
/* --- end of list --- */
}
};
MODULE_DEVICE_TABLE(pci, cx8802_pci_tbl);
static struct pci_driver cx8802_pci_driver = {
.name = "cx88-mpeg driver manager",
.id_table = cx8802_pci_tbl,
.probe = cx8802_probe,
.remove = cx8802_remove,
};
module_pci_driver(cx8802_pci_driver);
EXPORT_SYMBOL(cx8802_buf_prepare);
EXPORT_SYMBOL(cx8802_buf_queue);
EXPORT_SYMBOL(cx8802_cancel_buffers);
EXPORT_SYMBOL(cx8802_register_driver);
EXPORT_SYMBOL(cx8802_unregister_driver);
EXPORT_SYMBOL(cx8802_get_driver);
/* ----------------------------------------------------------- */
/*
* Local variables:
* c-basic-offset: 8
* End:
* kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off
*/

View file

@ -0,0 +1,836 @@
/*
cx88x-hw.h - CX2388x register offsets
Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
2001 Michael Eskin
2002 Yurij Sysoev <yurij@naturesoft.net>
2003 Gerd Knorr <kraxel@bytesex.org>
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 _CX88_REG_H_
#define _CX88_REG_H_
/* ---------------------------------------------------------------------- */
/* PCI IDs and config space */
#ifndef PCI_VENDOR_ID_CONEXANT
# define PCI_VENDOR_ID_CONEXANT 0x14F1
#endif
#ifndef PCI_DEVICE_ID_CX2300_VID
# define PCI_DEVICE_ID_CX2300_VID 0x8800
#endif
#define CX88X_DEVCTRL 0x40
#define CX88X_EN_TBFX 0x02
#define CX88X_EN_VSFX 0x04
/* ---------------------------------------------------------------------- */
/* PCI controller registers */
/* Command and Status Register */
#define F0_CMD_STAT_MM 0x2f0004
#define F1_CMD_STAT_MM 0x2f0104
#define F2_CMD_STAT_MM 0x2f0204
#define F3_CMD_STAT_MM 0x2f0304
#define F4_CMD_STAT_MM 0x2f0404
/* Device Control #1 */
#define F0_DEV_CNTRL1_MM 0x2f0040
#define F1_DEV_CNTRL1_MM 0x2f0140
#define F2_DEV_CNTRL1_MM 0x2f0240
#define F3_DEV_CNTRL1_MM 0x2f0340
#define F4_DEV_CNTRL1_MM 0x2f0440
/* Device Control #1 */
#define F0_BAR0_MM 0x2f0010
#define F1_BAR0_MM 0x2f0110
#define F2_BAR0_MM 0x2f0210
#define F3_BAR0_MM 0x2f0310
#define F4_BAR0_MM 0x2f0410
/* ---------------------------------------------------------------------- */
/* DMA Controller registers */
#define MO_PDMA_STHRSH 0x200000 // Source threshold
#define MO_PDMA_STADRS 0x200004 // Source target address
#define MO_PDMA_SIADRS 0x200008 // Source internal address
#define MO_PDMA_SCNTRL 0x20000C // Source control
#define MO_PDMA_DTHRSH 0x200010 // Destination threshold
#define MO_PDMA_DTADRS 0x200014 // Destination target address
#define MO_PDMA_DIADRS 0x200018 // Destination internal address
#define MO_PDMA_DCNTRL 0x20001C // Destination control
#define MO_LD_SSID 0x200030 // Load subsystem ID
#define MO_DEV_CNTRL2 0x200034 // Device control
#define MO_PCI_INTMSK 0x200040 // PCI interrupt mask
#define MO_PCI_INTSTAT 0x200044 // PCI interrupt status
#define MO_PCI_INTMSTAT 0x200048 // PCI interrupt masked status
#define MO_VID_INTMSK 0x200050 // Video interrupt mask
#define MO_VID_INTSTAT 0x200054 // Video interrupt status
#define MO_VID_INTMSTAT 0x200058 // Video interrupt masked status
#define MO_VID_INTSSTAT 0x20005C // Video interrupt set status
#define MO_AUD_INTMSK 0x200060 // Audio interrupt mask
#define MO_AUD_INTSTAT 0x200064 // Audio interrupt status
#define MO_AUD_INTMSTAT 0x200068 // Audio interrupt masked status
#define MO_AUD_INTSSTAT 0x20006C // Audio interrupt set status
#define MO_TS_INTMSK 0x200070 // Transport stream interrupt mask
#define MO_TS_INTSTAT 0x200074 // Transport stream interrupt status
#define MO_TS_INTMSTAT 0x200078 // Transport stream interrupt mask status
#define MO_TS_INTSSTAT 0x20007C // Transport stream interrupt set status
#define MO_VIP_INTMSK 0x200080 // VIP interrupt mask
#define MO_VIP_INTSTAT 0x200084 // VIP interrupt status
#define MO_VIP_INTMSTAT 0x200088 // VIP interrupt masked status
#define MO_VIP_INTSSTAT 0x20008C // VIP interrupt set status
#define MO_GPHST_INTMSK 0x200090 // Host interrupt mask
#define MO_GPHST_INTSTAT 0x200094 // Host interrupt status
#define MO_GPHST_INTMSTAT 0x200098 // Host interrupt masked status
#define MO_GPHST_INTSSTAT 0x20009C // Host interrupt set status
// DMA Channels 1-6 belong to SPIPE
#define MO_DMA7_PTR1 0x300018 // {24}RW* DMA Current Ptr : Ch#7
#define MO_DMA8_PTR1 0x30001C // {24}RW* DMA Current Ptr : Ch#8
// DMA Channels 9-20 belong to SPIPE
#define MO_DMA21_PTR1 0x300080 // {24}R0* DMA Current Ptr : Ch#21
#define MO_DMA22_PTR1 0x300084 // {24}R0* DMA Current Ptr : Ch#22
#define MO_DMA23_PTR1 0x300088 // {24}R0* DMA Current Ptr : Ch#23
#define MO_DMA24_PTR1 0x30008C // {24}R0* DMA Current Ptr : Ch#24
#define MO_DMA25_PTR1 0x300090 // {24}R0* DMA Current Ptr : Ch#25
#define MO_DMA26_PTR1 0x300094 // {24}R0* DMA Current Ptr : Ch#26
#define MO_DMA27_PTR1 0x300098 // {24}R0* DMA Current Ptr : Ch#27
#define MO_DMA28_PTR1 0x30009C // {24}R0* DMA Current Ptr : Ch#28
#define MO_DMA29_PTR1 0x3000A0 // {24}R0* DMA Current Ptr : Ch#29
#define MO_DMA30_PTR1 0x3000A4 // {24}R0* DMA Current Ptr : Ch#30
#define MO_DMA31_PTR1 0x3000A8 // {24}R0* DMA Current Ptr : Ch#31
#define MO_DMA32_PTR1 0x3000AC // {24}R0* DMA Current Ptr : Ch#32
#define MO_DMA21_PTR2 0x3000C0 // {24}RW* DMA Tab Ptr : Ch#21
#define MO_DMA22_PTR2 0x3000C4 // {24}RW* DMA Tab Ptr : Ch#22
#define MO_DMA23_PTR2 0x3000C8 // {24}RW* DMA Tab Ptr : Ch#23
#define MO_DMA24_PTR2 0x3000CC // {24}RW* DMA Tab Ptr : Ch#24
#define MO_DMA25_PTR2 0x3000D0 // {24}RW* DMA Tab Ptr : Ch#25
#define MO_DMA26_PTR2 0x3000D4 // {24}RW* DMA Tab Ptr : Ch#26
#define MO_DMA27_PTR2 0x3000D8 // {24}RW* DMA Tab Ptr : Ch#27
#define MO_DMA28_PTR2 0x3000DC // {24}RW* DMA Tab Ptr : Ch#28
#define MO_DMA29_PTR2 0x3000E0 // {24}RW* DMA Tab Ptr : Ch#29
#define MO_DMA30_PTR2 0x3000E4 // {24}RW* DMA Tab Ptr : Ch#30
#define MO_DMA31_PTR2 0x3000E8 // {24}RW* DMA Tab Ptr : Ch#31
#define MO_DMA32_PTR2 0x3000EC // {24}RW* DMA Tab Ptr : Ch#32
#define MO_DMA21_CNT1 0x300100 // {11}RW* DMA Buffer Size : Ch#21
#define MO_DMA22_CNT1 0x300104 // {11}RW* DMA Buffer Size : Ch#22
#define MO_DMA23_CNT1 0x300108 // {11}RW* DMA Buffer Size : Ch#23
#define MO_DMA24_CNT1 0x30010C // {11}RW* DMA Buffer Size : Ch#24
#define MO_DMA25_CNT1 0x300110 // {11}RW* DMA Buffer Size : Ch#25
#define MO_DMA26_CNT1 0x300114 // {11}RW* DMA Buffer Size : Ch#26
#define MO_DMA27_CNT1 0x300118 // {11}RW* DMA Buffer Size : Ch#27
#define MO_DMA28_CNT1 0x30011C // {11}RW* DMA Buffer Size : Ch#28
#define MO_DMA29_CNT1 0x300120 // {11}RW* DMA Buffer Size : Ch#29
#define MO_DMA30_CNT1 0x300124 // {11}RW* DMA Buffer Size : Ch#30
#define MO_DMA31_CNT1 0x300128 // {11}RW* DMA Buffer Size : Ch#31
#define MO_DMA32_CNT1 0x30012C // {11}RW* DMA Buffer Size : Ch#32
#define MO_DMA21_CNT2 0x300140 // {11}RW* DMA Table Size : Ch#21
#define MO_DMA22_CNT2 0x300144 // {11}RW* DMA Table Size : Ch#22
#define MO_DMA23_CNT2 0x300148 // {11}RW* DMA Table Size : Ch#23
#define MO_DMA24_CNT2 0x30014C // {11}RW* DMA Table Size : Ch#24
#define MO_DMA25_CNT2 0x300150 // {11}RW* DMA Table Size : Ch#25
#define MO_DMA26_CNT2 0x300154 // {11}RW* DMA Table Size : Ch#26
#define MO_DMA27_CNT2 0x300158 // {11}RW* DMA Table Size : Ch#27
#define MO_DMA28_CNT2 0x30015C // {11}RW* DMA Table Size : Ch#28
#define MO_DMA29_CNT2 0x300160 // {11}RW* DMA Table Size : Ch#29
#define MO_DMA30_CNT2 0x300164 // {11}RW* DMA Table Size : Ch#30
#define MO_DMA31_CNT2 0x300168 // {11}RW* DMA Table Size : Ch#31
#define MO_DMA32_CNT2 0x30016C // {11}RW* DMA Table Size : Ch#32
/* ---------------------------------------------------------------------- */
/* Video registers */
#define MO_VIDY_DMA 0x310000 // {64}RWp Video Y
#define MO_VIDU_DMA 0x310008 // {64}RWp Video U
#define MO_VIDV_DMA 0x310010 // {64}RWp Video V
#define MO_VBI_DMA 0x310018 // {64}RWp VBI (Vertical blanking interval)
#define MO_DEVICE_STATUS 0x310100
#define MO_INPUT_FORMAT 0x310104
#define MO_AGC_BURST 0x31010c
#define MO_CONTR_BRIGHT 0x310110
#define MO_UV_SATURATION 0x310114
#define MO_HUE 0x310118
#define MO_HTOTAL 0x310120
#define MO_HDELAY_EVEN 0x310124
#define MO_HDELAY_ODD 0x310128
#define MO_VDELAY_ODD 0x31012c
#define MO_VDELAY_EVEN 0x310130
#define MO_HACTIVE_EVEN 0x31013c
#define MO_HACTIVE_ODD 0x310140
#define MO_VACTIVE_EVEN 0x310144
#define MO_VACTIVE_ODD 0x310148
#define MO_HSCALE_EVEN 0x31014c
#define MO_HSCALE_ODD 0x310150
#define MO_VSCALE_EVEN 0x310154
#define MO_FILTER_EVEN 0x31015c
#define MO_VSCALE_ODD 0x310158
#define MO_FILTER_ODD 0x310160
#define MO_OUTPUT_FORMAT 0x310164
#define MO_PLL_REG 0x310168 // PLL register
#define MO_PLL_ADJ_CTRL 0x31016c // PLL adjust control register
#define MO_SCONV_REG 0x310170 // sample rate conversion register
#define MO_SCONV_FIFO 0x310174 // sample rate conversion fifo
#define MO_SUB_STEP 0x310178 // subcarrier step size
#define MO_SUB_STEP_DR 0x31017c // subcarrier step size for DR line
#define MO_CAPTURE_CTRL 0x310180 // capture control
#define MO_COLOR_CTRL 0x310184
#define MO_VBI_PACKET 0x310188 // vbi packet size / delay
#define MO_FIELD_COUNT 0x310190 // field counter
#define MO_VIP_CONFIG 0x310194
#define MO_VBOS_CONTROL 0x3101a8
#define MO_AGC_BACK_VBI 0x310200
#define MO_AGC_SYNC_TIP1 0x310208
#define MO_VIDY_GPCNT 0x31C020 // {16}RO Video Y general purpose counter
#define MO_VIDU_GPCNT 0x31C024 // {16}RO Video U general purpose counter
#define MO_VIDV_GPCNT 0x31C028 // {16}RO Video V general purpose counter
#define MO_VBI_GPCNT 0x31C02C // {16}RO VBI general purpose counter
#define MO_VIDY_GPCNTRL 0x31C030 // {2}WO Video Y general purpose control
#define MO_VIDU_GPCNTRL 0x31C034 // {2}WO Video U general purpose control
#define MO_VIDV_GPCNTRL 0x31C038 // {2}WO Video V general purpose control
#define MO_VBI_GPCNTRL 0x31C03C // {2}WO VBI general purpose counter
#define MO_VID_DMACNTRL 0x31C040 // {8}RW Video DMA control
#define MO_VID_XFR_STAT 0x31C044 // {1}RO Video transfer status
/* ---------------------------------------------------------------------- */
/* audio registers */
#define MO_AUDD_DMA 0x320000 // {64}RWp Audio downstream
#define MO_AUDU_DMA 0x320008 // {64}RWp Audio upstream
#define MO_AUDR_DMA 0x320010 // {64}RWp Audio RDS (downstream)
#define MO_AUDD_GPCNT 0x32C020 // {16}RO Audio down general purpose counter
#define MO_AUDU_GPCNT 0x32C024 // {16}RO Audio up general purpose counter
#define MO_AUDR_GPCNT 0x32C028 // {16}RO Audio RDS general purpose counter
#define MO_AUDD_GPCNTRL 0x32C030 // {2}WO Audio down general purpose control
#define MO_AUDU_GPCNTRL 0x32C034 // {2}WO Audio up general purpose control
#define MO_AUDR_GPCNTRL 0x32C038 // {2}WO Audio RDS general purpose control
#define MO_AUD_DMACNTRL 0x32C040 // {6}RW Audio DMA control
#define MO_AUD_XFR_STAT 0x32C044 // {1}RO Audio transfer status
#define MO_AUDD_LNGTH 0x32C048 // {12}RW Audio down line length
#define MO_AUDR_LNGTH 0x32C04C // {12}RW Audio RDS line length
#define AUD_INIT 0x320100
#define AUD_INIT_LD 0x320104
#define AUD_SOFT_RESET 0x320108
#define AUD_I2SINPUTCNTL 0x320120
#define AUD_BAUDRATE 0x320124
#define AUD_I2SOUTPUTCNTL 0x320128
#define AAGC_HYST 0x320134
#define AAGC_GAIN 0x320138
#define AAGC_DEF 0x32013c
#define AUD_IIR1_0_SEL 0x320150
#define AUD_IIR1_0_SHIFT 0x320154
#define AUD_IIR1_1_SEL 0x320158
#define AUD_IIR1_1_SHIFT 0x32015c
#define AUD_IIR1_2_SEL 0x320160
#define AUD_IIR1_2_SHIFT 0x320164
#define AUD_IIR1_3_SEL 0x320168
#define AUD_IIR1_3_SHIFT 0x32016c
#define AUD_IIR1_4_SEL 0x320170
#define AUD_IIR1_4_SHIFT 0x32017c
#define AUD_IIR1_5_SEL 0x320180
#define AUD_IIR1_5_SHIFT 0x320184
#define AUD_IIR2_0_SEL 0x320190
#define AUD_IIR2_0_SHIFT 0x320194
#define AUD_IIR2_1_SEL 0x320198
#define AUD_IIR2_1_SHIFT 0x32019c
#define AUD_IIR2_2_SEL 0x3201a0
#define AUD_IIR2_2_SHIFT 0x3201a4
#define AUD_IIR2_3_SEL 0x3201a8
#define AUD_IIR2_3_SHIFT 0x3201ac
#define AUD_IIR3_0_SEL 0x3201c0
#define AUD_IIR3_0_SHIFT 0x3201c4
#define AUD_IIR3_1_SEL 0x3201c8
#define AUD_IIR3_1_SHIFT 0x3201cc
#define AUD_IIR3_2_SEL 0x3201d0
#define AUD_IIR3_2_SHIFT 0x3201d4
#define AUD_IIR4_0_SEL 0x3201e0
#define AUD_IIR4_0_SHIFT 0x3201e4
#define AUD_IIR4_1_SEL 0x3201e8
#define AUD_IIR4_1_SHIFT 0x3201ec
#define AUD_IIR4_2_SEL 0x3201f0
#define AUD_IIR4_2_SHIFT 0x3201f4
#define AUD_IIR4_0_CA0 0x320200
#define AUD_IIR4_0_CA1 0x320204
#define AUD_IIR4_0_CA2 0x320208
#define AUD_IIR4_0_CB0 0x32020c
#define AUD_IIR4_0_CB1 0x320210
#define AUD_IIR4_1_CA0 0x320214
#define AUD_IIR4_1_CA1 0x320218
#define AUD_IIR4_1_CA2 0x32021c
#define AUD_IIR4_1_CB0 0x320220
#define AUD_IIR4_1_CB1 0x320224
#define AUD_IIR4_2_CA0 0x320228
#define AUD_IIR4_2_CA1 0x32022c
#define AUD_IIR4_2_CA2 0x320230
#define AUD_IIR4_2_CB0 0x320234
#define AUD_IIR4_2_CB1 0x320238
#define AUD_HP_MD_IIR4_1 0x320250
#define AUD_HP_PROG_IIR4_1 0x320254
#define AUD_FM_MODE_ENABLE 0x320258
#define AUD_POLY0_DDS_CONSTANT 0x320270
#define AUD_DN0_FREQ 0x320274
#define AUD_DN1_FREQ 0x320278
#define AUD_DN1_FREQ_SHIFT 0x32027c
#define AUD_DN1_AFC 0x320280
#define AUD_DN1_SRC_SEL 0x320284
#define AUD_DN1_SHFT 0x320288
#define AUD_DN2_FREQ 0x32028c
#define AUD_DN2_FREQ_SHIFT 0x320290
#define AUD_DN2_AFC 0x320294
#define AUD_DN2_SRC_SEL 0x320298
#define AUD_DN2_SHFT 0x32029c
#define AUD_CRDC0_SRC_SEL 0x320300
#define AUD_CRDC0_SHIFT 0x320304
#define AUD_CORDIC_SHIFT_0 0x320308
#define AUD_CRDC1_SRC_SEL 0x32030c
#define AUD_CRDC1_SHIFT 0x320310
#define AUD_CORDIC_SHIFT_1 0x320314
#define AUD_DCOC_0_SRC 0x320320
#define AUD_DCOC0_SHIFT 0x320324
#define AUD_DCOC_0_SHIFT_IN0 0x320328
#define AUD_DCOC_0_SHIFT_IN1 0x32032c
#define AUD_DCOC_1_SRC 0x320330
#define AUD_DCOC1_SHIFT 0x320334
#define AUD_DCOC_1_SHIFT_IN0 0x320338
#define AUD_DCOC_1_SHIFT_IN1 0x32033c
#define AUD_DCOC_2_SRC 0x320340
#define AUD_DCOC2_SHIFT 0x320344
#define AUD_DCOC_2_SHIFT_IN0 0x320348
#define AUD_DCOC_2_SHIFT_IN1 0x32034c
#define AUD_DCOC_PASS_IN 0x320350
#define AUD_PDET_SRC 0x320370
#define AUD_PDET_SHIFT 0x320374
#define AUD_PILOT_BQD_1_K0 0x320380
#define AUD_PILOT_BQD_1_K1 0x320384
#define AUD_PILOT_BQD_1_K2 0x320388
#define AUD_PILOT_BQD_1_K3 0x32038c
#define AUD_PILOT_BQD_1_K4 0x320390
#define AUD_PILOT_BQD_2_K0 0x320394
#define AUD_PILOT_BQD_2_K1 0x320398
#define AUD_PILOT_BQD_2_K2 0x32039c
#define AUD_PILOT_BQD_2_K3 0x3203a0
#define AUD_PILOT_BQD_2_K4 0x3203a4
#define AUD_THR_FR 0x3203c0
#define AUD_X_PROG 0x3203c4
#define AUD_Y_PROG 0x3203c8
#define AUD_HARMONIC_MULT 0x3203cc
#define AUD_C1_UP_THR 0x3203d0
#define AUD_C1_LO_THR 0x3203d4
#define AUD_C2_UP_THR 0x3203d8
#define AUD_C2_LO_THR 0x3203dc
#define AUD_PLL_EN 0x320400
#define AUD_PLL_SRC 0x320404
#define AUD_PLL_SHIFT 0x320408
#define AUD_PLL_IF_SEL 0x32040c
#define AUD_PLL_IF_SHIFT 0x320410
#define AUD_BIQUAD_PLL_K0 0x320414
#define AUD_BIQUAD_PLL_K1 0x320418
#define AUD_BIQUAD_PLL_K2 0x32041c
#define AUD_BIQUAD_PLL_K3 0x320420
#define AUD_BIQUAD_PLL_K4 0x320424
#define AUD_DEEMPH0_SRC_SEL 0x320440
#define AUD_DEEMPH0_SHIFT 0x320444
#define AUD_DEEMPH0_G0 0x320448
#define AUD_DEEMPH0_A0 0x32044c
#define AUD_DEEMPH0_B0 0x320450
#define AUD_DEEMPH0_A1 0x320454
#define AUD_DEEMPH0_B1 0x320458
#define AUD_DEEMPH1_SRC_SEL 0x32045c
#define AUD_DEEMPH1_SHIFT 0x320460
#define AUD_DEEMPH1_G0 0x320464
#define AUD_DEEMPH1_A0 0x320468
#define AUD_DEEMPH1_B0 0x32046c
#define AUD_DEEMPH1_A1 0x320470
#define AUD_DEEMPH1_B1 0x320474
#define AUD_OUT0_SEL 0x320490
#define AUD_OUT0_SHIFT 0x320494
#define AUD_OUT1_SEL 0x320498
#define AUD_OUT1_SHIFT 0x32049c
#define AUD_RDSI_SEL 0x3204a0
#define AUD_RDSI_SHIFT 0x3204a4
#define AUD_RDSQ_SEL 0x3204a8
#define AUD_RDSQ_SHIFT 0x3204ac
#define AUD_DBX_IN_GAIN 0x320500
#define AUD_DBX_WBE_GAIN 0x320504
#define AUD_DBX_SE_GAIN 0x320508
#define AUD_DBX_RMS_WBE 0x32050c
#define AUD_DBX_RMS_SE 0x320510
#define AUD_DBX_SE_BYPASS 0x320514
#define AUD_FAWDETCTL 0x320530
#define AUD_FAWDETWINCTL 0x320534
#define AUD_DEEMPHGAIN_R 0x320538
#define AUD_DEEMPHNUMER1_R 0x32053c
#define AUD_DEEMPHNUMER2_R 0x320540
#define AUD_DEEMPHDENOM1_R 0x320544
#define AUD_DEEMPHDENOM2_R 0x320548
#define AUD_ERRLOGPERIOD_R 0x32054c
#define AUD_ERRINTRPTTHSHLD1_R 0x320550
#define AUD_ERRINTRPTTHSHLD2_R 0x320554
#define AUD_ERRINTRPTTHSHLD3_R 0x320558
#define AUD_NICAM_STATUS1 0x32055c
#define AUD_NICAM_STATUS2 0x320560
#define AUD_ERRLOG1 0x320564
#define AUD_ERRLOG2 0x320568
#define AUD_ERRLOG3 0x32056c
#define AUD_DAC_BYPASS_L 0x320580
#define AUD_DAC_BYPASS_R 0x320584
#define AUD_DAC_BYPASS_CTL 0x320588
#define AUD_CTL 0x32058c
#define AUD_STATUS 0x320590
#define AUD_VOL_CTL 0x320594
#define AUD_BAL_CTL 0x320598
#define AUD_START_TIMER 0x3205b0
#define AUD_MODE_CHG_TIMER 0x3205b4
#define AUD_POLYPH80SCALEFAC 0x3205b8
#define AUD_DMD_RA_DDS 0x3205bc
#define AUD_I2S_RA_DDS 0x3205c0
#define AUD_RATE_THRES_DMD 0x3205d0
#define AUD_RATE_THRES_I2S 0x3205d4
#define AUD_RATE_ADJ1 0x3205d8
#define AUD_RATE_ADJ2 0x3205dc
#define AUD_RATE_ADJ3 0x3205e0
#define AUD_RATE_ADJ4 0x3205e4
#define AUD_RATE_ADJ5 0x3205e8
#define AUD_APB_IN_RATE_ADJ 0x3205ec
#define AUD_I2SCNTL 0x3205ec
#define AUD_PHASE_FIX_CTL 0x3205f0
#define AUD_PLL_PRESCALE 0x320600
#define AUD_PLL_DDS 0x320604
#define AUD_PLL_INT 0x320608
#define AUD_PLL_FRAC 0x32060c
#define AUD_PLL_JTAG 0x320620
#define AUD_PLL_SPMP 0x320624
#define AUD_AFE_12DB_EN 0x320628
// Audio QAM Register Addresses
#define AUD_PDF_DDS_CNST_BYTE2 0x320d01
#define AUD_PDF_DDS_CNST_BYTE1 0x320d02
#define AUD_PDF_DDS_CNST_BYTE0 0x320d03
#define AUD_PHACC_FREQ_8MSB 0x320d2a
#define AUD_PHACC_FREQ_8LSB 0x320d2b
#define AUD_QAM_MODE 0x320d04
/* ---------------------------------------------------------------------- */
/* transport stream registers */
#define MO_TS_DMA 0x330000 // {64}RWp Transport stream downstream
#define MO_TS_GPCNT 0x33C020 // {16}RO TS general purpose counter
#define MO_TS_GPCNTRL 0x33C030 // {2}WO TS general purpose control
#define MO_TS_DMACNTRL 0x33C040 // {6}RW TS DMA control
#define MO_TS_XFR_STAT 0x33C044 // {1}RO TS transfer status
#define MO_TS_LNGTH 0x33C048 // {12}RW TS line length
#define TS_HW_SOP_CNTRL 0x33C04C
#define TS_GEN_CNTRL 0x33C050
#define TS_BD_PKT_STAT 0x33C054
#define TS_SOP_STAT 0x33C058
#define TS_FIFO_OVFL_STAT 0x33C05C
#define TS_VALERR_CNTRL 0x33C060
/* ---------------------------------------------------------------------- */
/* VIP registers */
#define MO_VIPD_DMA 0x340000 // {64}RWp VIP downstream
#define MO_VIPU_DMA 0x340008 // {64}RWp VIP upstream
#define MO_VIPD_GPCNT 0x34C020 // {16}RO VIP down general purpose counter
#define MO_VIPU_GPCNT 0x34C024 // {16}RO VIP up general purpose counter
#define MO_VIPD_GPCNTRL 0x34C030 // {2}WO VIP down general purpose control
#define MO_VIPU_GPCNTRL 0x34C034 // {2}WO VIP up general purpose control
#define MO_VIP_DMACNTRL 0x34C040 // {6}RW VIP DMA control
#define MO_VIP_XFR_STAT 0x34C044 // {1}RO VIP transfer status
#define MO_VIP_CFG 0x340048 // VIP configuration
#define MO_VIPU_CNTRL 0x34004C // VIP upstream control #1
#define MO_VIPD_CNTRL 0x340050 // VIP downstream control #2
#define MO_VIPD_LNGTH 0x340054 // VIP downstream line length
#define MO_VIP_BRSTLN 0x340058 // VIP burst length
#define MO_VIP_INTCNTRL 0x34C05C // VIP Interrupt Control
#define MO_VIP_XFTERM 0x340060 // VIP transfer terminate
/* ---------------------------------------------------------------------- */
/* misc registers */
#define MO_M2M_DMA 0x350000 // {64}RWp Mem2Mem DMA Bfr
#define MO_GP0_IO 0x350010 // {32}RW* GPIOoutput enablesdata I/O
#define MO_GP1_IO 0x350014 // {32}RW* GPIOoutput enablesdata I/O
#define MO_GP2_IO 0x350018 // {32}RW* GPIOoutput enablesdata I/O
#define MO_GP3_IO 0x35001C // {32}RW* GPIO Mode/Ctrloutput enables
#define MO_GPIO 0x350020 // {32}RW* GPIO I2C Ctrldata I/O
#define MO_GPOE 0x350024 // {32}RW GPIO I2C Ctrloutput enables
#define MO_GP_ISM 0x350028 // {16}WO GPIO Intr Sens/Pol
#define MO_PLL_B 0x35C008 // {32}RW* PLL Control for ASB bus clks
#define MO_M2M_CNT 0x35C024 // {32}RW Mem2Mem DMA Cnt
#define MO_M2M_XSUM 0x35C028 // {32}RO M2M XOR-Checksum
#define MO_CRC 0x35C02C // {16}RW CRC16 init/result
#define MO_CRC_D 0x35C030 // {32}WO CRC16 new data in
#define MO_TM_CNT_LDW 0x35C034 // {32}RO Timer : Counter low dword
#define MO_TM_CNT_UW 0x35C038 // {16}RO Timer : Counter high word
#define MO_TM_LMT_LDW 0x35C03C // {32}RW Timer : Limit low dword
#define MO_TM_LMT_UW 0x35C040 // {32}RW Timer : Limit high word
#define MO_PINMUX_IO 0x35C044 // {8}RW Pin Mux Control
#define MO_TSTSEL_IO 0x35C048 // {2}RW Pin Mux Control
#define MO_AFECFG_IO 0x35C04C // AFE configuration reg
#define MO_DDS_IO 0x35C050 // DDS Increment reg
#define MO_DDSCFG_IO 0x35C054 // DDS Configuration reg
#define MO_SAMPLE_IO 0x35C058 // IRIn sample reg
#define MO_SRST_IO 0x35C05C // Output system reset reg
#define MO_INT1_MSK 0x35C060 // DMA RISC interrupt mask
#define MO_INT1_STAT 0x35C064 // DMA RISC interrupt status
#define MO_INT1_MSTAT 0x35C068 // DMA RISC interrupt masked status
/* ---------------------------------------------------------------------- */
/* i2c bus registers */
#define MO_I2C 0x368000 // I2C data/control
#define MO_I2C_DIV (0xf<<4)
#define MO_I2C_SYNC (1<<3)
#define MO_I2C_W3B (1<<2)
#define MO_I2C_SCL (1<<1)
#define MO_I2C_SDA (1<<0)
/* ---------------------------------------------------------------------- */
/* general purpose host registers */
/* FIXME: tyops? s/0x35/0x38/ ?? */
#define MO_GPHSTD_DMA 0x350000 // {64}RWp Host downstream
#define MO_GPHSTU_DMA 0x350008 // {64}RWp Host upstream
#define MO_GPHSTU_CNTRL 0x380048 // Host upstream control #1
#define MO_GPHSTD_CNTRL 0x38004C // Host downstream control #2
#define MO_GPHSTD_LNGTH 0x380050 // Host downstream line length
#define MO_GPHST_WSC 0x380054 // Host wait state control
#define MO_GPHST_XFR 0x380058 // Host transfer control
#define MO_GPHST_WDTH 0x38005C // Host interface width
#define MO_GPHST_HDSHK 0x380060 // Host peripheral handshake
#define MO_GPHST_MUX16 0x380064 // Host muxed 16-bit transfer parameters
#define MO_GPHST_MODE 0x380068 // Host mode select
#define MO_GPHSTD_GPCNT 0x35C020 // Host down general purpose counter
#define MO_GPHSTU_GPCNT 0x35C024 // Host up general purpose counter
#define MO_GPHSTD_GPCNTRL 0x38C030 // Host down general purpose control
#define MO_GPHSTU_GPCNTRL 0x38C034 // Host up general purpose control
#define MO_GPHST_DMACNTRL 0x38C040 // Host DMA control
#define MO_GPHST_XFR_STAT 0x38C044 // Host transfer status
#define MO_GPHST_SOFT_RST 0x38C06C // Host software reset
/* ---------------------------------------------------------------------- */
/* RISC instructions */
#define RISC_SYNC 0x80000000
#define RISC_SYNC_ODD 0x80000000
#define RISC_SYNC_EVEN 0x80000200
#define RISC_RESYNC 0x80008000
#define RISC_RESYNC_ODD 0x80008000
#define RISC_RESYNC_EVEN 0x80008200
#define RISC_WRITE 0x10000000
#define RISC_WRITEC 0x50000000
#define RISC_READ 0x90000000
#define RISC_READC 0xA0000000
#define RISC_JUMP 0x70000000
#define RISC_SKIP 0x20000000
#define RISC_WRITERM 0xB0000000
#define RISC_WRITECM 0xC0000000
#define RISC_WRITECR 0xD0000000
#define RISC_IMM 0x00000001
#define RISC_SOL 0x08000000
#define RISC_EOL 0x04000000
#define RISC_IRQ2 0x02000000
#define RISC_IRQ1 0x01000000
#define RISC_CNT_NONE 0x00000000
#define RISC_CNT_INC 0x00010000
#define RISC_CNT_RSVR 0x00020000
#define RISC_CNT_RESET 0x00030000
#define RISC_JMP_SRP 0x01
/* ---------------------------------------------------------------------- */
/* various constants */
// DMA
/* Interrupt mask/status */
#define PCI_INT_VIDINT (1 << 0)
#define PCI_INT_AUDINT (1 << 1)
#define PCI_INT_TSINT (1 << 2)
#define PCI_INT_VIPINT (1 << 3)
#define PCI_INT_HSTINT (1 << 4)
#define PCI_INT_TM1INT (1 << 5)
#define PCI_INT_SRCDMAINT (1 << 6)
#define PCI_INT_DSTDMAINT (1 << 7)
#define PCI_INT_RISC_RD_BERRINT (1 << 10)
#define PCI_INT_RISC_WR_BERRINT (1 << 11)
#define PCI_INT_BRDG_BERRINT (1 << 12)
#define PCI_INT_SRC_DMA_BERRINT (1 << 13)
#define PCI_INT_DST_DMA_BERRINT (1 << 14)
#define PCI_INT_IPB_DMA_BERRINT (1 << 15)
#define PCI_INT_I2CDONE (1 << 16)
#define PCI_INT_I2CRACK (1 << 17)
#define PCI_INT_IR_SMPINT (1 << 18)
#define PCI_INT_GPIO_INT0 (1 << 19)
#define PCI_INT_GPIO_INT1 (1 << 20)
#define SEL_BTSC 0x01
#define SEL_EIAJ 0x02
#define SEL_A2 0x04
#define SEL_SAP 0x08
#define SEL_NICAM 0x10
#define SEL_FMRADIO 0x20
// AUD_CTL
#define AUD_INT_DN_RISCI1 (1 << 0)
#define AUD_INT_UP_RISCI1 (1 << 1)
#define AUD_INT_RDS_DN_RISCI1 (1 << 2)
#define AUD_INT_DN_RISCI2 (1 << 4) /* yes, 3 is skipped */
#define AUD_INT_UP_RISCI2 (1 << 5)
#define AUD_INT_RDS_DN_RISCI2 (1 << 6)
#define AUD_INT_DN_SYNC (1 << 12)
#define AUD_INT_UP_SYNC (1 << 13)
#define AUD_INT_RDS_DN_SYNC (1 << 14)
#define AUD_INT_OPC_ERR (1 << 16)
#define AUD_INT_BER_IRQ (1 << 20)
#define AUD_INT_MCHG_IRQ (1 << 21)
#define EN_BTSC_FORCE_MONO 0
#define EN_BTSC_FORCE_STEREO 1
#define EN_BTSC_FORCE_SAP 2
#define EN_BTSC_AUTO_STEREO 3
#define EN_BTSC_AUTO_SAP 4
#define EN_A2_FORCE_MONO1 8
#define EN_A2_FORCE_MONO2 9
#define EN_A2_FORCE_STEREO 10
#define EN_A2_AUTO_MONO2 11
#define EN_A2_AUTO_STEREO 12
#define EN_EIAJ_FORCE_MONO1 16
#define EN_EIAJ_FORCE_MONO2 17
#define EN_EIAJ_FORCE_STEREO 18
#define EN_EIAJ_AUTO_MONO2 19
#define EN_EIAJ_AUTO_STEREO 20
#define EN_NICAM_FORCE_MONO1 32
#define EN_NICAM_FORCE_MONO2 33
#define EN_NICAM_FORCE_STEREO 34
#define EN_NICAM_AUTO_MONO2 35
#define EN_NICAM_AUTO_STEREO 36
#define EN_FMRADIO_FORCE_MONO 24
#define EN_FMRADIO_FORCE_STEREO 25
#define EN_FMRADIO_AUTO_STEREO 26
#define EN_NICAM_AUTO_FALLBACK 0x00000040
#define EN_FMRADIO_EN_RDS 0x00000200
#define EN_NICAM_TRY_AGAIN_BIT 0x00000400
#define EN_DAC_ENABLE 0x00001000
#define EN_I2SOUT_ENABLE 0x00002000
#define EN_I2SIN_STR2DAC 0x00004000
#define EN_I2SIN_ENABLE 0x00008000
#define EN_DMTRX_SUMDIFF (0 << 7)
#define EN_DMTRX_SUMR (1 << 7)
#define EN_DMTRX_LR (2 << 7)
#define EN_DMTRX_MONO (3 << 7)
#define EN_DMTRX_BYPASS (1 << 11)
// Video
#define VID_CAPTURE_CONTROL 0x310180
#define CX23880_CAP_CTL_CAPTURE_VBI_ODD (1<<3)
#define CX23880_CAP_CTL_CAPTURE_VBI_EVEN (1<<2)
#define CX23880_CAP_CTL_CAPTURE_ODD (1<<1)
#define CX23880_CAP_CTL_CAPTURE_EVEN (1<<0)
#define VideoInputMux0 0x0
#define VideoInputMux1 0x1
#define VideoInputMux2 0x2
#define VideoInputMux3 0x3
#define VideoInputTuner 0x0
#define VideoInputComposite 0x1
#define VideoInputSVideo 0x2
#define VideoInputOther 0x3
#define Xtal0 0x1
#define Xtal1 0x2
#define XtalAuto 0x3
#define VideoFormatAuto 0x0
#define VideoFormatNTSC 0x1
#define VideoFormatNTSCJapan 0x2
#define VideoFormatNTSC443 0x3
#define VideoFormatPAL 0x4
#define VideoFormatPALB 0x4
#define VideoFormatPALD 0x4
#define VideoFormatPALG 0x4
#define VideoFormatPALH 0x4
#define VideoFormatPALI 0x4
#define VideoFormatPALBDGHI 0x4
#define VideoFormatPALM 0x5
#define VideoFormatPALN 0x6
#define VideoFormatPALNC 0x7
#define VideoFormatPAL60 0x8
#define VideoFormatSECAM 0x9
#define VideoFormatAuto27MHz 0x10
#define VideoFormatNTSC27MHz 0x11
#define VideoFormatNTSCJapan27MHz 0x12
#define VideoFormatNTSC44327MHz 0x13
#define VideoFormatPAL27MHz 0x14
#define VideoFormatPALB27MHz 0x14
#define VideoFormatPALD27MHz 0x14
#define VideoFormatPALG27MHz 0x14
#define VideoFormatPALH27MHz 0x14
#define VideoFormatPALI27MHz 0x14
#define VideoFormatPALBDGHI27MHz 0x14
#define VideoFormatPALM27MHz 0x15
#define VideoFormatPALN27MHz 0x16
#define VideoFormatPALNC27MHz 0x17
#define VideoFormatPAL6027MHz 0x18
#define VideoFormatSECAM27MHz 0x19
#define NominalUSECAM 0x87
#define NominalVSECAM 0x85
#define NominalUNTSC 0xFE
#define NominalVNTSC 0xB4
#define NominalContrast 0xD8
#define HFilterAutoFormat 0x0
#define HFilterCIF 0x1
#define HFilterQCIF 0x2
#define HFilterICON 0x3
#define VFilter2TapInterpolate 0
#define VFilter3TapInterpolate 1
#define VFilter4TapInterpolate 2
#define VFilter5TapInterpolate 3
#define VFilter2TapNoInterpolate 4
#define VFilter3TapNoInterpolate 5
#define VFilter4TapNoInterpolate 6
#define VFilter5TapNoInterpolate 7
#define ColorFormatRGB32 0x0000
#define ColorFormatRGB24 0x0011
#define ColorFormatRGB16 0x0022
#define ColorFormatRGB15 0x0033
#define ColorFormatYUY2 0x0044
#define ColorFormatBTYUV 0x0055
#define ColorFormatY8 0x0066
#define ColorFormatRGB8 0x0077
#define ColorFormatPL422 0x0088
#define ColorFormatPL411 0x0099
#define ColorFormatYUV12 0x00AA
#define ColorFormatYUV9 0x00BB
#define ColorFormatRAW 0x00EE
#define ColorFormatBSWAP 0x0300
#define ColorFormatWSWAP 0x0c00
#define ColorFormatEvenMask 0x050f
#define ColorFormatOddMask 0x0af0
#define ColorFormatGamma 0x1000
#define Interlaced 0x1
#define NonInterlaced 0x0
#define FieldEven 0x1
#define FieldOdd 0x0
#define TGReadWriteMode 0x0
#define TGEnableMode 0x1
#define DV_CbAlign 0x0
#define DV_Y0Align 0x1
#define DV_CrAlign 0x2
#define DV_Y1Align 0x3
#define DVF_Analog 0x0
#define DVF_CCIR656 0x1
#define DVF_ByteStream 0x2
#define DVF_ExtVSYNC 0x4
#define DVF_ExtField 0x5
#define CHANNEL_VID_Y 0x1
#define CHANNEL_VID_U 0x2
#define CHANNEL_VID_V 0x3
#define CHANNEL_VID_VBI 0x4
#define CHANNEL_AUD_DN 0x5
#define CHANNEL_AUD_UP 0x6
#define CHANNEL_AUD_RDS_DN 0x7
#define CHANNEL_MPEG_DN 0x8
#define CHANNEL_VIP_DN 0x9
#define CHANNEL_VIP_UP 0xA
#define CHANNEL_HOST_DN 0xB
#define CHANNEL_HOST_UP 0xC
#define CHANNEL_FIRST 0x1
#define CHANNEL_LAST 0xC
#define GP_COUNT_CONTROL_NONE 0x0
#define GP_COUNT_CONTROL_INC 0x1
#define GP_COUNT_CONTROL_RESERVED 0x2
#define GP_COUNT_CONTROL_RESET 0x3
#define PLL_PRESCALE_BY_2 2
#define PLL_PRESCALE_BY_3 3
#define PLL_PRESCALE_BY_4 4
#define PLL_PRESCALE_BY_5 5
#define HLNotchFilter4xFsc 0
#define HLNotchFilterSquare 1
#define HLNotchFilter135NTSC 2
#define HLNotchFilter135PAL 3
#define NTSC_8x_SUB_CARRIER 28.63636E6
#define PAL_8x_SUB_CARRIER 35.46895E6
// Default analog settings
#define DEFAULT_HUE_NTSC 0x00
#define DEFAULT_BRIGHTNESS_NTSC 0x00
#define DEFAULT_CONTRAST_NTSC 0x39
#define DEFAULT_SAT_U_NTSC 0x7F
#define DEFAULT_SAT_V_NTSC 0x5A
typedef enum
{
SOURCE_TUNER = 0,
SOURCE_COMPOSITE,
SOURCE_SVIDEO,
SOURCE_OTHER1,
SOURCE_OTHER2,
SOURCE_COMPVIASVIDEO,
SOURCE_CCIR656
} VIDEOSOURCETYPE;
#endif /* _CX88_REG_H_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,245 @@
/*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include "cx88.h"
static unsigned int vbibufs = 4;
module_param(vbibufs,int,0644);
MODULE_PARM_DESC(vbibufs,"number of vbi buffers, range 2-32");
static unsigned int vbi_debug;
module_param(vbi_debug,int,0644);
MODULE_PARM_DESC(vbi_debug,"enable debug messages [vbi]");
#define dprintk(level,fmt, arg...) if (vbi_debug >= level) \
printk(KERN_DEBUG "%s: " fmt, dev->core->name , ## arg)
/* ------------------------------------------------------------------ */
int cx8800_vbi_fmt (struct file *file, void *priv,
struct v4l2_format *f)
{
struct cx8800_fh *fh = priv;
struct cx8800_dev *dev = fh->dev;
f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH;
f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
f->fmt.vbi.offset = 244;
f->fmt.vbi.count[0] = VBI_LINE_COUNT;
f->fmt.vbi.count[1] = VBI_LINE_COUNT;
if (dev->core->tvnorm & V4L2_STD_525_60) {
/* ntsc */
f->fmt.vbi.sampling_rate = 28636363;
f->fmt.vbi.start[0] = 10;
f->fmt.vbi.start[1] = 273;
} else if (dev->core->tvnorm & V4L2_STD_625_50) {
/* pal */
f->fmt.vbi.sampling_rate = 35468950;
f->fmt.vbi.start[0] = 7 -1;
f->fmt.vbi.start[1] = 319 -1;
}
return 0;
}
static int cx8800_start_vbi_dma(struct cx8800_dev *dev,
struct cx88_dmaqueue *q,
struct cx88_buffer *buf)
{
struct cx88_core *core = dev->core;
/* setup fifo + format */
cx88_sram_channel_setup(dev->core, &cx88_sram_channels[SRAM_CH24],
buf->vb.width, buf->risc.dma);
cx_write(MO_VBOS_CONTROL, ( (1 << 18) | // comb filter delay fixup
(1 << 15) | // enable vbi capture
(1 << 11) ));
/* reset counter */
cx_write(MO_VBI_GPCNTRL, GP_COUNT_CONTROL_RESET);
q->count = 1;
/* enable irqs */
cx_set(MO_PCI_INTMSK, core->pci_irqmask | PCI_INT_VIDINT);
cx_set(MO_VID_INTMSK, 0x0f0088);
/* enable capture */
cx_set(VID_CAPTURE_CONTROL,0x18);
/* start dma */
cx_set(MO_DEV_CNTRL2, (1<<5));
cx_set(MO_VID_DMACNTRL, 0x88);
return 0;
}
int cx8800_stop_vbi_dma(struct cx8800_dev *dev)
{
struct cx88_core *core = dev->core;
/* stop dma */
cx_clear(MO_VID_DMACNTRL, 0x88);
/* disable capture */
cx_clear(VID_CAPTURE_CONTROL,0x18);
/* disable irqs */
cx_clear(MO_PCI_INTMSK, PCI_INT_VIDINT);
cx_clear(MO_VID_INTMSK, 0x0f0088);
return 0;
}
int cx8800_restart_vbi_queue(struct cx8800_dev *dev,
struct cx88_dmaqueue *q)
{
struct cx88_buffer *buf;
if (list_empty(&q->active))
return 0;
buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
dprintk(2,"restart_queue [%p/%d]: restart dma\n",
buf, buf->vb.i);
cx8800_start_vbi_dma(dev, q, buf);
list_for_each_entry(buf, &q->active, vb.queue)
buf->count = q->count++;
mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
return 0;
}
void cx8800_vbi_timeout(unsigned long data)
{
struct cx8800_dev *dev = (struct cx8800_dev*)data;
struct cx88_core *core = dev->core;
struct cx88_dmaqueue *q = &dev->vbiq;
struct cx88_buffer *buf;
unsigned long flags;
cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH24]);
cx_clear(MO_VID_DMACNTRL, 0x88);
cx_clear(VID_CAPTURE_CONTROL, 0x18);
spin_lock_irqsave(&dev->slock,flags);
while (!list_empty(&q->active)) {
buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
list_del(&buf->vb.queue);
buf->vb.state = VIDEOBUF_ERROR;
wake_up(&buf->vb.done);
printk("%s/0: [%p/%d] timeout - dma=0x%08lx\n", dev->core->name,
buf, buf->vb.i, (unsigned long)buf->risc.dma);
}
cx8800_restart_vbi_queue(dev,q);
spin_unlock_irqrestore(&dev->slock,flags);
}
/* ------------------------------------------------------------------ */
static int
vbi_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
{
*size = VBI_LINE_COUNT * VBI_LINE_LENGTH * 2;
if (0 == *count)
*count = vbibufs;
if (*count < 2)
*count = 2;
if (*count > 32)
*count = 32;
return 0;
}
static int
vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
enum v4l2_field field)
{
struct cx8800_fh *fh = q->priv_data;
struct cx8800_dev *dev = fh->dev;
struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb);
unsigned int size;
int rc;
size = VBI_LINE_COUNT * VBI_LINE_LENGTH * 2;
if (0 != buf->vb.baddr && buf->vb.bsize < size)
return -EINVAL;
if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
buf->vb.width = VBI_LINE_LENGTH;
buf->vb.height = VBI_LINE_COUNT;
buf->vb.size = size;
buf->vb.field = V4L2_FIELD_SEQ_TB;
if (0 != (rc = videobuf_iolock(q,&buf->vb,NULL)))
goto fail;
cx88_risc_buffer(dev->pci, &buf->risc,
dma->sglist,
0, buf->vb.width * buf->vb.height,
buf->vb.width, 0,
buf->vb.height);
}
buf->vb.state = VIDEOBUF_PREPARED;
return 0;
fail:
cx88_free_buffer(q,buf);
return rc;
}
static void
vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
{
struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb);
struct cx88_buffer *prev;
struct cx8800_fh *fh = vq->priv_data;
struct cx8800_dev *dev = fh->dev;
struct cx88_dmaqueue *q = &dev->vbiq;
/* add jump to stopper */
buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma);
if (list_empty(&q->active)) {
list_add_tail(&buf->vb.queue,&q->active);
cx8800_start_vbi_dma(dev, q, buf);
buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
dprintk(2,"[%p/%d] vbi_queue - first active\n",
buf, buf->vb.i);
} else {
prev = list_entry(q->active.prev, struct cx88_buffer, vb.queue);
list_add_tail(&buf->vb.queue,&q->active);
buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
dprintk(2,"[%p/%d] buffer_queue - append to active\n",
buf, buf->vb.i);
}
}
static void vbi_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
{
struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb);
cx88_free_buffer(q,buf);
}
const struct videobuf_queue_ops cx8800_vbi_qops = {
.buf_setup = vbi_setup,
.buf_prepare = vbi_prepare,
.buf_queue = vbi_queue,
.buf_release = vbi_release,
};
/* ------------------------------------------------------------------ */
/*
* Local variables:
* c-basic-offset: 8
* End:
*/

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,158 @@
/*
cx88-vp3054-i2c.c -- support for the secondary I2C bus of the
DNTV Live! DVB-T Pro (VP-3054), wired as:
GPIO[0] -> SCL, GPIO[1] -> SDA
(c) 2005 Chris Pascoe <c.pascoe@itee.uq.edu.au>
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/slab.h>
#include <linux/init.h>
#include <asm/io.h>
#include "cx88.h"
#include "cx88-vp3054-i2c.h"
MODULE_DESCRIPTION("driver for cx2388x VP3054 design");
MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
MODULE_LICENSE("GPL");
/* ----------------------------------------------------------------------- */
static void vp3054_bit_setscl(void *data, int state)
{
struct cx8802_dev *dev = data;
struct cx88_core *core = dev->core;
struct vp3054_i2c_state *vp3054_i2c = dev->vp3054;
if (state) {
vp3054_i2c->state |= 0x0001; /* SCL high */
vp3054_i2c->state &= ~0x0100; /* external pullup */
} else {
vp3054_i2c->state &= ~0x0001; /* SCL low */
vp3054_i2c->state |= 0x0100; /* drive pin */
}
cx_write(MO_GP0_IO, 0x010000 | vp3054_i2c->state);
cx_read(MO_GP0_IO);
}
static void vp3054_bit_setsda(void *data, int state)
{
struct cx8802_dev *dev = data;
struct cx88_core *core = dev->core;
struct vp3054_i2c_state *vp3054_i2c = dev->vp3054;
if (state) {
vp3054_i2c->state |= 0x0002; /* SDA high */
vp3054_i2c->state &= ~0x0200; /* tristate pin */
} else {
vp3054_i2c->state &= ~0x0002; /* SDA low */
vp3054_i2c->state |= 0x0200; /* drive pin */
}
cx_write(MO_GP0_IO, 0x020000 | vp3054_i2c->state);
cx_read(MO_GP0_IO);
}
static int vp3054_bit_getscl(void *data)
{
struct cx8802_dev *dev = data;
struct cx88_core *core = dev->core;
u32 state;
state = cx_read(MO_GP0_IO);
return (state & 0x01) ? 1 : 0;
}
static int vp3054_bit_getsda(void *data)
{
struct cx8802_dev *dev = data;
struct cx88_core *core = dev->core;
u32 state;
state = cx_read(MO_GP0_IO);
return (state & 0x02) ? 1 : 0;
}
/* ----------------------------------------------------------------------- */
static const struct i2c_algo_bit_data vp3054_i2c_algo_template = {
.setsda = vp3054_bit_setsda,
.setscl = vp3054_bit_setscl,
.getsda = vp3054_bit_getsda,
.getscl = vp3054_bit_getscl,
.udelay = 16,
.timeout = 200,
};
/* ----------------------------------------------------------------------- */
int vp3054_i2c_probe(struct cx8802_dev *dev)
{
struct cx88_core *core = dev->core;
struct vp3054_i2c_state *vp3054_i2c;
int rc;
if (core->boardnr != CX88_BOARD_DNTV_LIVE_DVB_T_PRO)
return 0;
vp3054_i2c = kzalloc(sizeof(*vp3054_i2c), GFP_KERNEL);
if (vp3054_i2c == NULL)
return -ENOMEM;
dev->vp3054 = vp3054_i2c;
vp3054_i2c->algo = vp3054_i2c_algo_template;
vp3054_i2c->adap.dev.parent = &dev->pci->dev;
strlcpy(vp3054_i2c->adap.name, core->name,
sizeof(vp3054_i2c->adap.name));
vp3054_i2c->adap.owner = THIS_MODULE;
vp3054_i2c->algo.data = dev;
i2c_set_adapdata(&vp3054_i2c->adap, dev);
vp3054_i2c->adap.algo_data = &vp3054_i2c->algo;
vp3054_bit_setscl(dev,1);
vp3054_bit_setsda(dev,1);
rc = i2c_bit_add_bus(&vp3054_i2c->adap);
if (0 != rc) {
printk("%s: vp3054_i2c register FAILED\n", core->name);
kfree(dev->vp3054);
dev->vp3054 = NULL;
}
return rc;
}
void vp3054_i2c_remove(struct cx8802_dev *dev)
{
struct vp3054_i2c_state *vp3054_i2c = dev->vp3054;
if (vp3054_i2c == NULL ||
dev->core->boardnr != CX88_BOARD_DNTV_LIVE_DVB_T_PRO)
return;
i2c_del_adapter(&vp3054_i2c->adap);
kfree(vp3054_i2c);
}
EXPORT_SYMBOL(vp3054_i2c_probe);
EXPORT_SYMBOL(vp3054_i2c_remove);

View file

@ -0,0 +1,41 @@
/*
cx88-vp3054-i2c.h -- support for the secondary I2C bus of the
DNTV Live! DVB-T Pro (VP-3054), wired as:
GPIO[0] -> SCL, GPIO[1] -> SDA
(c) 2005 Chris Pascoe <c.pascoe@itee.uq.edu.au>
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.
*/
/* ----------------------------------------------------------------------- */
struct vp3054_i2c_state {
struct i2c_adapter adap;
struct i2c_algo_bit_data algo;
u32 state;
};
/* ----------------------------------------------------------------------- */
#if IS_ENABLED(CONFIG_VIDEO_CX88_VP3054)
int vp3054_i2c_probe(struct cx8802_dev *dev);
void vp3054_i2c_remove(struct cx8802_dev *dev);
#else
static inline int vp3054_i2c_probe(struct cx8802_dev *dev)
{ return 0; }
static inline void vp3054_i2c_remove(struct cx8802_dev *dev)
{ }
#endif

View file

@ -0,0 +1,753 @@
/*
*
* v4l2 device driver for cx2388x based TV cards
*
* (c) 2003,04 Gerd Knorr <kraxel@bytesex.org> [SUSE Labs]
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/pci.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <linux/videodev2.h>
#include <linux/kdev_t.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fh.h>
#include <media/tuner.h>
#include <media/tveeprom.h>
#include <media/videobuf-dma-sg.h>
#include <media/cx2341x.h>
#include <media/videobuf-dvb.h>
#include <media/ir-kbd-i2c.h>
#include <media/wm8775.h>
#include "btcx-risc.h"
#include "cx88-reg.h"
#include "tuner-xc2028.h"
#include <linux/mutex.h>
#define CX88_VERSION "0.0.9"
#define UNSET (-1U)
#define CX88_MAXBOARDS 8
/* Max number of inputs by card */
#define MAX_CX88_INPUT 8
/* ----------------------------------------------------------- */
/* defines and enums */
/* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM/LC */
#define CX88_NORMS (V4L2_STD_ALL \
& ~V4L2_STD_PAL_H \
& ~V4L2_STD_NTSC_M_KR \
& ~V4L2_STD_SECAM_LC)
#define FORMAT_FLAGS_PACKED 0x01
#define FORMAT_FLAGS_PLANAR 0x02
#define VBI_LINE_COUNT 17
#define VBI_LINE_LENGTH 2048
#define AUD_RDS_LINES 4
/* need "shadow" registers for some write-only ones ... */
#define SHADOW_AUD_VOL_CTL 1
#define SHADOW_AUD_BAL_CTL 2
#define SHADOW_MAX 3
/* FM Radio deemphasis type */
enum cx88_deemph_type {
FM_NO_DEEMPH = 0,
FM_DEEMPH_50,
FM_DEEMPH_75
};
enum cx88_board_type {
CX88_BOARD_NONE = 0,
CX88_MPEG_DVB,
CX88_MPEG_BLACKBIRD
};
enum cx8802_board_access {
CX8802_DRVCTL_SHARED = 1,
CX8802_DRVCTL_EXCLUSIVE = 2,
};
/* ----------------------------------------------------------- */
/* tv norms */
static inline unsigned int norm_maxw(v4l2_std_id norm)
{
return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 720 : 768;
}
static inline unsigned int norm_maxh(v4l2_std_id norm)
{
return (norm & V4L2_STD_625_50) ? 576 : 480;
}
/* ----------------------------------------------------------- */
/* static data */
struct cx8800_fmt {
const char *name;
u32 fourcc; /* v4l2 format id */
int depth;
int flags;
u32 cxformat;
};
/* ----------------------------------------------------------- */
/* SRAM memory management data (see cx88-core.c) */
#define SRAM_CH21 0 /* video */
#define SRAM_CH22 1
#define SRAM_CH23 2
#define SRAM_CH24 3 /* vbi */
#define SRAM_CH25 4 /* audio */
#define SRAM_CH26 5
#define SRAM_CH28 6 /* mpeg */
#define SRAM_CH27 7 /* audio rds */
/* more */
struct sram_channel {
const char *name;
u32 cmds_start;
u32 ctrl_start;
u32 cdt;
u32 fifo_start;
u32 fifo_size;
u32 ptr1_reg;
u32 ptr2_reg;
u32 cnt1_reg;
u32 cnt2_reg;
};
extern const struct sram_channel cx88_sram_channels[];
/* ----------------------------------------------------------- */
/* card configuration */
#define CX88_BOARD_NOAUTO UNSET
#define CX88_BOARD_UNKNOWN 0
#define CX88_BOARD_HAUPPAUGE 1
#define CX88_BOARD_GDI 2
#define CX88_BOARD_PIXELVIEW 3
#define CX88_BOARD_ATI_WONDER_PRO 4
#define CX88_BOARD_WINFAST2000XP_EXPERT 5
#define CX88_BOARD_AVERTV_STUDIO_303 6
#define CX88_BOARD_MSI_TVANYWHERE_MASTER 7
#define CX88_BOARD_WINFAST_DV2000 8
#define CX88_BOARD_LEADTEK_PVR2000 9
#define CX88_BOARD_IODATA_GVVCP3PCI 10
#define CX88_BOARD_PROLINK_PLAYTVPVR 11
#define CX88_BOARD_ASUS_PVR_416 12
#define CX88_BOARD_MSI_TVANYWHERE 13
#define CX88_BOARD_KWORLD_DVB_T 14
#define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1 15
#define CX88_BOARD_KWORLD_LTV883 16
#define CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q 17
#define CX88_BOARD_HAUPPAUGE_DVB_T1 18
#define CX88_BOARD_CONEXANT_DVB_T1 19
#define CX88_BOARD_PROVIDEO_PV259 20
#define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS 21
#define CX88_BOARD_PCHDTV_HD3000 22
#define CX88_BOARD_DNTV_LIVE_DVB_T 23
#define CX88_BOARD_HAUPPAUGE_ROSLYN 24
#define CX88_BOARD_DIGITALLOGIC_MEC 25
#define CX88_BOARD_IODATA_GVBCTV7E 26
#define CX88_BOARD_PIXELVIEW_PLAYTV_ULTRA_PRO 27
#define CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T 28
#define CX88_BOARD_ADSTECH_DVB_T_PCI 29
#define CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1 30
#define CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD 31
#define CX88_BOARD_AVERMEDIA_ULTRATV_MC_550 32
#define CX88_BOARD_KWORLD_VSTREAM_EXPERT_DVD 33
#define CX88_BOARD_ATI_HDTVWONDER 34
#define CX88_BOARD_WINFAST_DTV1000 35
#define CX88_BOARD_AVERTV_303 36
#define CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1 37
#define CX88_BOARD_HAUPPAUGE_NOVASE2_S1 38
#define CX88_BOARD_KWORLD_DVBS_100 39
#define CX88_BOARD_HAUPPAUGE_HVR1100 40
#define CX88_BOARD_HAUPPAUGE_HVR1100LP 41
#define CX88_BOARD_DNTV_LIVE_DVB_T_PRO 42
#define CX88_BOARD_KWORLD_DVB_T_CX22702 43
#define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL 44
#define CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT 45
#define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID 46
#define CX88_BOARD_PCHDTV_HD5500 47
#define CX88_BOARD_KWORLD_MCE200_DELUXE 48
#define CX88_BOARD_PIXELVIEW_PLAYTV_P7000 49
#define CX88_BOARD_NPGTECH_REALTV_TOP10FM 50
#define CX88_BOARD_WINFAST_DTV2000H 51
#define CX88_BOARD_GENIATECH_DVBS 52
#define CX88_BOARD_HAUPPAUGE_HVR3000 53
#define CX88_BOARD_NORWOOD_MICRO 54
#define CX88_BOARD_TE_DTV_250_OEM_SWANN 55
#define CX88_BOARD_HAUPPAUGE_HVR1300 56
#define CX88_BOARD_ADSTECH_PTV_390 57
#define CX88_BOARD_PINNACLE_PCTV_HD_800i 58
#define CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO 59
#define CX88_BOARD_PINNACLE_HYBRID_PCTV 60
#define CX88_BOARD_WINFAST_TV2000_XP_GLOBAL 61
#define CX88_BOARD_POWERCOLOR_REAL_ANGEL 62
#define CX88_BOARD_GENIATECH_X8000_MT 63
#define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO 64
#define CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD 65
#define CX88_BOARD_PROLINK_PV_8000GT 66
#define CX88_BOARD_KWORLD_ATSC_120 67
#define CX88_BOARD_HAUPPAUGE_HVR4000 68
#define CX88_BOARD_HAUPPAUGE_HVR4000LITE 69
#define CX88_BOARD_TEVII_S460 70
#define CX88_BOARD_OMICOM_SS4_PCI 71
#define CX88_BOARD_TBS_8920 72
#define CX88_BOARD_TEVII_S420 73
#define CX88_BOARD_PROLINK_PV_GLOBAL_XTREME 74
#define CX88_BOARD_PROF_7300 75
#define CX88_BOARD_SATTRADE_ST4200 76
#define CX88_BOARD_TBS_8910 77
#define CX88_BOARD_PROF_6200 78
#define CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII 79
#define CX88_BOARD_HAUPPAUGE_IRONLY 80
#define CX88_BOARD_WINFAST_DTV1800H 81
#define CX88_BOARD_WINFAST_DTV2000H_J 82
#define CX88_BOARD_PROF_7301 83
#define CX88_BOARD_SAMSUNG_SMT_7020 84
#define CX88_BOARD_TWINHAN_VP1027_DVBS 85
#define CX88_BOARD_TEVII_S464 86
#define CX88_BOARD_WINFAST_DTV2000H_PLUS 87
#define CX88_BOARD_WINFAST_DTV1800H_XC4000 88
#define CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36 89
#define CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43 90
enum cx88_itype {
CX88_VMUX_COMPOSITE1 = 1,
CX88_VMUX_COMPOSITE2,
CX88_VMUX_COMPOSITE3,
CX88_VMUX_COMPOSITE4,
CX88_VMUX_SVIDEO,
CX88_VMUX_TELEVISION,
CX88_VMUX_CABLE,
CX88_VMUX_DVB,
CX88_VMUX_DEBUG,
CX88_RADIO,
};
struct cx88_input {
enum cx88_itype type;
u32 gpio0, gpio1, gpio2, gpio3;
unsigned int vmux:2;
unsigned int audioroute:4;
};
enum cx88_audio_chip {
CX88_AUDIO_WM8775 = 1,
CX88_AUDIO_TVAUDIO,
};
struct cx88_board {
const char *name;
unsigned int tuner_type;
unsigned int radio_type;
unsigned char tuner_addr;
unsigned char radio_addr;
int tda9887_conf;
struct cx88_input input[MAX_CX88_INPUT];
struct cx88_input radio;
enum cx88_board_type mpeg;
enum cx88_audio_chip audio_chip;
int num_frontends;
/* Used for I2S devices */
int i2sinputcntl;
};
struct cx88_subid {
u16 subvendor;
u16 subdevice;
u32 card;
};
enum cx88_tvaudio {
WW_NONE = 1,
WW_BTSC,
WW_BG,
WW_DK,
WW_I,
WW_L,
WW_EIAJ,
WW_I2SPT,
WW_FM,
WW_I2SADC,
WW_M
};
#define INPUT(nr) (core->board.input[nr])
/* ----------------------------------------------------------- */
/* device / file handle status */
#define RESOURCE_OVERLAY 1
#define RESOURCE_VIDEO 2
#define RESOURCE_VBI 4
#define BUFFER_TIMEOUT msecs_to_jiffies(2000)
/* buffer for one video frame */
struct cx88_buffer {
/* common v4l buffer stuff -- must be first */
struct videobuf_buffer vb;
/* cx88 specific */
unsigned int bpl;
struct btcx_riscmem risc;
const struct cx8800_fmt *fmt;
u32 count;
};
struct cx88_dmaqueue {
struct list_head active;
struct list_head queued;
struct timer_list timeout;
struct btcx_riscmem stopper;
u32 count;
};
struct cx88_core {
struct list_head devlist;
atomic_t refcount;
/* board name */
int nr;
char name[32];
u32 model;
/* pci stuff */
int pci_bus;
int pci_slot;
u32 __iomem *lmmio;
u8 __iomem *bmmio;
u32 shadow[SHADOW_MAX];
int pci_irqmask;
/* i2c i/o */
struct i2c_adapter i2c_adap;
struct i2c_algo_bit_data i2c_algo;
struct i2c_client i2c_client;
u32 i2c_state, i2c_rc;
/* config info -- analog */
struct v4l2_device v4l2_dev;
struct v4l2_ctrl_handler video_hdl;
struct v4l2_ctrl *chroma_agc;
struct v4l2_ctrl_handler audio_hdl;
struct v4l2_subdev *sd_wm8775;
struct i2c_client *i2c_rtc;
unsigned int boardnr;
struct cx88_board board;
/* Supported V4L _STD_ tuner formats */
unsigned int tuner_formats;
/* config info -- dvb */
#if IS_ENABLED(CONFIG_VIDEO_CX88_DVB)
int (*prev_set_voltage)(struct dvb_frontend *fe, fe_sec_voltage_t voltage);
#endif
void (*gate_ctrl)(struct cx88_core *core, int open);
/* state info */
struct task_struct *kthread;
v4l2_std_id tvnorm;
enum cx88_tvaudio tvaudio;
u32 audiomode_manual;
u32 audiomode_current;
u32 input;
u32 last_analog_input;
u32 astat;
u32 use_nicam;
unsigned long last_change;
/* IR remote control state */
struct cx88_IR *ir;
/* I2C remote data */
struct IR_i2c_init_data init_data;
struct wm8775_platform_data wm8775_data;
struct mutex lock;
/* various v4l controls */
u32 freq;
int users;
int mpeg_users;
/* cx88-video needs to access cx8802 for hybrid tuner pll access. */
struct cx8802_dev *dvbdev;
enum cx88_board_type active_type_id;
int active_ref;
int active_fe_id;
};
static inline struct cx88_core *to_core(struct v4l2_device *v4l2_dev)
{
return container_of(v4l2_dev, struct cx88_core, v4l2_dev);
}
#define call_hw(core, grpid, o, f, args...) \
do { \
if (!core->i2c_rc) { \
if (core->gate_ctrl) \
core->gate_ctrl(core, 1); \
v4l2_device_call_all(&core->v4l2_dev, grpid, o, f, ##args); \
if (core->gate_ctrl) \
core->gate_ctrl(core, 0); \
} \
} while (0)
#define call_all(core, o, f, args...) call_hw(core, 0, o, f, ##args)
#define WM8775_GID (1 << 0)
#define wm8775_s_ctrl(core, id, val) \
do { \
struct v4l2_ctrl *ctrl_ = \
v4l2_ctrl_find(core->sd_wm8775->ctrl_handler, id); \
if (ctrl_ && !core->i2c_rc) { \
if (core->gate_ctrl) \
core->gate_ctrl(core, 1); \
v4l2_ctrl_s_ctrl(ctrl_, val); \
if (core->gate_ctrl) \
core->gate_ctrl(core, 0); \
} \
} while (0)
#define wm8775_g_ctrl(core, id) \
({ \
struct v4l2_ctrl *ctrl_ = \
v4l2_ctrl_find(core->sd_wm8775->ctrl_handler, id); \
s32 val = 0; \
if (ctrl_ && !core->i2c_rc) { \
if (core->gate_ctrl) \
core->gate_ctrl(core, 1); \
val = v4l2_ctrl_g_ctrl(ctrl_); \
if (core->gate_ctrl) \
core->gate_ctrl(core, 0); \
} \
val; \
})
struct cx8800_dev;
struct cx8802_dev;
/* ----------------------------------------------------------- */
/* function 0: video stuff */
struct cx8800_fh {
struct v4l2_fh fh;
struct cx8800_dev *dev;
unsigned int resources;
/* video capture */
struct videobuf_queue vidq;
/* vbi capture */
struct videobuf_queue vbiq;
};
struct cx8800_suspend_state {
int disabled;
};
struct cx8800_dev {
struct cx88_core *core;
spinlock_t slock;
/* various device info */
unsigned int resources;
struct video_device *video_dev;
struct video_device *vbi_dev;
struct video_device *radio_dev;
/* pci i/o */
struct pci_dev *pci;
unsigned char pci_rev,pci_lat;
const struct cx8800_fmt *fmt;
unsigned int width, height;
/* capture queues */
struct cx88_dmaqueue vidq;
struct cx88_dmaqueue vbiq;
/* various v4l controls */
/* other global state info */
struct cx8800_suspend_state state;
};
/* ----------------------------------------------------------- */
/* function 1: audio/alsa stuff */
/* =============> moved to cx88-alsa.c <====================== */
/* ----------------------------------------------------------- */
/* function 2: mpeg stuff */
struct cx8802_fh {
struct v4l2_fh fh;
struct cx8802_dev *dev;
struct videobuf_queue mpegq;
};
struct cx8802_suspend_state {
int disabled;
};
struct cx8802_driver {
struct cx88_core *core;
/* List of drivers attached to device */
struct list_head drvlist;
/* Type of driver and access required */
enum cx88_board_type type_id;
enum cx8802_board_access hw_access;
/* MPEG 8802 internal only */
int (*suspend)(struct pci_dev *pci_dev, pm_message_t state);
int (*resume)(struct pci_dev *pci_dev);
/* Callers to the following functions must hold core->lock */
/* MPEG 8802 -> mini driver - Driver probe and configuration */
int (*probe)(struct cx8802_driver *drv);
int (*remove)(struct cx8802_driver *drv);
/* MPEG 8802 -> mini driver - Access for hardware control */
int (*advise_acquire)(struct cx8802_driver *drv);
int (*advise_release)(struct cx8802_driver *drv);
/* MPEG 8802 <- mini driver - Access for hardware control */
int (*request_acquire)(struct cx8802_driver *drv);
int (*request_release)(struct cx8802_driver *drv);
};
struct cx8802_dev {
struct cx88_core *core;
spinlock_t slock;
/* pci i/o */
struct pci_dev *pci;
unsigned char pci_rev,pci_lat;
/* dma queues */
struct cx88_dmaqueue mpegq;
u32 ts_packet_size;
u32 ts_packet_count;
/* other global state info */
struct cx8802_suspend_state state;
/* for blackbird only */
struct list_head devlist;
#if IS_ENABLED(CONFIG_VIDEO_CX88_BLACKBIRD)
struct video_device *mpeg_dev;
u32 mailbox;
int width;
int height;
unsigned char mpeg_active; /* nonzero if mpeg encoder is active */
/* mpeg params */
struct cx2341x_handler cxhdl;
#endif
#if IS_ENABLED(CONFIG_VIDEO_CX88_DVB)
/* for dvb only */
struct videobuf_dvb_frontends frontends;
#endif
#if IS_ENABLED(CONFIG_VIDEO_CX88_VP3054)
/* For VP3045 secondary I2C bus support */
struct vp3054_i2c_state *vp3054;
#endif
/* for switching modulation types */
unsigned char ts_gen_cntrl;
/* List of attached drivers; must hold core->lock to access */
struct list_head drvlist;
struct work_struct request_module_wk;
};
/* ----------------------------------------------------------- */
#define cx_read(reg) readl(core->lmmio + ((reg)>>2))
#define cx_write(reg,value) writel((value), core->lmmio + ((reg)>>2))
#define cx_writeb(reg,value) writeb((value), core->bmmio + (reg))
#define cx_andor(reg,mask,value) \
writel((readl(core->lmmio+((reg)>>2)) & ~(mask)) |\
((value) & (mask)), core->lmmio+((reg)>>2))
#define cx_set(reg,bit) cx_andor((reg),(bit),(bit))
#define cx_clear(reg,bit) cx_andor((reg),(bit),0)
#define cx_wait(d) { if (need_resched()) schedule(); else udelay(d); }
/* shadow registers */
#define cx_sread(sreg) (core->shadow[sreg])
#define cx_swrite(sreg,reg,value) \
(core->shadow[sreg] = value, \
writel(core->shadow[sreg], core->lmmio + ((reg)>>2)))
#define cx_sandor(sreg,reg,mask,value) \
(core->shadow[sreg] = (core->shadow[sreg] & ~(mask)) | ((value) & (mask)), \
writel(core->shadow[sreg], core->lmmio + ((reg)>>2)))
/* ----------------------------------------------------------- */
/* cx88-core.c */
extern unsigned int cx88_core_debug;
extern void cx88_print_irqbits(const char *name, const char *tag, const char *strings[],
int len, u32 bits, u32 mask);
extern int cx88_core_irq(struct cx88_core *core, u32 status);
extern void cx88_wakeup(struct cx88_core *core,
struct cx88_dmaqueue *q, u32 count);
extern void cx88_shutdown(struct cx88_core *core);
extern int cx88_reset(struct cx88_core *core);
extern int
cx88_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
struct scatterlist *sglist,
unsigned int top_offset, unsigned int bottom_offset,
unsigned int bpl, unsigned int padding, unsigned int lines);
extern int
cx88_risc_databuffer(struct pci_dev *pci, struct btcx_riscmem *risc,
struct scatterlist *sglist, unsigned int bpl,
unsigned int lines, unsigned int lpi);
extern int
cx88_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
u32 reg, u32 mask, u32 value);
extern void
cx88_free_buffer(struct videobuf_queue *q, struct cx88_buffer *buf);
extern void cx88_risc_disasm(struct cx88_core *core,
struct btcx_riscmem *risc);
extern int cx88_sram_channel_setup(struct cx88_core *core,
const struct sram_channel *ch,
unsigned int bpl, u32 risc);
extern void cx88_sram_channel_dump(struct cx88_core *core,
const struct sram_channel *ch);
extern int cx88_set_scale(struct cx88_core *core, unsigned int width,
unsigned int height, enum v4l2_field field);
extern int cx88_set_tvnorm(struct cx88_core *core, v4l2_std_id norm);
extern struct video_device *cx88_vdev_init(struct cx88_core *core,
struct pci_dev *pci,
const struct video_device *template_,
const char *type);
extern struct cx88_core* cx88_core_get(struct pci_dev *pci);
extern void cx88_core_put(struct cx88_core *core,
struct pci_dev *pci);
extern int cx88_start_audio_dma(struct cx88_core *core);
extern int cx88_stop_audio_dma(struct cx88_core *core);
/* ----------------------------------------------------------- */
/* cx88-vbi.c */
/* Can be used as g_vbi_fmt, try_vbi_fmt and s_vbi_fmt */
int cx8800_vbi_fmt (struct file *file, void *priv,
struct v4l2_format *f);
/*
int cx8800_start_vbi_dma(struct cx8800_dev *dev,
struct cx88_dmaqueue *q,
struct cx88_buffer *buf);
*/
int cx8800_stop_vbi_dma(struct cx8800_dev *dev);
int cx8800_restart_vbi_queue(struct cx8800_dev *dev,
struct cx88_dmaqueue *q);
void cx8800_vbi_timeout(unsigned long data);
extern const struct videobuf_queue_ops cx8800_vbi_qops;
/* ----------------------------------------------------------- */
/* cx88-i2c.c */
extern int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci);
/* ----------------------------------------------------------- */
/* cx88-cards.c */
extern int cx88_tuner_callback(void *dev, int component, int command, int arg);
extern int cx88_get_resources(const struct cx88_core *core,
struct pci_dev *pci);
extern struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr);
extern void cx88_setup_xc3028(struct cx88_core *core, struct xc2028_ctrl *ctl);
/* ----------------------------------------------------------- */
/* cx88-tvaudio.c */
void cx88_set_tvaudio(struct cx88_core *core);
void cx88_newstation(struct cx88_core *core);
void cx88_get_stereo(struct cx88_core *core, struct v4l2_tuner *t);
void cx88_set_stereo(struct cx88_core *core, u32 mode, int manual);
int cx88_audio_thread(void *data);
int cx8802_register_driver(struct cx8802_driver *drv);
int cx8802_unregister_driver(struct cx8802_driver *drv);
/* Caller must hold core->lock */
struct cx8802_driver * cx8802_get_driver(struct cx8802_dev *dev, enum cx88_board_type btype);
/* ----------------------------------------------------------- */
/* cx88-dsp.c */
s32 cx88_dsp_detect_stereo_sap(struct cx88_core *core);
/* ----------------------------------------------------------- */
/* cx88-input.c */
int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci);
int cx88_ir_fini(struct cx88_core *core);
void cx88_ir_irq(struct cx88_core *core);
int cx88_ir_start(struct cx88_core *core);
void cx88_ir_stop(struct cx88_core *core);
extern void cx88_i2c_init_ir(struct cx88_core *core);
/* ----------------------------------------------------------- */
/* cx88-mpeg.c */
int cx8802_buf_prepare(struct videobuf_queue *q,struct cx8802_dev *dev,
struct cx88_buffer *buf, enum v4l2_field field);
void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf);
void cx8802_cancel_buffers(struct cx8802_dev *dev);
/* ----------------------------------------------------------- */
/* cx88-video.c*/
int cx88_enum_input (struct cx88_core *core,struct v4l2_input *i);
int cx88_set_freq(struct cx88_core *core, const struct v4l2_frequency *f);
int cx88_video_mux(struct cx88_core *core, unsigned int input);
void cx88_querycap(struct file *file, struct cx88_core *core,
struct v4l2_capability *cap);