V4L/DVB (12730): Add conexant cx25821 driver

GIT_BRANCH=devel
GIT_AUTHOR_DATE=1252851239
GIT_AUTHOR_NAME=Palash Bandyopadhyay
GIT_AUTHOR_EMAIL=Palash.Bandyopadhyay@conexant.com
Add conexant cx25821 driver release v106 of the Athena driver.

Signed-off-by: Palash Bandyopadhyay <palash.bandyopadhyay@conexant.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
Mauro Carvalho Chehab 2009-09-15 11:33:54 -03:00
parent 78f28b7c55
commit 02b20b0b4c
37 changed files with 16790 additions and 0 deletions

View File

@ -0,0 +1,34 @@
config VIDEO_CX25821
tristate "Conexant cx25821 support"
depends on DVB_CORE && VIDEO_DEV && PCI && I2C && INPUT
select I2C_ALGOBIT
select VIDEO_BTCX
select VIDEO_TVEEPROM
select VIDEO_IR
select VIDEOBUF_DVB
select VIDEOBUF_DMA_SG
select VIDEO_CX25840
select VIDEO_CX2341X
---help---
This is a video4linux driver for Conexant 25821 based
TV cards.
To compile this driver as a module, choose M here: the
module will be called cx25821
config VIDEO_CX25821_ALSA
tristate "Conexant 25821 DMA audio support"
depends on VIDEO_CX25821 && SND && EXPERIMENTAL
select SND_PCM
---help---
This is a video4linux driver for direct (DMA) audio on
Conexant 25821 based capture 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 cx25821-alsa.

View File

@ -0,0 +1,15 @@
cx25821-objs := cx25821-core.o cx25821-cards.o cx25821-i2c.o cx25821-gpio.o \
cx25821-medusa-video.o cx25821-video.o cx25821-video0.o cx25821-video1.o \
cx25821-video2.o cx25821-video3.o cx25821-video4.o cx25821-video5.o \
cx25821-video6.o cx25821-video7.o cx25821-vidups9.o cx25821-vidups10.o \
cx25821-audups11.o cx25821-video-upstream.o cx25821-video-upstream-ch2.o \
cx25821-audio-upstream.o cx25821-videoioctl.o
obj-$(CONFIG_VIDEO_CX25821) += cx25821.o
obj-$(CONFIG_VIDEO_CX25821_ALSA) += cx25821-alsa.o
EXTRA_CFLAGS += -Idrivers/media/video
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
EXTRA_CFLAGS += $(extra-cflags-y) $(extra-cflags-m)

View File

@ -0,0 +1,791 @@
/*
* Driver for the Conexant CX25821 PCIe bridge
*
* Copyright (C) 2009 Conexant Systems Inc.
* Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
* Based on SAA713x ALSA driver and CX88 driver
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/vmalloc.h>
#include <linux/dma-mapping.h>
#include <linux/pci.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 "cx25821.h"
#include "cx25821-reg.h"
#define AUDIO_SRAM_CHANNEL SRAM_CH08
#define dprintk(level,fmt, arg...) if (debug >= level) \
printk(KERN_INFO "%s/1: " fmt, chip->dev->name , ## arg)
#define dprintk_core(level,fmt, arg...) if (debug >= level) \
printk(KERN_DEBUG "%s/1: " fmt, chip->dev->name , ## arg)
/****************************************************************************
Data type declarations - Can be moded to a header file later
****************************************************************************/
static struct snd_card *snd_cx25821_cards[SNDRV_CARDS];
static int devno;
struct cx25821_audio_dev {
struct cx25821_dev *dev;
struct cx25821_dmaqueue q;
/* pci i/o */
struct pci_dev *pci;
/* audio controls */
int irq;
struct snd_card *card;
unsigned long iobase;
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 cx25821_buffer *buf;
struct snd_pcm_substream *substream;
};
typedef struct cx25821_audio_dev snd_cx25821_card_t;
/****************************************************************************
Module global static vars
****************************************************************************/
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 1};
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable cx25821 soundcard. default enabled.");
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for cx25821 capture interface(s).");
/****************************************************************************
Module macros
****************************************************************************/
MODULE_DESCRIPTION("ALSA driver module for cx25821 based capture cards");
MODULE_AUTHOR("Hiep Huynh");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{Conexant,25821}");//"{{Conexant,23881},"
static unsigned int debug;
module_param(debug,int,0644);
MODULE_PARM_DESC(debug,"enable debug messages");
/****************************************************************************
Module specific funtions
****************************************************************************/
/* Constants taken from cx88-reg.h */
#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 GP_COUNT_CONTROL_RESET 0x3
#define PCI_MSK_AUD_EXT (1 << 4)
#define PCI_MSK_AUD_INT (1 << 3)
/*
* BOARD Specific: Sets audio DMA
*/
static int _cx25821_start_audio_dma(snd_cx25821_card_t *chip)
{
struct cx25821_buffer *buf = chip->buf;
struct cx25821_dev * dev = chip->dev;
struct sram_channel *audio_ch = &cx25821_sram_channels[AUDIO_SRAM_CHANNEL];
u32 tmp = 0;
// enable output on the GPIO 0 for the MCLK ADC (Audio)
cx25821_set_gpiopin_direction( chip->dev, 0, 0 );
/* Make sure RISC/FIFO are off before changing FIFO/RISC settings */
cx_clear(AUD_INT_DMA_CTL, FLD_AUD_DST_A_RISC_EN | FLD_AUD_DST_A_FIFO_EN );
/* setup fifo + format - out channel */
cx25821_sram_channel_setup_audio(chip->dev, audio_ch, buf->bpl, buf->risc.dma);
/* sets bpl size */
cx_write(AUD_A_LNGTH, buf->bpl);
/* reset counter */
cx_write(AUD_A_GPCNT_CTL, GP_COUNT_CONTROL_RESET); //GP_COUNT_CONTROL_RESET = 0x3
atomic_set(&chip->count, 0);
//Set the input mode to 16-bit
tmp = cx_read(AUD_A_CFG);
cx_write(AUD_A_CFG, tmp | FLD_AUD_DST_PK_MODE | FLD_AUD_DST_ENABLE | FLD_AUD_CLK_ENABLE);
//printk(KERN_INFO "DEBUG: Start audio DMA, %d B/line, cmds_start(0x%x)= %d lines/FIFO, %d periods, %d "
// "byte buffer\n", buf->bpl, audio_ch->cmds_start, cx_read(audio_ch->cmds_start + 12)>>1,
// chip->num_periods, buf->bpl * chip->num_periods);
/* Enables corresponding bits at AUD_INT_STAT */
cx_write(AUD_A_INT_MSK, FLD_AUD_DST_RISCI1 | FLD_AUD_DST_OF | FLD_AUD_DST_SYNC | FLD_AUD_DST_OPC_ERR );
/* Clean any pending interrupt bits already set */
cx_write(AUD_A_INT_STAT, ~0);
/* enable audio irqs */
cx_set(PCI_INT_MSK, chip->dev->pci_irqmask | PCI_MSK_AUD_INT);
// Turn on audio downstream fifo and risc enable 0x101
tmp = cx_read(AUD_INT_DMA_CTL);
cx_set(AUD_INT_DMA_CTL, tmp | (FLD_AUD_DST_A_RISC_EN | FLD_AUD_DST_A_FIFO_EN) );
mdelay(100);
return 0;
}
/*
* BOARD Specific: Resets audio DMA
*/
static int _cx25821_stop_audio_dma(snd_cx25821_card_t *chip)
{
struct cx25821_dev *dev = chip->dev;
/* stop dma */
cx_clear(AUD_INT_DMA_CTL, FLD_AUD_DST_A_RISC_EN | FLD_AUD_DST_A_FIFO_EN );
/* disable irqs */
cx_clear(PCI_INT_MSK, PCI_MSK_AUD_INT);
cx_clear(AUD_A_INT_MSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC | AUD_INT_DN_RISCI2 | AUD_INT_DN_RISCI1);
return 0;
}
#define MAX_IRQ_LOOP 50
/*
* BOARD Specific: IRQ dma bits
*/
static char *cx25821_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 cx25821_aud_irq(snd_cx25821_card_t *chip, u32 status, u32 mask)
{
struct cx25821_dev *dev = chip->dev;
if (0 == (status & mask))
{
return;
}
cx_write(AUD_A_INT_STAT, status);
if (debug > 1 || (status & mask & ~0xff))
cx25821_print_irqbits(dev->name, "irq aud",
cx25821_aud_irqs, ARRAY_SIZE(cx25821_aud_irqs),
status, mask);
/* risc op code error */
if (status & AUD_INT_OPC_ERR) {
printk(KERN_WARNING "WARNING %s/1: Audio risc op code error\n",dev->name);
cx_clear(AUD_INT_DMA_CTL, FLD_AUD_DST_A_RISC_EN | FLD_AUD_DST_A_FIFO_EN );
cx25821_sram_channel_dump_audio(dev, &cx25821_sram_channels[AUDIO_SRAM_CHANNEL]);
}
if (status & AUD_INT_DN_SYNC) {
printk(KERN_WARNING "WARNING %s: Downstream sync error!\n",dev->name);
cx_write(AUD_A_GPCNT_CTL, GP_COUNT_CONTROL_RESET);
return;
}
/* risc1 downstream */
if (status & AUD_INT_DN_RISCI1) {
atomic_set(&chip->count, cx_read(AUD_A_GPCNT));
snd_pcm_period_elapsed(chip->substream);
}
}
/*
* BOARD Specific: Handles IRQ calls
*/
static irqreturn_t cx25821_irq(int irq, void *dev_id)
{
snd_cx25821_card_t *chip = dev_id;
struct cx25821_dev *dev = chip->dev;
u32 status, pci_status;
u32 audint_status, audint_mask;
int loop, handled = 0;
int audint_count = 0;
audint_status = cx_read(AUD_A_INT_STAT);
audint_mask = cx_read(AUD_A_INT_MSK);
audint_count = cx_read(AUD_A_GPCNT);
status = cx_read(PCI_INT_STAT);
for (loop = 0; loop < 1; loop++)
{
status = cx_read(PCI_INT_STAT);
if (0 == status)
{
status = cx_read(PCI_INT_STAT);
audint_status = cx_read(AUD_A_INT_STAT);
audint_mask = cx_read(AUD_A_INT_MSK);
if (status)
{
handled = 1;
cx_write(PCI_INT_STAT, status);
cx25821_aud_irq(chip, audint_status, audint_mask);
break;
}
else
goto out;
}
handled = 1;
cx_write(PCI_INT_STAT, status);
cx25821_aud_irq(chip, audint_status, audint_mask);
}
pci_status = cx_read(PCI_INT_STAT);
if (handled)
cx_write(PCI_INT_STAT, pci_status);
out:
return IRQ_RETVAL(handled);
}
static int dsp_buffer_free(snd_cx25821_card_t *chip)
{
BUG_ON(!chip->dma_size);
dprintk(2,"Freeing buffer\n");
videobuf_sg_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 384
static struct snd_pcm_hardware snd_cx25821_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/3,
.period_bytes_max = DEFAULT_FIFO_SIZE/3,
.periods_min = 1,
.periods_max = AUDIO_LINE_SIZE,
.buffer_bytes_max = (AUDIO_LINE_SIZE*AUDIO_LINE_SIZE), //128*128 = 16384 = 1024 * 16
};
/*
* audio pcm capture open callback
*/
static int snd_cx25821_pcm_open(struct snd_pcm_substream *substream)
{
snd_cx25821_card_t *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
unsigned int bpl = 0;
if (!chip) {
printk(KERN_ERR "DEBUG: cx25821 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_cx25821_digital_hw;
if (cx25821_sram_channels[AUDIO_SRAM_CHANNEL].fifo_size != DEFAULT_FIFO_SIZE)
{
bpl = cx25821_sram_channels[AUDIO_SRAM_CHANNEL].fifo_size / 3; //since there are 3 audio Clusters
bpl &= ~7; /* must be multiple of 8 */
if( bpl > AUDIO_LINE_SIZE )
{
bpl = AUDIO_LINE_SIZE;
}
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_cx25821_close(struct snd_pcm_substream *substream)
{
return 0;
}
/*
* hw_params callback
*/
static int snd_cx25821_hw_params(struct snd_pcm_substream * substream,
struct snd_pcm_hw_params * hw_params)
{
snd_cx25821_card_t *chip = snd_pcm_substream_chip(substream);
struct videobuf_dmabuf *dma;
struct cx25821_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 = videobuf_sg_alloc(sizeof(*buf));
if (NULL == buf)
return -ENOMEM;
if( chip->period_size > AUDIO_LINE_SIZE )
{
chip->period_size = AUDIO_LINE_SIZE;
}
buf->vb.memory = V4L2_MEMORY_MMAP;
buf->vb.field = V4L2_FIELD_NONE;
buf->vb.width = chip->period_size;
buf->bpl = chip->period_size;
buf->vb.height = chip->num_periods;
buf->vb.size = chip->dma_size;
dma = videobuf_to_dma(&buf->vb);
videobuf_dma_init(dma);
ret = videobuf_dma_init_kernel(dma, PCI_DMA_FROMDEVICE,
(PAGE_ALIGN(buf->vb.size) >> PAGE_SHIFT));
if (ret < 0)
goto error;
ret = videobuf_sg_dma_map(&chip->pci->dev, dma);
if (ret < 0)
goto error;
ret = cx25821_risc_databuffer_audio(chip->pci, &buf->risc, dma->sglist, buf->vb.width, buf->vb.height, 1);
if (ret < 0)
{
printk(KERN_INFO "DEBUG: ERROR after cx25821_risc_databuffer_audio() \n");
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);
buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
buf->vb.state = VIDEOBUF_PREPARED;
chip->buf = buf;
chip->dma_risc = dma;
substream->runtime->dma_area = chip->dma_risc->vmalloc;
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_cx25821_hw_free(struct snd_pcm_substream * substream)
{
snd_cx25821_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_cx25821_prepare(struct snd_pcm_substream *substream)
{
return 0;
}
/*
* trigger callback
*/
static int snd_cx25821_card_trigger(struct snd_pcm_substream *substream, int cmd)
{
snd_cx25821_card_t *chip = snd_pcm_substream_chip(substream);
int err = 0;
/* Local interrupts are already disabled by ALSA */
spin_lock(&chip->reg_lock);
switch (cmd)
{
case SNDRV_PCM_TRIGGER_START:
err = _cx25821_start_audio_dma(chip);
break;
case SNDRV_PCM_TRIGGER_STOP:
err = _cx25821_stop_audio_dma(chip);
break;
default:
err = -EINVAL;
break;
}
spin_unlock(&chip->reg_lock);
return err;
}
/*
* pointer callback
*/
static snd_pcm_uframes_t snd_cx25821_pointer(struct snd_pcm_substream *substream)
{
snd_cx25821_card_t *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
u16 count;
count = atomic_read(&chip->count);
return runtime->period_size * (count & (runtime->periods-1));
}
/*
* page callback (needed for mmap)
*/
static struct page *snd_cx25821_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_cx25821_pcm_ops = {
.open = snd_cx25821_pcm_open,
.close = snd_cx25821_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_cx25821_hw_params,
.hw_free = snd_cx25821_hw_free,
.prepare = snd_cx25821_prepare,
.trigger = snd_cx25821_card_trigger,
.pointer = snd_cx25821_pointer,
.page = snd_cx25821_page,
};
/*
* ALSA create a PCM device: Called when initializing the board. Sets up the name and hooks up
* the callbacks
*/
static int snd_cx25821_pcm(snd_cx25821_card_t *chip, int device, char *name)
{
struct snd_pcm *pcm;
int err;
err = snd_pcm_new(chip->card, name, device, 0, 1, &pcm);
if (err < 0)
{
printk(KERN_INFO "ERROR: FAILED snd_pcm_new() in %s\n", __func__);
return err;
}
pcm->private_data = chip;
pcm->info_flags = 0;
strcpy(pcm->name, name);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cx25821_pcm_ops);
return 0;
}
/****************************************************************************
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 struct pci_device_id cx25821_audio_pci_tbl[] __devinitdata = {
{0x14f1,0x0920,PCI_ANY_ID,PCI_ANY_ID,0,0,0},
{0, }
};
MODULE_DEVICE_TABLE(pci, cx25821_audio_pci_tbl);
/*
* Not used in the function snd_cx25821_dev_free so removing
* from the file.
*/
/*
static int snd_cx25821_free(snd_cx25821_card_t *chip)
{
if (chip->irq >= 0)
free_irq(chip->irq, chip);
cx25821_dev_unregister(chip->dev);
pci_disable_device(chip->pci);
return 0;
}
*/
/*
* Component Destructor
*/
static void snd_cx25821_dev_free(struct snd_card * card)
{
snd_cx25821_card_t *chip = card->private_data;
//snd_cx25821_free(chip);
snd_card_free(chip->card);
}
/*
* Alsa Constructor - Component probe
*/
static int cx25821_audio_initdev(struct cx25821_dev *dev)
{
struct snd_card *card;
snd_cx25821_card_t *chip;
int err;
if (devno >= SNDRV_CARDS)
{
printk(KERN_INFO "DEBUG ERROR: devno >= SNDRV_CARDS %s\n", __func__);
return (-ENODEV);
}
if (!enable[devno]) {
++devno;
printk(KERN_INFO "DEBUG ERROR: !enable[devno] %s\n", __func__);
return (-ENOENT);
}
card = snd_card_new(index[devno], id[devno], THIS_MODULE, sizeof(snd_cx25821_card_t));
if (!card)
{
printk(KERN_INFO "DEBUG ERROR: cannot create snd_card_new in %s\n", __func__);
return (-ENOMEM);
}
strcpy(card->driver, "cx25821");
/* Card "creation" */
card->private_free = snd_cx25821_dev_free;
chip = (snd_cx25821_card_t *) card->private_data;
spin_lock_init(&chip->reg_lock);
chip->dev = dev;
chip->card = card;
chip->pci = dev->pci;
chip->iobase = pci_resource_start(dev->pci, 0);
chip->irq = dev->pci->irq;
err = request_irq(dev->pci->irq, cx25821_irq,
IRQF_SHARED | IRQF_DISABLED, chip->dev->name, chip);
if (err < 0) {
printk(KERN_ERR "ERROR %s: can't get IRQ %d for ALSA\n", chip->dev->name, dev->pci->irq);
goto error;
}
if ((err = snd_cx25821_pcm(chip, 0, "cx25821 Digital")) < 0)
{
printk(KERN_INFO "DEBUG ERROR: cannot create snd_cx25821_pcm %s\n", __func__);
goto error;
}
snd_card_set_dev(card, &chip->pci->dev);
strcpy(card->shortname, "cx25821");
sprintf(card->longname, "%s at 0x%lx irq %d", chip->dev->name, chip->iobase, chip->irq);
strcpy (card->mixername, "CX25821");
printk(KERN_INFO "%s/%i: ALSA support for cx25821 boards\n", card->driver, devno);
err = snd_card_register(card);
if (err < 0)
{
printk(KERN_INFO "DEBUG ERROR: cannot register sound card %s\n", __func__);
goto error;
}
snd_cx25821_cards[devno] = card;
devno++;
return 0;
error:
snd_card_free(card);
return err;
}
/****************************************************************************
LINUX MODULE INIT
****************************************************************************/
static void cx25821_audio_fini(void)
{
snd_card_free(snd_cx25821_cards[0]);
}
/*
* Module initializer
*
* Loops through present saa7134 cards, and assigns an ALSA device
* to each one
*
*/
static int cx25821_alsa_init(void)
{
struct cx25821_dev *dev = NULL;
struct list_head *list;
list_for_each(list,&cx25821_devlist) {
dev = list_entry(list, struct cx25821_dev, devlist);
cx25821_audio_initdev(dev);
}
if (dev == NULL)
printk(KERN_INFO "cx25821 ERROR ALSA: no cx25821 cards found\n");
return 0;
}
late_initcall(cx25821_alsa_init);
module_exit(cx25821_audio_fini);
/* ----------------------------------------------------------- */
/*
* Local variables:
* c-basic-offset: 8
* End:
*/

View File

@ -0,0 +1,825 @@
/*
* Driver for the Conexant CX25821 PCIe bridge
*
* Copyright (C) 2009 Conexant Systems Inc.
* Authors <hiep.huynh@conexant.com>, <shu.lin@conexant.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "cx25821-video.h"
#include "cx25821-audio-upstream.h"
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/syscalls.h>
#include <linux/file.h>
#include <linux/fcntl.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards");
MODULE_AUTHOR("Hiep Huynh <hiep.huynh@conexant.com>");
MODULE_LICENSE("GPL");
static int _intr_msk = FLD_AUD_SRC_RISCI1 | FLD_AUD_SRC_OF | FLD_AUD_SRC_SYNC | FLD_AUD_SRC_OPC_ERR;
int cx25821_sram_channel_setup_upstream_audio(struct cx25821_dev *dev,
struct sram_channel *ch,
unsigned int bpl, u32 risc)
{
unsigned int i, lines;
u32 cdt;
if (ch->cmds_start == 0) {
cx_write(ch->ptr1_reg, 0);
cx_write(ch->ptr2_reg, 0);
cx_write(ch->cnt2_reg, 0);
cx_write(ch->cnt1_reg, 0);
return 0;
}
bpl = (bpl + 7) & ~7; /* alignment */
cdt = ch->cdt;
lines = ch->fifo_size / bpl;
if (lines > 3)
{
lines = 3;
}
BUG_ON(lines < 2);
/* write CDT */
for (i = 0; i < lines; i++) {
cx_write(cdt + 16*i, ch->fifo_start + bpl*i);
cx_write(cdt + 16*i + 4, 0);
cx_write(cdt + 16*i + 8, 0);
cx_write(cdt + 16*i + 12, 0);
}
/* write CMDS */
cx_write(ch->cmds_start + 0, risc);
cx_write(ch->cmds_start + 4, 0);
cx_write(ch->cmds_start + 8, cdt);
cx_write(ch->cmds_start + 12, AUDIO_CDT_SIZE_QW);
cx_write(ch->cmds_start + 16, ch->ctrl_start);
//IQ size
cx_write(ch->cmds_start + 20, AUDIO_IQ_SIZE_DW);
for (i = 24; i < 80; i += 4)
cx_write(ch->cmds_start + i, 0);
/* fill registers */
cx_write(ch->ptr1_reg, ch->fifo_start);
cx_write(ch->ptr2_reg, cdt);
cx_write(ch->cnt2_reg, AUDIO_CDT_SIZE_QW);
cx_write(ch->cnt1_reg, AUDIO_CLUSTER_SIZE_QW - 1);
return 0;
}
static __le32 *cx25821_risc_field_upstream_audio( struct cx25821_dev *dev, __le32 *rp,
dma_addr_t databuf_phys_addr,
unsigned int bpl, int fifo_enable)
{
unsigned int line;
struct sram_channel *sram_ch = &dev->sram_channels[dev->_audio_upstream_channel_select];
int offset = 0;
/* scan lines */
for (line = 0; line < LINES_PER_AUDIO_BUFFER; line++)
{
*(rp++) = cpu_to_le32(RISC_READ|RISC_SOL|RISC_EOL|bpl);
*(rp++) = cpu_to_le32(databuf_phys_addr + offset);
*(rp++) = cpu_to_le32(0); /* bits 63-32 */
// Check if we need to enable the FIFO after the first 3 lines
// For the upstream audio channel, the risc engine will enable the FIFO.
if ( fifo_enable && line == 2 )
{
*(rp++) = RISC_WRITECR;
*(rp++) = sram_ch->dma_ctl;
*(rp++) = sram_ch->fld_aud_fifo_en;
*(rp++) = 0x00000020;
}
offset += AUDIO_LINE_SIZE;
}
return rp;
}
int cx25821_risc_buffer_upstream_audio( struct cx25821_dev *dev,
struct pci_dev *pci,
unsigned int bpl, unsigned int lines)
{
__le32 *rp;
int fifo_enable = 0;
int frame = 0, i = 0;
int frame_size = AUDIO_DATA_BUF_SZ;
int databuf_offset = 0;
int risc_flag = RISC_CNT_INC;
dma_addr_t risc_phys_jump_addr;
/* Virtual address of Risc buffer program */
rp = dev->_risc_virt_addr;
/* sync instruction */
*(rp++) = cpu_to_le32(RISC_RESYNC | AUDIO_SYNC_LINE);
for( frame = 0; frame < NUM_AUDIO_FRAMES; frame++ )
{
databuf_offset = frame_size * frame;
if( frame == 0 )
{
fifo_enable = 1;
risc_flag = RISC_CNT_RESET;
}
else
{
fifo_enable = 0;
risc_flag = RISC_CNT_INC;
}
//Calculate physical jump address
if( (frame+1) == NUM_AUDIO_FRAMES )
{
risc_phys_jump_addr = dev->_risc_phys_start_addr + RISC_SYNC_INSTRUCTION_SIZE;
}
else
{
risc_phys_jump_addr = dev->_risc_phys_start_addr + RISC_SYNC_INSTRUCTION_SIZE + AUDIO_RISC_DMA_BUF_SIZE*(frame+1);
}
rp = cx25821_risc_field_upstream_audio(dev, rp, dev->_audiodata_buf_phys_addr+databuf_offset, bpl, fifo_enable);
if( USE_RISC_NOOP_AUDIO )
{
for( i = 0; i < NUM_NO_OPS; i++ )
{
*(rp++) = cpu_to_le32(RISC_NOOP);
}
}
// Loop to (Nth)FrameRISC or to Start of Risc program & generate IRQ
*(rp++) = cpu_to_le32(RISC_JUMP|RISC_IRQ1|risc_flag);
*(rp++) = cpu_to_le32(risc_phys_jump_addr);
*(rp++) = cpu_to_le32(0);
//Recalculate virtual address based on frame index
rp = dev->_risc_virt_addr + RISC_SYNC_INSTRUCTION_SIZE/4 + (AUDIO_RISC_DMA_BUF_SIZE*(frame+1)/4 ) ;
}
return 0;
}
void cx25821_free_memory_audio(struct cx25821_dev *dev)
{
if (dev->_risc_virt_addr)
{
pci_free_consistent(dev->pci, dev->_audiorisc_size, dev->_risc_virt_addr, dev->_risc_phys_addr);
dev->_risc_virt_addr = NULL;
}
if (dev->_audiodata_buf_virt_addr)
{
pci_free_consistent(dev->pci, dev->_audiodata_buf_size, dev->_audiodata_buf_virt_addr, dev->_audiodata_buf_phys_addr);
dev->_audiodata_buf_virt_addr = NULL;
}
}
void cx25821_stop_upstream_audio(struct cx25821_dev *dev)
{
struct sram_channel *sram_ch = &dev->sram_channels[AUDIO_UPSTREAM_SRAM_CHANNEL_B];
u32 tmp = 0;
if( !dev->_audio_is_running )
{
printk("cx25821: No audio file is currently running so return!\n");
return;
}
//Disable RISC interrupts
cx_write( sram_ch->int_msk, 0 );
//Turn OFF risc and fifo enable in AUD_DMA_CNTRL
tmp = cx_read( sram_ch->dma_ctl );
cx_write( sram_ch->dma_ctl, tmp & ~(sram_ch->fld_aud_fifo_en | sram_ch->fld_aud_risc_en) );
//Clear data buffer memory
if( dev->_audiodata_buf_virt_addr )
memset( dev->_audiodata_buf_virt_addr, 0, dev->_audiodata_buf_size );
dev->_audio_is_running = 0;
dev->_is_first_audio_frame = 0;
dev->_audioframe_count = 0;
dev->_audiofile_status = END_OF_FILE;
if( dev->_irq_audio_queues )
{
kfree(dev->_irq_audio_queues);
dev->_irq_audio_queues = NULL;
}
if( dev->_audiofilename != NULL )
kfree(dev->_audiofilename);
}
void cx25821_free_mem_upstream_audio(struct cx25821_dev *dev)
{
if( dev->_audio_is_running )
{
cx25821_stop_upstream_audio(dev);
}
cx25821_free_memory_audio(dev);
}
int cx25821_get_audio_data(struct cx25821_dev *dev, struct sram_channel *sram_ch )
{
struct file * myfile;
int frame_index_temp = dev->_audioframe_index;
int i = 0;
int line_size = AUDIO_LINE_SIZE;
int frame_size = AUDIO_DATA_BUF_SZ;
int frame_offset = frame_size * frame_index_temp;
ssize_t vfs_read_retval = 0;
char mybuf[line_size];
loff_t file_offset = dev->_audioframe_count * frame_size;
loff_t pos;
mm_segment_t old_fs;
if( dev->_audiofile_status == END_OF_FILE )
return 0;
myfile = filp_open( dev->_audiofilename, O_RDONLY | O_LARGEFILE, 0 );
if (IS_ERR(myfile))
{
const int open_errno = -PTR_ERR(myfile);
printk("%s(): ERROR opening file(%s) with errno = %d! \n", __func__, dev->_audiofilename, open_errno);
return PTR_ERR(myfile);
}
else
{
if( !(myfile->f_op) )
{
printk("%s: File has no file operations registered!\n", __func__);
filp_close(myfile, NULL);
return -EIO;
}
if( !myfile->f_op->read )
{
printk("%s: File has no READ operations registered! \n", __func__);
filp_close(myfile, NULL);
return -EIO;
}
pos = myfile->f_pos;
old_fs = get_fs();
set_fs(KERNEL_DS);
for( i = 0; i < dev->_audio_lines_count; i++ )
{
pos = file_offset;
vfs_read_retval = vfs_read(myfile, mybuf, line_size, &pos);
if( vfs_read_retval > 0 && vfs_read_retval == line_size && dev->_audiodata_buf_virt_addr != NULL )
{
memcpy( (void*)(dev->_audiodata_buf_virt_addr+frame_offset/4), mybuf, vfs_read_retval);
}
file_offset += vfs_read_retval;
frame_offset += vfs_read_retval;
if( vfs_read_retval < line_size )
{
printk(KERN_INFO "Done: exit %s() since no more bytes to read from Audio file.\n", __func__ );
break;
}
}
if( i > 0 )
dev->_audioframe_count++;
dev->_audiofile_status = (vfs_read_retval == line_size) ? IN_PROGRESS : END_OF_FILE;
set_fs(old_fs);
filp_close(myfile, NULL);
}
return 0;
}
static void cx25821_audioups_handler(struct work_struct *work)
{
struct cx25821_dev *dev = container_of(work, struct cx25821_dev, _audio_work_entry);
if( !dev )
{
printk("ERROR %s(): since container_of(work_struct) FAILED! \n", __func__ );
return;
}
cx25821_get_audio_data( dev, &dev->sram_channels[dev->_audio_upstream_channel_select] );
}
int cx25821_openfile_audio(struct cx25821_dev *dev, struct sram_channel *sram_ch)
{
struct file * myfile;
int i = 0, j = 0;
int line_size = AUDIO_LINE_SIZE;
ssize_t vfs_read_retval = 0;
char mybuf[line_size];
loff_t pos;
loff_t offset = (unsigned long)0;
mm_segment_t old_fs;
myfile = filp_open( dev->_audiofilename, O_RDONLY | O_LARGEFILE, 0 );
if (IS_ERR(myfile))
{
const int open_errno = -PTR_ERR(myfile);
printk("%s(): ERROR opening file(%s) with errno = %d! \n", __func__, dev->_audiofilename, open_errno);
return PTR_ERR(myfile);
}
else
{
if( !(myfile->f_op) )
{
printk("%s: File has no file operations registered! \n", __func__);
filp_close(myfile, NULL);
return -EIO;
}
if( !myfile->f_op->read )
{
printk("%s: File has no READ operations registered! \n", __func__);
filp_close(myfile, NULL);
return -EIO;
}
pos = myfile->f_pos;
old_fs = get_fs();
set_fs(KERNEL_DS);
for( j = 0; j < NUM_AUDIO_FRAMES; j++ )
{
for( i = 0; i < dev->_audio_lines_count; i++ )
{
pos = offset;
vfs_read_retval = vfs_read(myfile, mybuf, line_size, &pos);
if( vfs_read_retval > 0 && vfs_read_retval == line_size && dev->_audiodata_buf_virt_addr != NULL )
{
memcpy( (void*)(dev->_audiodata_buf_virt_addr+offset/4), mybuf, vfs_read_retval);
}
offset += vfs_read_retval;
if( vfs_read_retval < line_size )
{
printk(KERN_INFO "Done: exit %s() since no more bytes to read from Audio file.\n", __func__ );
break;
}
}
if( i > 0 )
{
dev->_audioframe_count++;
}
if( vfs_read_retval < line_size )
{
break;
}
}
dev->_audiofile_status = (vfs_read_retval == line_size) ? IN_PROGRESS : END_OF_FILE;
set_fs(old_fs);
myfile->f_pos = 0;
filp_close(myfile, NULL);
}
return 0;
}
static int cx25821_audio_upstream_buffer_prepare(struct cx25821_dev *dev,
struct sram_channel *sram_ch,
int bpl)
{
int ret = 0;
dma_addr_t dma_addr;
dma_addr_t data_dma_addr;
cx25821_free_memory_audio(dev);
dev->_risc_virt_addr = pci_alloc_consistent(dev->pci, dev->audio_upstream_riscbuf_size, &dma_addr);
dev->_risc_virt_start_addr = dev->_risc_virt_addr;
dev->_risc_phys_start_addr = dma_addr;
dev->_risc_phys_addr = dma_addr;
dev->_audiorisc_size = dev->audio_upstream_riscbuf_size;
if (!dev->_risc_virt_addr)
{
printk("cx25821 ERROR: pci_alloc_consistent() FAILED to allocate memory for RISC program! Returning.\n");
return -ENOMEM;
}
//Clear out memory at address
memset( dev->_risc_virt_addr, 0, dev->_audiorisc_size );
//For Audio Data buffer allocation
dev->_audiodata_buf_virt_addr = pci_alloc_consistent(dev->pci, dev->audio_upstream_databuf_size, &data_dma_addr);
dev->_audiodata_buf_phys_addr = data_dma_addr;
dev->_audiodata_buf_size = dev->audio_upstream_databuf_size;
if (!dev->_audiodata_buf_virt_addr)
{
printk("cx25821 ERROR: pci_alloc_consistent() FAILED to allocate memory for data buffer! Returning. \n");
return -ENOMEM;
}
//Clear out memory at address
memset( dev->_audiodata_buf_virt_addr, 0, dev->_audiodata_buf_size );
ret = cx25821_openfile_audio(dev, sram_ch);
if( ret < 0 )
return ret;
//Creating RISC programs
ret = cx25821_risc_buffer_upstream_audio(dev, dev->pci, bpl, dev->_audio_lines_count );
if (ret < 0)
{
printk(KERN_DEBUG "cx25821 ERROR creating audio upstream RISC programs! \n");
goto error;
}
return 0;
error:
return ret;
}
int cx25821_audio_upstream_irq(struct cx25821_dev *dev, int chan_num, u32 status)
{
int i = 0;
u32 int_msk_tmp;
struct sram_channel *channel = &dev->sram_channels[chan_num];
dma_addr_t risc_phys_jump_addr;
__le32 * rp;
if (status & FLD_AUD_SRC_RISCI1)
{
//Get interrupt_index of the program that interrupted
u32 prog_cnt = cx_read( channel->gpcnt );
//Since we've identified our IRQ, clear our bits from the interrupt mask and interrupt status registers
cx_write(channel->int_msk, 0);
cx_write(channel->int_stat, cx_read(channel->int_stat) );
spin_lock(&dev->slock);
while(prog_cnt != dev->_last_index_irq)
{
//Update _last_index_irq
if(dev->_last_index_irq < (NUMBER_OF_PROGRAMS-1))
{
dev->_last_index_irq++;
}
else
{
dev->_last_index_irq = 0;
}
dev->_audioframe_index = dev->_last_index_irq;
queue_work(dev->_irq_audio_queues, &dev->_audio_work_entry);
}
if ( dev->_is_first_audio_frame )
{
dev->_is_first_audio_frame = 0;
if( dev->_risc_virt_start_addr != NULL )
{
risc_phys_jump_addr = dev->_risc_phys_start_addr + RISC_SYNC_INSTRUCTION_SIZE + AUDIO_RISC_DMA_BUF_SIZE;
rp = cx25821_risc_field_upstream_audio(dev, dev->_risc_virt_start_addr+1, dev->_audiodata_buf_phys_addr, AUDIO_LINE_SIZE, FIFO_DISABLE);
if( USE_RISC_NOOP_AUDIO )
{
for( i = 0; i < NUM_NO_OPS; i++ )
{
*(rp++) = cpu_to_le32(RISC_NOOP);
}
}
// Jump to 2nd Audio Frame
*(rp++) = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_RESET);
*(rp++) = cpu_to_le32(risc_phys_jump_addr);
*(rp++) = cpu_to_le32(0);
}
}
spin_unlock(&dev->slock);
}
else
{
if(status & FLD_AUD_SRC_OF)
printk("%s: Audio Received Overflow Error Interrupt!\n", __func__);
if(status & FLD_AUD_SRC_SYNC)
printk("%s: Audio Received Sync Error Interrupt!\n", __func__);
if(status & FLD_AUD_SRC_OPC_ERR)
printk("%s: Audio Received OpCode Error Interrupt!\n", __func__);
// Read and write back the interrupt status register to clear our bits
cx_write(channel->int_stat, cx_read(channel->int_stat) );
}
if( dev->_audiofile_status == END_OF_FILE )
{
printk("cx25821: EOF Channel Audio Framecount = %d\n", dev->_audioframe_count );
return -1;
}
//ElSE, set the interrupt mask register, re-enable irq.
int_msk_tmp = cx_read( channel->int_msk );
cx_write( channel->int_msk, int_msk_tmp |= _intr_msk );
return 0;
}
static irqreturn_t cx25821_upstream_irq_audio(int irq, void *dev_id)
{
struct cx25821_dev *dev = dev_id;
u32 msk_stat, audio_status;
int handled = 0;
struct sram_channel *sram_ch;
if( !dev )
return -1;
sram_ch = &dev->sram_channels[dev->_audio_upstream_channel_select];
msk_stat = cx_read(sram_ch->int_mstat);
audio_status = cx_read(sram_ch->int_stat);
// Only deal with our interrupt
if(audio_status)
{
handled = cx25821_audio_upstream_irq(dev, dev->_audio_upstream_channel_select, audio_status);
}
if( handled < 0 )
{
cx25821_stop_upstream_audio(dev);
}
else
{
handled += handled;
}
return IRQ_RETVAL(handled);
}
static void cx25821_wait_fifo_enable(struct cx25821_dev *dev, struct sram_channel *sram_ch)
{
int count = 0;
u32 tmp;
do
{
//Wait 10 microsecond before checking to see if the FIFO is turned ON.
udelay(10);
tmp = cx_read( sram_ch->dma_ctl );
if(count++ > 1000) //10 millisecond timeout
{
printk("cx25821 ERROR: %s() fifo is NOT turned on. Timeout!\n", __func__);
return;
}
} while( !(tmp & sram_ch->fld_aud_fifo_en) );
}
int cx25821_start_audio_dma_upstream(struct cx25821_dev *dev,
struct sram_channel *sram_ch)
{
u32 tmp = 0;
int err = 0;
// Set the physical start address of the RISC program in the initial program counter(IPC) member of the CMDS.
cx_write(sram_ch->cmds_start + 0, dev->_risc_phys_addr);
cx_write(sram_ch->cmds_start + 4, 0); /* Risc IPC High 64 bits 63-32 */
/* reset counter */
cx_write(sram_ch->gpcnt_ctl, 3);
//Set the line length (It looks like we do not need to set the line length)
cx_write(sram_ch->aud_length, AUDIO_LINE_SIZE & FLD_AUD_DST_LN_LNGTH);
//Set the input mode to 16-bit
tmp = cx_read( sram_ch->aud_cfg );
tmp |= FLD_AUD_SRC_ENABLE | FLD_AUD_DST_PK_MODE | FLD_AUD_CLK_ENABLE | FLD_AUD_MASTER_MODE | FLD_AUD_CLK_SELECT_PLL_D | FLD_AUD_SONY_MODE;
cx_write( sram_ch->aud_cfg, tmp );
// Read and write back the interrupt status register to clear it
tmp = cx_read( sram_ch->int_stat);
cx_write( sram_ch->int_stat, tmp);
// Clear our bits from the interrupt status register.
cx_write( sram_ch->int_stat, _intr_msk );
//Set the interrupt mask register, enable irq.
cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << sram_ch->irq_bit));
tmp = cx_read( sram_ch->int_msk );
cx_write( sram_ch->int_msk, tmp |= _intr_msk );
err = request_irq(dev->pci->irq, cx25821_upstream_irq_audio, IRQF_SHARED | IRQF_DISABLED, dev->name, dev);
if (err < 0)
{
printk(KERN_ERR "%s: can't get upstream IRQ %d\n", dev->name, dev->pci->irq);
goto fail_irq;
}
// Start the DMA engine
tmp = cx_read( sram_ch->dma_ctl );
cx_set( sram_ch->dma_ctl, tmp | sram_ch->fld_aud_risc_en );
dev->_audio_is_running = 1;
dev->_is_first_audio_frame = 1;
// The fifo_en bit turns on by the first Risc program
cx25821_wait_fifo_enable(dev, sram_ch);
return 0;
fail_irq:
cx25821_dev_unregister(dev);
return err;
}
int cx25821_audio_upstream_init(struct cx25821_dev *dev, int channel_select)
{
struct sram_channel *sram_ch;
int retval = 0;
int err = 0;
int str_length = 0;
if( dev->_audio_is_running )
{
printk("Audio Channel is still running so return!\n");
return 0;
}
dev->_audio_upstream_channel_select = channel_select;
sram_ch = &dev->sram_channels[channel_select];
//Work queue
INIT_WORK(&dev->_audio_work_entry, cx25821_audioups_handler);
dev->_irq_audio_queues = create_singlethread_workqueue("cx25821_audioworkqueue");
if(!dev->_irq_audio_queues)
{
printk("cx25821 ERROR: create_singlethread_workqueue() for Audio FAILED!\n");
return -ENOMEM;
}
dev->_last_index_irq = 0;
dev->_audio_is_running = 0;
dev->_audioframe_count = 0;
dev->_audiofile_status = RESET_STATUS;
dev->_audio_lines_count = LINES_PER_AUDIO_BUFFER;
_line_size = AUDIO_LINE_SIZE;
if( dev->input_audiofilename )
{
str_length = strlen(dev->input_audiofilename);
dev->_audiofilename = (char *) kmalloc(str_length + 1, GFP_KERNEL);
if( !dev->_audiofilename )
goto error;
memcpy(dev->_audiofilename, dev->input_audiofilename, str_length + 1);
//Default if filename is empty string
if( strcmp(dev->input_audiofilename,"") == 0)
{
dev->_audiofilename = "/root/audioGOOD.wav";
}
}
else
{
str_length = strlen(_defaultAudioName);
dev->_audiofilename = (char *) kmalloc(str_length + 1, GFP_KERNEL);
if( !dev->_audiofilename )
goto error;
memcpy(dev->_audiofilename, _defaultAudioName, str_length + 1);
}
retval = cx25821_sram_channel_setup_upstream_audio(dev, sram_ch, _line_size, 0);
dev->audio_upstream_riscbuf_size = AUDIO_RISC_DMA_BUF_SIZE * NUM_AUDIO_PROGS + RISC_SYNC_INSTRUCTION_SIZE;
dev->audio_upstream_databuf_size = AUDIO_DATA_BUF_SZ * NUM_AUDIO_PROGS;
//Allocating buffers and prepare RISC program
retval = cx25821_audio_upstream_buffer_prepare(dev, sram_ch, _line_size);
if (retval < 0)
{
printk(KERN_ERR "%s: Failed to set up Audio upstream buffers!\n", dev->name);
goto error;
}
//Start RISC engine
cx25821_start_audio_dma_upstream(dev, sram_ch);
return 0;
error:
cx25821_dev_unregister(dev);
return err;
}

View File

@ -0,0 +1,62 @@
/*
* Driver for the Conexant CX25821 PCIe bridge
*
* Copyright (C) 2009 Conexant Systems Inc.
* Authors <hiep.huynh@conexant.com>, <shu.lin@conexant.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/mutex.h>
#include <linux/workqueue.h>
#define NUM_AUDIO_PROGS 8
#define NUM_AUDIO_FRAMES 8
#define END_OF_FILE 0
#define IN_PROGRESS 1
#define RESET_STATUS -1
#define FIFO_DISABLE 0
#define FIFO_ENABLE 1
#define NUM_NO_OPS 4
#define RISC_READ_INSTRUCTION_SIZE 12
#define RISC_JUMP_INSTRUCTION_SIZE 12
#define RISC_WRITECR_INSTRUCTION_SIZE 16
#define RISC_SYNC_INSTRUCTION_SIZE 4
#define DWORD_SIZE 4
#define AUDIO_SYNC_LINE 4
#define LINES_PER_AUDIO_BUFFER 15
#define AUDIO_LINE_SIZE 128
#define AUDIO_DATA_BUF_SZ (AUDIO_LINE_SIZE * LINES_PER_AUDIO_BUFFER)
#define USE_RISC_NOOP_AUDIO 1
#ifdef USE_RISC_NOOP_AUDIO
#define AUDIO_RISC_DMA_BUF_SIZE ( LINES_PER_AUDIO_BUFFER*RISC_READ_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + NUM_NO_OPS*DWORD_SIZE + RISC_JUMP_INSTRUCTION_SIZE)
#endif
#ifndef USE_RISC_NOOP_AUDIO
#define AUDIO_RISC_DMA_BUF_SIZE ( LINES_PER_AUDIO_BUFFER*RISC_READ_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + RISC_JUMP_INSTRUCTION_SIZE)
#endif
static int _line_size;
char * _defaultAudioName = "/root/audioGOOD.wav";

View File

@ -0,0 +1,60 @@
/*
* Driver for the Conexant CX25821 PCIe bridge
*
* Copyright (C) 2009 Conexant Systems Inc.
* Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __CX25821_AUDIO_H__
#define __CX25821_AUDIO_H__
#define USE_RISC_NOOP 1
#define LINES_PER_BUFFER 15
#define AUDIO_LINE_SIZE 128
//Number of buffer programs to use at once.
#define NUMBER_OF_PROGRAMS 8
//Max size of the RISC program for a buffer. - worst case is 2 writes per line
// Space is also added for the 4 no-op instructions added on the end.
#ifndef USE_RISC_NOOP
#define MAX_BUFFER_PROGRAM_SIZE \
(2*LINES_PER_BUFFER*RISC_WRITE_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE*4)
#endif
// MAE 12 July 2005 Try to use NOOP RISC instruction instead
#ifdef USE_RISC_NOOP
#define MAX_BUFFER_PROGRAM_SIZE \
(2*LINES_PER_BUFFER*RISC_WRITE_INSTRUCTION_SIZE + RISC_NOOP_INSTRUCTION_SIZE*4)
#endif
//Sizes of various instructions in bytes. Used when adding instructions.
#define RISC_WRITE_INSTRUCTION_SIZE 12
#define RISC_JUMP_INSTRUCTION_SIZE 12
#define RISC_SKIP_INSTRUCTION_SIZE 4
#define RISC_SYNC_INSTRUCTION_SIZE 4
#define RISC_WRITECR_INSTRUCTION_SIZE 16
#define RISC_NOOP_INSTRUCTION_SIZE 4
#define MAX_AUDIO_DMA_BUFFER_SIZE (MAX_BUFFER_PROGRAM_SIZE * NUMBER_OF_PROGRAMS + RISC_SYNC_INSTRUCTION_SIZE)
#endif

View File

@ -0,0 +1,441 @@
/*
* Driver for the Conexant CX25821 PCIe bridge
*
* Copyright (C) 2009 Conexant Systems Inc.
* Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
* Based on Steven Toth <stoth@linuxtv.org> cx23885 driver
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "cx25821-video.h"
static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
{
struct cx25821_buffer *buf = container_of(vb, struct cx25821_buffer, vb);
struct cx25821_buffer *prev;
struct cx25821_fh *fh = vq->priv_data;
struct cx25821_dev *dev = fh->dev;
struct cx25821_dmaqueue *q = &dev->vidq[SRAM_CH11];
/* 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);
buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]);
if (!list_empty(&q->queued)) {
list_add_tail(&buf->vb.queue, &q->queued);
buf->vb.state = VIDEOBUF_QUEUED;
dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, buf->vb.i);
} else if (list_empty(&q->active)) {
list_add_tail(&buf->vb.queue, &q->active);
cx25821_start_video_dma(dev, q, buf, &dev->sram_channels[SRAM_CH11]);
buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
dprintk(2, "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n",
buf, buf->vb. i, buf->count, q->count);
} else {
prev = list_entry(q->active.prev, struct cx25821_buffer, vb.queue);
if (prev->vb.width == buf->vb.width &&
prev->vb.height == buf->vb.height &&
prev->fmt == buf->fmt) {
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);
/* 64 bit bits 63-32 */
prev->risc.jmp[2] = cpu_to_le32(0);
dprintk(2, "[%p/%d] buffer_queue - append to active, buf->count=%d\n", buf, buf->vb.i, buf->count);
} else {
list_add_tail(&buf->vb.queue, &q->queued);
buf->vb.state = VIDEOBUF_QUEUED;
dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, buf->vb.i);
}
}
if (list_empty(&q->active))
{
dprintk(2, "active queue empty!\n");
}
}
static struct videobuf_queue_ops cx25821_video_qops = {
.buf_setup = buffer_setup,
.buf_prepare = buffer_prepare,
.buf_queue = buffer_queue,
.buf_release = buffer_release,
};
static int video_open(struct file *file)
{
int minor = video_devdata(file)->minor;
struct cx25821_dev *h, *dev = NULL;
struct cx25821_fh *fh;
struct list_head *list;
enum v4l2_buf_type type = 0;
lock_kernel();
list_for_each(list, &cx25821_devlist)
{
h = list_entry(list, struct cx25821_dev, devlist);
if (h->video_dev[SRAM_CH11] && h->video_dev[SRAM_CH11]->minor == minor)
{
dev = h;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
}
}
if (NULL == dev) {
unlock_kernel();
return -ENODEV;
}
printk("open minor=%d type=%s\n", minor, v4l2_type_names[type]);
/* allocate + initialize per filehandle data */
fh = kzalloc(sizeof(*fh), GFP_KERNEL);
if (NULL == fh) {
unlock_kernel();
return -ENOMEM;
}
file->private_data = fh;
fh->dev = dev;
fh->type = type;
fh->width = 720;
if(dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK)
fh->height = 576;
else
fh->height = 480;
dev->channel_opened = 10;
fh->fmt = format_by_fourcc(V4L2_PIX_FMT_YUYV);
v4l2_prio_open(&dev->prio,&fh->prio);
videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops,
&dev->pci->dev, &dev->slock,
V4L2_BUF_TYPE_VIDEO_CAPTURE,
V4L2_FIELD_INTERLACED,
sizeof(struct cx25821_buffer),
fh);
dprintk(1, "post videobuf_queue_init()\n");
unlock_kernel();
return 0;
}
static ssize_t video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
{
struct cx25821_fh *fh = file->private_data;
switch (fh->type)
{
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
if (res_locked(fh->dev, RESOURCE_VIDEO11))
return -EBUSY;
return videobuf_read_one(&fh->vidq, data, count, ppos, file->f_flags & O_NONBLOCK);
default:
BUG();
return 0;
}
}
static unsigned int video_poll(struct file *file, struct poll_table_struct *wait)
{
struct cx25821_fh *fh = file->private_data;
struct cx25821_buffer *buf;
if (res_check(fh, RESOURCE_VIDEO11)) {
/* streaming capture */
if (list_empty(&fh->vidq.stream))
return POLLERR;
buf = list_entry(fh->vidq.stream.next,
struct cx25821_buffer, vb.stream);
} else {
/* read() capture */
buf = (struct cx25821_buffer *)fh->vidq.read_buf;
if (NULL == buf)
return POLLERR;
}
poll_wait(file, &buf->vb.done, wait);
if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR)
return POLLIN|POLLRDNORM;
return 0;
}
static int video_release(struct file *file)
{
struct cx25821_fh *fh = file->private_data;
struct cx25821_dev *dev = fh->dev;
//stop the risc engine and fifo
//cx_write(channel11->dma_ctl, 0);
/* stop video capture */
if (res_check(fh, RESOURCE_VIDEO11)) {
videobuf_queue_cancel(&fh->vidq);
res_free(dev, fh, RESOURCE_VIDEO11);
}
if (fh->vidq.read_buf) {
buffer_release(&fh->vidq, fh->vidq.read_buf);
kfree(fh->vidq.read_buf);
}
videobuf_mmap_free(&fh->vidq);
v4l2_prio_close(&dev->prio,&fh->prio);
file->private_data = NULL;
kfree(fh);
return 0;
}
static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = fh->dev;
if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE))
{
return -EINVAL;
}
if (unlikely(i != fh->type))
{
return -EINVAL;
}
if (unlikely(!res_get(dev, fh, get_resource(fh, RESOURCE_VIDEO11))))
{
return -EBUSY;
}
return videobuf_streamon(get_queue(fh));
}
static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = fh->dev;
int err, res;
if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
if (i != fh->type)
return -EINVAL;
res = get_resource(fh, RESOURCE_VIDEO11);
err = videobuf_streamoff(get_queue(fh));
if (err < 0)
return err;
res_free(dev, fh, res);
return 0;
}
static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
int err;
if (fh)
{
err = v4l2_prio_check(&dev->prio, &fh->prio);
if (0 != err)
return err;
}
dprintk(2, "%s()\n", __func__);
err = vidioc_try_fmt_vid_cap(file, priv, f);
if (0 != err)
return err;
fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
fh->width = f->fmt.pix.width;
fh->height = f->fmt.pix.height;
fh->vidq.field = f->fmt.pix.field;
dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, fh->height, fh->vidq.field);
cx25821_call_all(dev, video, s_fmt, f);
return 0;
}
static long video_ioctl_upstream11(struct file *file, unsigned int cmd, unsigned long arg)
{
struct cx25821_fh *fh = file->private_data;
struct cx25821_dev *dev = fh->dev;
int command = 0;
struct upstream_user_struct *data_from_user;
data_from_user = (struct upstream_user_struct *)arg;
if( !data_from_user )
{
printk("cx25821 in %s(): Upstream data is INVALID. Returning.\n", __func__);
return 0;
}
command = data_from_user->command;
if( command != UPSTREAM_START_AUDIO && command != UPSTREAM_STOP_AUDIO )
{
return 0;
}
dev->input_filename = data_from_user->input_filename;
dev->input_audiofilename = data_from_user->input_filename;
dev->vid_stdname = data_from_user->vid_stdname;
dev->pixel_format = data_from_user->pixel_format;
dev->channel_select = data_from_user->channel_select;
dev->command = data_from_user->command;
switch(command)
{
case UPSTREAM_START_AUDIO:
cx25821_start_upstream_audio(dev, data_from_user);
break;
case UPSTREAM_STOP_AUDIO:
cx25821_stop_upstream_audio(dev);
break;
}
return 0;
}
static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
struct cx25821_fh *fh = priv;
return videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK);
}
static int vidioc_log_status (struct file *file, void *priv)
{
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
char name[32 + 2];
snprintf(name, sizeof(name), "%s/2", dev->name);
printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n",
dev->name);
cx25821_call_all(dev, core, log_status);
printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n",
dev->name);
return 0;
}
static int vidioc_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctl)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = fh->dev;
int err;
if (fh)
{
err = v4l2_prio_check(&dev->prio, &fh->prio);
if (0 != err)
return err;
}
return 0;
}
// exported stuff
static const struct v4l2_file_operations video_fops = {
.owner = THIS_MODULE,
.open = video_open,
.release = video_release,
.read = video_read,
.poll = video_poll,
.mmap = video_mmap,
.ioctl = video_ioctl_upstream11,
};
static const struct v4l2_ioctl_ops video_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
.vidioc_reqbufs = vidioc_reqbufs,
.vidioc_querybuf = vidioc_querybuf,
.vidioc_qbuf = vidioc_qbuf,
.vidioc_dqbuf = vidioc_dqbuf,
#ifdef TUNER_FLAG
.vidioc_s_std = vidioc_s_std,
.vidioc_querystd = vidioc_querystd,
#endif
.vidioc_cropcap = vidioc_cropcap,
.vidioc_s_crop = vidioc_s_crop,
.vidioc_g_crop = vidioc_g_crop,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
.vidioc_g_ctrl = vidioc_g_ctrl,
.vidioc_s_ctrl = vidioc_s_ctrl,
.vidioc_queryctrl = vidioc_queryctrl,
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
.vidioc_log_status = vidioc_log_status,
.vidioc_g_priority = vidioc_g_priority,
.vidioc_s_priority = vidioc_s_priority,
#ifdef CONFIG_VIDEO_V4L1_COMPAT
.vidiocgmbuf = vidiocgmbuf,
#endif
#ifdef TUNER_FLAG
.vidioc_g_tuner = vidioc_g_tuner,
.vidioc_s_tuner = vidioc_s_tuner,
.vidioc_g_frequency = vidioc_g_frequency,
.vidioc_s_frequency = vidioc_s_frequency,
#endif
#ifdef CONFIG_VIDEO_ADV_DEBUG
.vidioc_g_register = vidioc_g_register,
.vidioc_s_register = vidioc_s_register,
#endif
};
struct video_device cx25821_video_template11 = {
.name = "cx25821-audioupstream",
.fops = &video_fops,
.minor = -1,
.ioctl_ops = &video_ioctl_ops,
.tvnorms = CX25821_NORMS,
.current_norm = V4L2_STD_NTSC_M,
};

View File

@ -0,0 +1,45 @@
/*
* Driver for the Conexant CX25821 PCIe bridge
*
* Copyright (C) 2009 Conexant Systems Inc.
* Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _BITFUNCS_H
#define _BITFUNCS_H
#define SetBit(Bit) (1 << Bit)
inline u8 getBit(u32 sample, u8 index)
{
return (u8) ((sample >> index) & 1);
}
inline u32 clearBitAtPos(u32 value, u8 bit)
{
return value & ~(1 << bit);
}
inline u32 setBitAtPos(u32 sample, u8 bit)
{
sample |= (1 << bit);
return sample;
}
#endif

View File

@ -0,0 +1,72 @@
/*
* Driver for the Conexant CX25821 PCIe bridge
*
* Copyright (C) 2009 Conexant Systems Inc.
* Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
* Based on Steven Toth <stoth@linuxtv.org> cx23885 driver
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <media/cx25840.h>
#include "cx25821.h"
#include "tuner-xc2028.h"
// board config info
struct cx25821_board cx25821_boards[] = {
[UNKNOWN_BOARD] = {
.name = "UNKNOWN/GENERIC",
// Ensure safe default for unknown boards
.clk_freq = 0,
},
[CX25821_BOARD] = {
.name = "CX25821",
.portb = CX25821_RAW,
.portc = CX25821_264,
.input[0].type = CX25821_VMUX_COMPOSITE,
},
};
const unsigned int cx25821_bcount = ARRAY_SIZE(cx25821_boards);
struct cx25821_subid cx25821_subids[]={
{
.subvendor = 0x14f1,
.subdevice = 0x0920,
.card = CX25821_BOARD,
},
};
void cx25821_card_setup(struct cx25821_dev *dev)
{
static u8 eeprom[256];
if (dev->i2c_bus[0].i2c_rc == 0)
{
dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1;
tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom, sizeof(eeprom));
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,116 @@
/*
* Driver for the Conexant CX25821 PCIe bridge
*
* Copyright (C) 2009 Conexant Systems Inc.
* Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "cx25821.h"
/********************* GPIO stuffs *********************/
void cx25821_set_gpiopin_direction( struct cx25821_dev *dev,
int pin_number,
int pin_logic_value)
{
int bit = pin_number;
u32 gpio_oe_reg = GPIO_LO_OE;
u32 gpio_register = 0;
u32 value = 0;
// Check for valid pinNumber
if ( pin_number >= 47 )
return;
if ( pin_number > 31 )
{
bit = pin_number - 31;
gpio_oe_reg = GPIO_HI_OE;
}
// Here we will make sure that the GPIOs 0 and 1 are output. keep the rest as is
gpio_register = cx_read( gpio_oe_reg );
if (pin_logic_value == 1)
{
value = gpio_register | Set_GPIO_Bit(bit) ;
}
else
{
value = gpio_register & Clear_GPIO_Bit(bit) ;
}
cx_write( gpio_oe_reg, value );
}
static void cx25821_set_gpiopin_logicvalue( struct cx25821_dev *dev,
int pin_number,
int pin_logic_value)
{
int bit = pin_number;
u32 gpio_reg = GPIO_LO;
u32 value = 0;
// Check for valid pinNumber
if (pin_number >= 47)
return;
cx25821_set_gpiopin_direction(dev, pin_number, 0); // change to output direction
if ( pin_number > 31 )
{
bit = pin_number - 31;
gpio_reg = GPIO_HI;
}
value = cx_read( gpio_reg );
if (pin_logic_value == 0)
{
value &= Clear_GPIO_Bit(bit);
}
else
{
value |= Set_GPIO_Bit(bit);
}
cx_write( gpio_reg, value);
}
void cx25821_gpio_init(struct cx25821_dev *dev)
{
if( dev == NULL )
{
return;
}
switch (dev->board)
{
case CX25821_BOARD_CONEXANT_ATHENA10:
default:
//set GPIO 5 to select the path for Medusa/Athena
cx25821_set_gpiopin_logicvalue(dev, 5, 1);
mdelay(20);
break;
}
}

View File

@ -0,0 +1,3 @@
void cx25821_gpio_init(struct athena_dev *dev);

View File

@ -0,0 +1,437 @@
/*
* Driver for the Conexant CX25821 PCIe bridge
*
* Copyright (C) 2009 Conexant Systems Inc.
* Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
* Based on Steven Toth <stoth@linuxtv.org> cx23885 driver
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "cx25821.h"
#include <linux/i2c.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=0;
module_param(i2c_scan, int, 0444);
MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time");
#define dprintk(level, fmt, arg...)\
do { if (i2c_debug >= level)\
printk(KERN_DEBUG "%s/0: " fmt, dev->name, ## arg);\
} while (0)
#define I2C_WAIT_DELAY 32
#define I2C_WAIT_RETRY 64
#define I2C_EXTEND (1 << 3)
#define I2C_NOSTOP (1 << 4)
static inline int i2c_slave_did_ack(struct i2c_adapter *i2c_adap)
{
struct cx25821_i2c *bus = i2c_adap->algo_data;
struct cx25821_dev *dev = bus->dev;
return cx_read(bus->reg_stat) & 0x01;
}
static inline int i2c_is_busy(struct i2c_adapter *i2c_adap)
{
struct cx25821_i2c *bus = i2c_adap->algo_data;
struct cx25821_dev *dev = bus->dev;
return cx_read(bus->reg_stat) & 0x02 ? 1 : 0;
}
static int i2c_wait_done(struct i2c_adapter *i2c_adap)
{
int count;
for (count = 0; count < I2C_WAIT_RETRY; count++) {
if (!i2c_is_busy(i2c_adap))
break;
udelay(I2C_WAIT_DELAY);
}
if (I2C_WAIT_RETRY == count)
return 0;
return 1;
}
static int i2c_sendbytes(struct i2c_adapter *i2c_adap, const struct i2c_msg *msg, int joined_rlen)
{
struct cx25821_i2c *bus = i2c_adap->algo_data;
struct cx25821_dev *dev = bus->dev;
u32 wdata, addr, ctrl;
int retval, cnt;
if (joined_rlen)
dprintk(1, "%s(msg->wlen=%d, nextmsg->rlen=%d)\n", __func__, msg->len, joined_rlen);
else
dprintk(1, "%s(msg->len=%d)\n", __func__, msg->len);
/* Deal with i2c probe functions with zero payload */
if (msg->len == 0)
{
cx_write(bus->reg_addr, msg->addr << 25);
cx_write(bus->reg_ctrl, bus->i2c_period | (1 << 2));
if (!i2c_wait_done(i2c_adap))
return -EIO;
if (!i2c_slave_did_ack(i2c_adap))
return -EIO;
dprintk(1, "%s() returns 0\n", __func__);
return 0;
}
/* dev, reg + first byte */
addr = (msg->addr << 25) | msg->buf[0];
wdata = msg->buf[0];
ctrl = bus->i2c_period | (1 << 12) | (1 << 2);
if (msg->len > 1)
ctrl |= I2C_NOSTOP | I2C_EXTEND;
else if (joined_rlen)
ctrl |= I2C_NOSTOP;
cx_write(bus->reg_addr, addr);
cx_write(bus->reg_wdata, wdata);
cx_write(bus->reg_ctrl, ctrl);
retval = i2c_wait_done(i2c_adap);
if (retval < 0)
goto err;
if (retval == 0)
goto eio;
if (i2c_debug)
{
if (!(ctrl & I2C_NOSTOP))
printk(" >\n");
}
for (cnt = 1; cnt < msg->len; cnt++) {
/* following bytes */
wdata = msg->buf[cnt];
ctrl = bus->i2c_period | (1 << 12) | (1 << 2);
if (cnt < msg->len - 1)
ctrl |= I2C_NOSTOP | I2C_EXTEND;
else if (joined_rlen)
ctrl |= I2C_NOSTOP;
cx_write(bus->reg_addr, addr);
cx_write(bus->reg_wdata, wdata);
cx_write(bus->reg_ctrl, ctrl);
retval = i2c_wait_done(i2c_adap);
if (retval < 0)
goto err;
if (retval == 0)
goto eio;
if (i2c_debug)
{
dprintk(1, " %02x", msg->buf[cnt]);
if (!(ctrl & I2C_NOSTOP))
dprintk(1, " >\n");
}
}
return msg->len;
eio:
retval = -EIO;
err:
if (i2c_debug)
printk(KERN_ERR " ERR: %d\n", retval);
return retval;
}
static int i2c_readbytes(struct i2c_adapter *i2c_adap, const struct i2c_msg *msg, int joined)
{
struct cx25821_i2c *bus = i2c_adap->algo_data;
struct cx25821_dev *dev = bus->dev;
u32 ctrl, cnt;
int retval;
if (i2c_debug && !joined)
dprintk(1, "6-%s(msg->len=%d)\n", __func__, msg->len);
/* Deal with i2c probe functions with zero payload */
if (msg->len == 0) {
cx_write(bus->reg_addr, msg->addr << 25);
cx_write(bus->reg_ctrl, bus->i2c_period | (1 << 2) | 1);
if (!i2c_wait_done(i2c_adap))
return -EIO;
if (!i2c_slave_did_ack(i2c_adap))
return -EIO;
dprintk(1, "%s() returns 0\n", __func__);
return 0;
}
if (i2c_debug) {
if (joined)
dprintk(1, " R");
else
dprintk(1, " <R %02x", (msg->addr << 1) + 1);
}
for (cnt = 0; cnt < msg->len; cnt++) {
ctrl = bus->i2c_period | (1 << 12) | (1 << 2) | 1;
if (cnt < msg->len - 1)
ctrl |= I2C_NOSTOP | I2C_EXTEND;
cx_write(bus->reg_addr, msg->addr << 25);
cx_write(bus->reg_ctrl, ctrl);
retval = i2c_wait_done(i2c_adap);
if (retval < 0)
goto err;
if (retval == 0)
goto eio;
msg->buf[cnt] = cx_read(bus->reg_rdata) & 0xff;
if (i2c_debug) {
dprintk(1, " %02x", msg->buf[cnt]);
if (!(ctrl & I2C_NOSTOP))
dprintk(1, " >\n");
}
}
return msg->len;
eio:
retval = -EIO;
err:
if (i2c_debug)
printk(KERN_ERR " ERR: %d\n", retval);
return retval;
}
static int i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num)
{
struct cx25821_i2c *bus = i2c_adap->algo_data;
struct cx25821_dev *dev = bus->dev;
int i, retval = 0;
dprintk(1, "%s(num = %d)\n", __func__, num);
for (i = 0 ; i < num; i++)
{
dprintk(1, "%s(num = %d) addr = 0x%02x len = 0x%x\n",
__func__, num, msgs[i].addr, msgs[i].len);
if (msgs[i].flags & I2C_M_RD)
{
/* read */
retval = i2c_readbytes(i2c_adap, &msgs[i], 0);
}
else if (i + 1 < num && (msgs[i + 1].flags & I2C_M_RD) &&
msgs[i].addr == msgs[i + 1].addr)
{
/* write then read from same address */
retval = i2c_sendbytes(i2c_adap, &msgs[i], msgs[i + 1].len);
if (retval < 0)
goto err;
i++;
retval = i2c_readbytes(i2c_adap, &msgs[i], 1);
}
else
{
/* write */
retval = i2c_sendbytes(i2c_adap, &msgs[i], 0);
}
if (retval < 0)
goto err;
}
return num;
err:
return retval;
}
static u32 cx25821_functionality(struct i2c_adapter *adap)
{
return I2C_FUNC_SMBUS_EMUL |
I2C_FUNC_I2C |
I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_READ_WORD_DATA |
I2C_FUNC_SMBUS_WRITE_WORD_DATA;
}
static struct i2c_algorithm cx25821_i2c_algo_template = {
.master_xfer = i2c_xfer,
.functionality = cx25821_functionality,
};
static struct i2c_adapter cx25821_i2c_adap_template = {
.name = "cx25821",
.owner = THIS_MODULE,
.id = I2C_HW_B_CX25821,
.algo = &cx25821_i2c_algo_template,
};
static struct i2c_client cx25821_i2c_client_template = {
.name = "cx25821 internal",
};
/* init + register i2c algo-bit adapter */
int cx25821_i2c_register(struct cx25821_i2c *bus)
{
struct cx25821_dev *dev = bus->dev;
dprintk(1, "%s(bus = %d)\n", __func__, bus->nr);
memcpy(&bus->i2c_adap, &cx25821_i2c_adap_template,
sizeof(bus->i2c_adap));
memcpy(&bus->i2c_algo, &cx25821_i2c_algo_template,
sizeof(bus->i2c_algo));
memcpy(&bus->i2c_client, &cx25821_i2c_client_template,
sizeof(bus->i2c_client));
bus->i2c_adap.dev.parent = &dev->pci->dev;
strlcpy(bus->i2c_adap.name, bus->dev->name, sizeof(bus->i2c_adap.name));
bus->i2c_algo.data = bus;
bus->i2c_adap.algo_data = bus;
i2c_set_adapdata(&bus->i2c_adap, &dev->v4l2_dev);
i2c_add_adapter(&bus->i2c_adap);
bus->i2c_client.adapter = &bus->i2c_adap;
//set up the I2c
bus->i2c_client.addr = (0x88>>1);
return bus->i2c_rc;
}
int cx25821_i2c_unregister(struct cx25821_i2c *bus)
{
i2c_del_adapter(&bus->i2c_adap);
return 0;
}
void cx25821_av_clk(struct cx25821_dev *dev, int enable)
{
/* write 0 to bus 2 addr 0x144 via i2x_xfer() */
char buffer[3];
struct i2c_msg msg;
dprintk(1, "%s(enabled = %d)\n", __func__, enable);
/* Register 0x144 */
buffer[0] = 0x01;
buffer[1] = 0x44;
if (enable == 1)
buffer[2] = 0x05;
else
buffer[2] = 0x00;
msg.addr = 0x44;
msg.flags = I2C_M_TEN;
msg.len = 3;
msg.buf = buffer;
i2c_xfer(&dev->i2c_bus[0].i2c_adap, &msg, 1);
}
int cx25821_i2c_read(struct cx25821_i2c *bus, u16 reg_addr, int *value)
{
struct i2c_client *client = &bus->i2c_client;
int retval = 0;
int v = 0;
u8 addr[2] = {0, 0};
u8 buf[4] = {0,0,0,0};
struct i2c_msg msgs[2]={
{
.addr = client->addr,
.flags = 0,
.len = 2,
.buf = addr,
}, {
.addr = client->addr,
.flags = I2C_M_RD,
.len = 4,
.buf = buf,
}
};
addr[0] = (reg_addr>>8);
addr[1] = (reg_addr & 0xff);
msgs[0].addr = 0x44;
msgs[1].addr = 0x44;
retval = i2c_xfer(client->adapter, msgs, 2);
v = (buf[3]<<24) | (buf[2]<<16) | (buf[1]<<8) | buf[0];
*value = v;
return v;
}
int cx25821_i2c_write(struct cx25821_i2c *bus, u16 reg_addr, int value)
{
struct i2c_client *client = &bus->i2c_client;
int retval = 0;
u8 buf[6] = {0, 0, 0, 0, 0, 0};
struct i2c_msg msgs[1]={
{
.addr = client->addr,
.flags = 0,
.len = 6,
.buf = buf,
}
};
buf[0] = reg_addr>>8;
buf[1] = reg_addr & 0xff;
buf[5] = (value>>24) & 0xff;
buf[4] = (value>>16) & 0xff;
buf[3] = (value>>8) & 0xff;
buf[2] = value & 0xff;
client->flags = 0;
msgs[0].addr = 0x44;
retval = i2c_xfer(client->adapter, msgs, 1);
return retval;
}

View File

@ -0,0 +1,51 @@
/*
* Driver for the Conexant CX25821 PCIe bridge
*
* Copyright (C) 2009 Conexant Systems Inc.
* Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _MEDUSA_DEF_H_
#define _MEDUSA_DEF_H_
// Video deocder that we supported
#define VDEC_A 0
#define VDEC_B 1
#define VDEC_C 2
#define VDEC_D 3
#define VDEC_E 4
#define VDEC_F 5
#define VDEC_G 6
#define VDEC_H 7
//#define AUTO_SWITCH_BIT[] = { 8, 9, 10, 11, 12, 13, 14, 15 };
// The following bit position enables automatic source switching for decoder A-H.
// Display index per camera.
//#define VDEC_INDEX[] = {0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7};
// Select input bit to video decoder A-H.
//#define CH_SRC_SEL_BIT[] = {24, 25, 26, 27, 28, 29, 30, 31};
// end of display sequence
#define END_OF_SEQ 0xF;
// registry string size
#define MAX_REGISTRY_SZ 40;
#endif

View File

@ -0,0 +1,456 @@
/*
* Driver for the Conexant CX25821 PCIe bridge
*
* Copyright (C) 2009 Conexant Systems Inc.
* Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __MEDUSA_REGISTERS__
#define __MEDUSA_REGISTERS__
// Serial Slave Registers
#define HOST_REGISTER1 0x0000
#define HOST_REGISTER2 0x0001
// Chip Configuration Registers
#define CHIP_CTRL 0x0100
#define AFE_AB_CTRL 0x0104
#define AFE_CD_CTRL 0x0108
#define AFE_EF_CTRL 0x010C
#define AFE_GH_CTRL 0x0110
#define DENC_AB_CTRL 0x0114
#define BYP_AB_CTRL 0x0118
#define MON_A_CTRL 0x011C
#define DISP_SEQ_A 0x0120
#define DISP_SEQ_B 0x0124
#define DISP_AB_CNT 0x0128
#define DISP_CD_CNT 0x012C
#define DISP_EF_CNT 0x0130
#define DISP_GH_CNT 0x0134
#define DISP_IJ_CNT 0x0138
#define PIN_OE_CTRL 0x013C
#define PIN_SPD_CTRL 0x0140
#define PIN_SPD_CTRL2 0x0144
#define IRQ_STAT_CTRL 0x0148
#define POWER_CTRL_AB 0x014C
#define POWER_CTRL_CD 0x0150
#define POWER_CTRL_EF 0x0154
#define POWER_CTRL_GH 0x0158
#define TUNE_CTRL 0x015C
#define BIAS_CTRL 0x0160
#define AFE_AB_DIAG_CTRL 0x0164
#define AFE_CD_DIAG_CTRL 0x0168
#define AFE_EF_DIAG_CTRL 0x016C
#define AFE_GH_DIAG_CTRL 0x0170
#define PLL_AB_DIAG_CTRL 0x0174
#define PLL_CD_DIAG_CTRL 0x0178
#define PLL_EF_DIAG_CTRL 0x017C
#define PLL_GH_DIAG_CTRL 0x0180
#define TEST_CTRL 0x0184
#define BIST_STAT 0x0188
#define BIST_STAT2 0x018C
#define BIST_VID_PLL_AB_STAT 0x0190
#define BIST_VID_PLL_CD_STAT 0x0194
#define BIST_VID_PLL_EF_STAT 0x0198
#define BIST_VID_PLL_GH_STAT 0x019C
#define DLL_DIAG_CTRL 0x01A0
#define DEV_CH_ID_CTRL 0x01A4
#define ABIST_CTRL_STATUS 0x01A8
#define ABIST_FREQ 0x01AC
#define ABIST_GOERT_SHIFT 0x01B0
#define ABIST_COEF12 0x01B4
#define ABIST_COEF34 0x01B8
#define ABIST_COEF56 0x01BC
#define ABIST_COEF7_SNR 0x01C0
#define ABIST_ADC_CAL 0x01C4
#define ABIST_BIN1_VGA0 0x01C8
#define ABIST_BIN2_VGA1 0x01CC
#define ABIST_BIN3_VGA2 0x01D0
#define ABIST_BIN4_VGA3 0x01D4
#define ABIST_BIN5_VGA4 0x01D8
#define ABIST_BIN6_VGA5 0x01DC
#define ABIST_BIN7_VGA6 0x0x1E0
#define ABIST_CLAMP_A 0x0x1E4
#define ABIST_CLAMP_B 0x0x1E8
#define ABIST_CLAMP_C 0x01EC
#define ABIST_CLAMP_D 0x01F0
#define ABIST_CLAMP_E 0x01F4
#define ABIST_CLAMP_F 0x01F8
// Digital Video Encoder A Registers
#define DENC_A_REG_1 0x0200
#define DENC_A_REG_2 0x0204
#define DENC_A_REG_3 0x0208
#define DENC_A_REG_4 0x020C
#define DENC_A_REG_5 0x0210
#define DENC_A_REG_6 0x0214
#define DENC_A_REG_7 0x0218
#define DENC_A_REG_8 0x021C
// Digital Video Encoder B Registers
#define DENC_B_REG_1 0x0300
#define DENC_B_REG_2 0x0304
#define DENC_B_REG_3 0x0308
#define DENC_B_REG_4 0x030C
#define DENC_B_REG_5 0x0310
#define DENC_B_REG_6 0x0314
#define DENC_B_REG_7 0x0318
#define DENC_B_REG_8 0x031C
// Video Decoder A Registers
#define MODE_CTRL 0x1000
#define OUT_CTRL1 0x1004
#define OUT_CTRL_NS 0x1008
#define GEN_STAT 0x100C
#define INT_STAT_MASK 0x1010
#define LUMA_CTRL 0x1014
#define CHROMA_CTRL 0x1018
#define CRUSH_CTRL 0x101C
#define HORIZ_TIM_CTRL 0x1020
#define VERT_TIM_CTRL 0x1024
#define MISC_TIM_CTRL 0x1028
#define FIELD_COUNT 0x102C
#define HSCALE_CTRL 0x1030
#define VSCALE_CTRL 0x1034
#define MAN_VGA_CTRL 0x1038
#define MAN_AGC_CTRL 0x103C
#define DFE_CTRL1 0x1040
#define DFE_CTRL2 0x1044
#define DFE_CTRL3 0x1048
#define PLL_CTRL 0x104C
#define PLL_CTRL_FAST 0x1050
#define HTL_CTRL 0x1054
#define SRC_CFG 0x1058
#define SC_STEP_SIZE 0x105C
#define SC_CONVERGE_CTRL 0x1060
#define SC_LOOP_CTRL 0x1064
#define COMB_2D_HFS_CFG 0x1068
#define COMB_2D_HFD_CFG 0x106C
#define COMB_2D_LF_CFG 0x1070
#define COMB_2D_BLEND 0x1074
#define COMB_MISC_CTRL 0x1078
#define COMB_FLAT_THRESH_CTRL 0x107C
#define COMB_TEST 0x1080
#define BP_MISC_CTRL 0x1084
#define VCR_DET_CTRL 0x1088
#define NOISE_DET_CTRL 0x108C
#define COMB_FLAT_NOISE_CTRL 0x1090
#define VERSION 0x11F8
#define SOFT_RST_CTRL 0x11FC
// Video Decoder B Registers
#define VDEC_B_MODE_CTRL 0x1200
#define VDEC_B_OUT_CTRL1 0x1204
#define VDEC_B_OUT_CTRL_NS 0x1208
#define VDEC_B_GEN_STAT 0x120C
#define VDEC_B_INT_STAT_MASK 0x1210
#define VDEC_B_LUMA_CTRL 0x1214
#define VDEC_B_CHROMA_CTRL 0x1218
#define VDEC_B_CRUSH_CTRL 0x121C
#define VDEC_B_HORIZ_TIM_CTRL 0x1220
#define VDEC_B_VERT_TIM_CTRL 0x1224
#define VDEC_B_MISC_TIM_CTRL 0x1228
#define VDEC_B_FIELD_COUNT 0x122C
#define VDEC_B_HSCALE_CTRL 0x1230
#define VDEC_B_VSCALE_CTRL 0x1234
#define VDEC_B_MAN_VGA_CTRL 0x1238
#define VDEC_B_MAN_AGC_CTRL 0x123C
#define VDEC_B_DFE_CTRL1 0x1240
#define VDEC_B_DFE_CTRL2 0x1244
#define VDEC_B_DFE_CTRL3 0x1248
#define VDEC_B_PLL_CTRL 0x124C
#define VDEC_B_PLL_CTRL_FAST 0x1250
#define VDEC_B_HTL_CTRL 0x1254
#define VDEC_B_SRC_CFG 0x1258
#define VDEC_B_SC_STEP_SIZE 0x125C
#define VDEC_B_SC_CONVERGE_CTRL 0x1260
#define VDEC_B_SC_LOOP_CTRL 0x1264
#define VDEC_B_COMB_2D_HFS_CFG 0x1268
#define VDEC_B_COMB_2D_HFD_CFG 0x126C
#define VDEC_B_COMB_2D_LF_CFG 0x1270
#define VDEC_B_COMB_2D_BLEND 0x1274
#define VDEC_B_COMB_MISC_CTRL 0x1278
#define VDEC_B_COMB_FLAT_THRESH_CTRL 0x127C
#define VDEC_B_COMB_TEST 0x1280
#define VDEC_B_BP_MISC_CTRL 0x1284
#define VDEC_B_VCR_DET_CTRL 0x1288
#define VDEC_B_NOISE_DET_CTRL 0x128C
#define VDEC_B_COMB_FLAT_NOISE_CTRL 0x1290
#define VDEC_B_VERSION 0x13F8
#define VDEC_B_SOFT_RST_CTRL 0x13FC
// Video Decoder C Registers
#define VDEC_C_MODE_CTRL 0x1400
#define VDEC_C_OUT_CTRL1 0x1404
#define VDEC_C_OUT_CTRL_NS 0x1408
#define VDEC_C_GEN_STAT 0x140C
#define VDEC_C_INT_STAT_MASK 0x1410
#define VDEC_C_LUMA_CTRL 0x1414
#define VDEC_C_CHROMA_CTRL 0x1418
#define VDEC_C_CRUSH_CTRL 0x141C
#define VDEC_C_HORIZ_TIM_CTRL 0x1420
#define VDEC_C_VERT_TIM_CTRL 0x1424
#define VDEC_C_MISC_TIM_CTRL 0x1428
#define VDEC_C_FIELD_COUNT 0x142C
#define VDEC_C_HSCALE_CTRL 0x1430
#define VDEC_C_VSCALE_CTRL 0x1434
#define VDEC_C_MAN_VGA_CTRL 0x1438
#define VDEC_C_MAN_AGC_CTRL 0x143C
#define VDEC_C_DFE_CTRL1 0x1440
#define VDEC_C_DFE_CTRL2 0x1444
#define VDEC_C_DFE_CTRL3 0x1448
#define VDEC_C_PLL_CTRL 0x144C
#define VDEC_C_PLL_CTRL_FAST 0x1450
#define VDEC_C_HTL_CTRL 0x1454
#define VDEC_C_SRC_CFG 0x1458
#define VDEC_C_SC_STEP_SIZE 0x145C
#define VDEC_C_SC_CONVERGE_CTRL 0x1460
#define VDEC_C_SC_LOOP_CTRL 0x1464
#define VDEC_C_COMB_2D_HFS_CFG 0x1468
#define VDEC_C_COMB_2D_HFD_CFG 0x146C
#define VDEC_C_COMB_2D_LF_CFG 0x1470
#define VDEC_C_COMB_2D_BLEND 0x1474
#define VDEC_C_COMB_MISC_CTRL 0x1478
#define VDEC_C_COMB_FLAT_THRESH_CTRL 0x147C
#define VDEC_C_COMB_TEST 0x1480
#define VDEC_C_BP_MISC_CTRL 0x1484
#define VDEC_C_VCR_DET_CTRL 0x1488
#define VDEC_C_NOISE_DET_CTRL 0x148C
#define VDEC_C_COMB_FLAT_NOISE_CTRL 0x1490
#define VDEC_C_VERSION 0x15F8
#define VDEC_C_SOFT_RST_CTRL 0x15FC
// Video Decoder D Registers
#define VDEC_D_MODE_CTRL 0x1600
#define VDEC_D_OUT_CTRL1 0x1604
#define VDEC_D_OUT_CTRL_NS 0x1608
#define VDEC_D_GEN_STAT 0x160C
#define VDEC_D_INT_STAT_MASK 0x1610
#define VDEC_D_LUMA_CTRL 0x1614
#define VDEC_D_CHROMA_CTRL 0x1618
#define VDEC_D_CRUSH_CTRL 0x161C
#define VDEC_D_HORIZ_TIM_CTRL 0x1620
#define VDEC_D_VERT_TIM_CTRL 0x1624
#define VDEC_D_MISC_TIM_CTRL 0x1628
#define VDEC_D_FIELD_COUNT 0x162C
#define VDEC_D_HSCALE_CTRL 0x1630
#define VDEC_D_VSCALE_CTRL 0x1634
#define VDEC_D_MAN_VGA_CTRL 0x1638
#define VDEC_D_MAN_AGC_CTRL 0x163C
#define VDEC_D_DFE_CTRL1 0x1640
#define VDEC_D_DFE_CTRL2 0x1644
#define VDEC_D_DFE_CTRL3 0x1648
#define VDEC_D_PLL_CTRL 0x164C
#define VDEC_D_PLL_CTRL_FAST 0x1650
#define VDEC_D_HTL_CTRL 0x1654
#define VDEC_D_SRC_CFG 0x1658
#define VDEC_D_SC_STEP_SIZE 0x165C
#define VDEC_D_SC_CONVERGE_CTRL 0x1660
#define VDEC_D_SC_LOOP_CTRL 0x1664
#define VDEC_D_COMB_2D_HFS_CFG 0x1668
#define VDEC_D_COMB_2D_HFD_CFG 0x166C
#define VDEC_D_COMB_2D_LF_CFG 0x1670
#define VDEC_D_COMB_2D_BLEND 0x1674
#define VDEC_D_COMB_MISC_CTRL 0x1678
#define VDEC_D_COMB_FLAT_THRESH_CTRL 0x167C
#define VDEC_D_COMB_TEST 0x1680
#define VDEC_D_BP_MISC_CTRL 0x1684
#define VDEC_D_VCR_DET_CTRL 0x1688
#define VDEC_D_NOISE_DET_CTRL 0x168C
#define VDEC_D_COMB_FLAT_NOISE_CTRL 0x1690
#define VDEC_D_VERSION 0x17F8
#define VDEC_D_SOFT_RST_CTRL 0x17FC
// Video Decoder E Registers
#define VDEC_E_MODE_CTRL 0x1800
#define VDEC_E_OUT_CTRL1 0x1804
#define VDEC_E_OUT_CTRL_NS 0x1808
#define VDEC_E_GEN_STAT 0x180C
#define VDEC_E_INT_STAT_MASK 0x1810
#define VDEC_E_LUMA_CTRL 0x1814
#define VDEC_E_CHROMA_CTRL 0x1818
#define VDEC_E_CRUSH_CTRL 0x181C
#define VDEC_E_HORIZ_TIM_CTRL 0x1820
#define VDEC_E_VERT_TIM_CTRL 0x1824
#define VDEC_E_MISC_TIM_CTRL 0x1828
#define VDEC_E_FIELD_COUNT 0x182C
#define VDEC_E_HSCALE_CTRL 0x1830
#define VDEC_E_VSCALE_CTRL 0x1834
#define VDEC_E_MAN_VGA_CTRL 0x1838
#define VDEC_E_MAN_AGC_CTRL 0x183C
#define VDEC_E_DFE_CTRL1 0x1840
#define VDEC_E_DFE_CTRL2 0x1844
#define VDEC_E_DFE_CTRL3 0x1848
#define VDEC_E_PLL_CTRL 0x184C
#define VDEC_E_PLL_CTRL_FAST 0x1850
#define VDEC_E_HTL_CTRL 0x1854
#define VDEC_E_SRC_CFG 0x1858
#define VDEC_E_SC_STEP_SIZE 0x185C
#define VDEC_E_SC_CONVERGE_CTRL 0x1860
#define VDEC_E_SC_LOOP_CTRL 0x1864
#define VDEC_E_COMB_2D_HFS_CFG 0x1868
#define VDEC_E_COMB_2D_HFD_CFG 0x186C
#define VDEC_E_COMB_2D_LF_CFG 0x1870
#define VDEC_E_COMB_2D_BLEND 0x1874
#define VDEC_E_COMB_MISC_CTRL 0x1878
#define VDEC_E_COMB_FLAT_THRESH_CTRL 0x187C
#define VDEC_E_COMB_TEST 0x1880
#define VDEC_E_BP_MISC_CTRL 0x1884
#define VDEC_E_VCR_DET_CTRL 0x1888
#define VDEC_E_NOISE_DET_CTRL 0x188C
#define VDEC_E_COMB_FLAT_NOISE_CTRL 0x1890
#define VDEC_E_VERSION 0x19F8
#define VDEC_E_SOFT_RST_CTRL 0x19FC
// Video Decoder F Registers
#define VDEC_F_MODE_CTRL 0x1A00
#define VDEC_F_OUT_CTRL1 0x1A04
#define VDEC_F_OUT_CTRL_NS 0x1A08
#define VDEC_F_GEN_STAT 0x1A0C
#define VDEC_F_INT_STAT_MASK 0x1A10
#define VDEC_F_LUMA_CTRL 0x1A14
#define VDEC_F_CHROMA_CTRL 0x1A18
#define VDEC_F_CRUSH_CTRL 0x1A1C
#define VDEC_F_HORIZ_TIM_CTRL 0x1A20
#define VDEC_F_VERT_TIM_CTRL 0x1A24
#define VDEC_F_MISC_TIM_CTRL 0x1A28
#define VDEC_F_FIELD_COUNT 0x1A2C
#define VDEC_F_HSCALE_CTRL 0x1A30
#define VDEC_F_VSCALE_CTRL 0x1A34
#define VDEC_F_MAN_VGA_CTRL 0x1A38
#define VDEC_F_MAN_AGC_CTRL 0x1A3C
#define VDEC_F_DFE_CTRL1 0x1A40
#define VDEC_F_DFE_CTRL2 0x1A44
#define VDEC_F_DFE_CTRL3 0x1A48
#define VDEC_F_PLL_CTRL 0x1A4C
#define VDEC_F_PLL_CTRL_FAST 0x1A50
#define VDEC_F_HTL_CTRL 0x1A54
#define VDEC_F_SRC_CFG 0x1A58
#define VDEC_F_SC_STEP_SIZE 0x1A5C
#define VDEC_F_SC_CONVERGE_CTRL 0x1A60
#define VDEC_F_SC_LOOP_CTRL 0x1A64
#define VDEC_F_COMB_2D_HFS_CFG 0x1A68
#define VDEC_F_COMB_2D_HFD_CFG 0x1A6C
#define VDEC_F_COMB_2D_LF_CFG 0x1A70
#define VDEC_F_COMB_2D_BLEND 0x1A74
#define VDEC_F_COMB_MISC_CTRL 0x1A78
#define VDEC_F_COMB_FLAT_THRESH_CTRL 0x1A7C
#define VDEC_F_COMB_TEST 0x1A80
#define VDEC_F_BP_MISC_CTRL 0x1A84
#define VDEC_F_VCR_DET_CTRL 0x1A88
#define VDEC_F_NOISE_DET_CTRL 0x1A8C
#define VDEC_F_COMB_FLAT_NOISE_CTRL 0x1A90
#define VDEC_F_VERSION 0x1BF8
#define VDEC_F_SOFT_RST_CTRL 0x1BFC
// Video Decoder G Registers
#define VDEC_G_MODE_CTRL 0x1C00
#define VDEC_G_OUT_CTRL1 0x1C04
#define VDEC_G_OUT_CTRL_NS 0x1C08
#define VDEC_G_GEN_STAT 0x1C0C
#define VDEC_G_INT_STAT_MASK 0x1C10
#define VDEC_G_LUMA_CTRL 0x1C14
#define VDEC_G_CHROMA_CTRL 0x1C18
#define VDEC_G_CRUSH_CTRL 0x1C1C
#define VDEC_G_HORIZ_TIM_CTRL 0x1C20
#define VDEC_G_VERT_TIM_CTRL 0x1C24
#define VDEC_G_MISC_TIM_CTRL 0x1C28
#define VDEC_G_FIELD_COUNT 0x1C2C
#define VDEC_G_HSCALE_CTRL 0x1C30
#define VDEC_G_VSCALE_CTRL 0x1C34
#define VDEC_G_MAN_VGA_CTRL 0x1C38
#define VDEC_G_MAN_AGC_CTRL 0x1C3C
#define VDEC_G_DFE_CTRL1 0x1C40
#define VDEC_G_DFE_CTRL2 0x1C44
#define VDEC_G_DFE_CTRL3 0x1C48
#define VDEC_G_PLL_CTRL 0x1C4C
#define VDEC_G_PLL_CTRL_FAST 0x1C50
#define VDEC_G_HTL_CTRL 0x1C54
#define VDEC_G_SRC_CFG 0x1C58
#define VDEC_G_SC_STEP_SIZE 0x1C5C
#define VDEC_G_SC_CONVERGE_CTRL 0x1C60
#define VDEC_G_SC_LOOP_CTRL 0x1C64
#define VDEC_G_COMB_2D_HFS_CFG 0x1C68
#define VDEC_G_COMB_2D_HFD_CFG 0x1C6C
#define VDEC_G_COMB_2D_LF_CFG 0x1C70
#define VDEC_G_COMB_2D_BLEND 0x1C74
#define VDEC_G_COMB_MISC_CTRL 0x1C78
#define VDEC_G_COMB_FLAT_THRESH_CTRL 0x1C7C
#define VDEC_G_COMB_TEST 0x1C80
#define VDEC_G_BP_MISC_CTRL 0x1C84
#define VDEC_G_VCR_DET_CTRL 0x1C88
#define VDEC_G_NOISE_DET_CTRL 0x1C8C
#define VDEC_G_COMB_FLAT_NOISE_CTRL 0x1C90
#define VDEC_G_VERSION 0x1DF8
#define VDEC_G_SOFT_RST_CTRL 0x1DFC
// Video Decoder H Registers
#define VDEC_H_MODE_CTRL 0x1E00
#define VDEC_H_OUT_CTRL1 0x1E04
#define VDEC_H_OUT_CTRL_NS 0x1E08
#define VDEC_H_GEN_STAT 0x1E0C
#define VDEC_H_INT_STAT_MASK 0x1E1E
#define VDEC_H_LUMA_CTRL 0x1E14
#define VDEC_H_CHROMA_CTRL 0x1E18
#define VDEC_H_CRUSH_CTRL 0x1E1C
#define VDEC_H_HORIZ_TIM_CTRL 0x1E20
#define VDEC_H_VERT_TIM_CTRL 0x1E24
#define VDEC_H_MISC_TIM_CTRL 0x1E28
#define VDEC_H_FIELD_COUNT 0x1E2C
#define VDEC_H_HSCALE_CTRL 0x1E30
#define VDEC_H_VSCALE_CTRL 0x1E34
#define VDEC_H_MAN_VGA_CTRL 0x1E38
#define VDEC_H_MAN_AGC_CTRL 0x1E3C
#define VDEC_H_DFE_CTRL1 0x1E40
#define VDEC_H_DFE_CTRL2 0x1E44
#define VDEC_H_DFE_CTRL3 0x1E48
#define VDEC_H_PLL_CTRL 0x1E4C
#define VDEC_H_PLL_CTRL_FAST 0x1E50
#define VDEC_H_HTL_CTRL 0x1E54
#define VDEC_H_SRC_CFG 0x1E58
#define VDEC_H_SC_STEP_SIZE 0x1E5C
#define VDEC_H_SC_CONVERGE_CTRL 0x1E60
#define VDEC_H_SC_LOOP_CTRL 0x1E64
#define VDEC_H_COMB_2D_HFS_CFG 0x1E68
#define VDEC_H_COMB_2D_HFD_CFG 0x1E6C
#define VDEC_H_COMB_2D_LF_CFG 0x1E70
#define VDEC_H_COMB_2D_BLEND 0x1E74
#define VDEC_H_COMB_MISC_CTRL 0x1E78
#define VDEC_H_COMB_FLAT_THRESH_CTRL 0x1E7C
#define VDEC_H_COMB_TEST 0x1E80
#define VDEC_H_BP_MISC_CTRL 0x1E84
#define VDEC_H_VCR_DET_CTRL 0x1E88
#define VDEC_H_NOISE_DET_CTRL 0x1E8C
#define VDEC_H_COMB_FLAT_NOISE_CTRL 0x1E90
#define VDEC_H_VERSION 0x1FF8
#define VDEC_H_SOFT_RST_CTRL 0x1FFC
//*****************************************************************************
// LUMA_CTRL register fields
#define VDEC_A_BRITE_CTRL 0x1014
#define VDEC_A_CNTRST_CTRL 0x1015
#define VDEC_A_PEAK_SEL 0x1016
//*****************************************************************************
// CHROMA_CTRL register fields
#define VDEC_A_USAT_CTRL 0x1018
#define VDEC_A_VSAT_CTRL 0x1019
#define VDEC_A_HUE_CTRL 0x101A
#endif

View File

@ -0,0 +1,769 @@
/*
* Driver for the Conexant CX25821 PCIe bridge
*
* Copyright (C) 2009 Conexant Systems Inc.
* Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "cx25821.h"
#include "cx25821-medusa-video.h"
#include "cx25821-biffuncs.h"
/////////////////////////////////////////////////////////////////////////////////////////
//medusa_enable_bluefield_output()
//
// Enable the generation of blue filed output if no video
//
static void medusa_enable_bluefield_output(struct cx25821_dev *dev, int channel, int enable)
{
int ret_val = 1;
u32 value = 0;
u32 tmp = 0;
int out_ctrl = OUT_CTRL1;
int out_ctrl_ns = OUT_CTRL_NS;
switch (channel)
{
default:
case VDEC_A:
break;
case VDEC_B:
out_ctrl = VDEC_B_OUT_CTRL1;
out_ctrl_ns = VDEC_B_OUT_CTRL_NS;
break;
case VDEC_C:
out_ctrl = VDEC_C_OUT_CTRL1;
out_ctrl_ns = VDEC_C_OUT_CTRL_NS;
break;
case VDEC_D:
out_ctrl = VDEC_D_OUT_CTRL1;
out_ctrl_ns = VDEC_D_OUT_CTRL_NS;
break;
case VDEC_E:
out_ctrl = VDEC_E_OUT_CTRL1;
out_ctrl_ns = VDEC_E_OUT_CTRL_NS;
return;
case VDEC_F:
out_ctrl = VDEC_F_OUT_CTRL1;
out_ctrl_ns = VDEC_F_OUT_CTRL_NS;
return;
case VDEC_G:
out_ctrl = VDEC_G_OUT_CTRL1;
out_ctrl_ns = VDEC_G_OUT_CTRL_NS;
return;
case VDEC_H:
out_ctrl = VDEC_H_OUT_CTRL1;
out_ctrl_ns = VDEC_H_OUT_CTRL_NS;
return;
}
value = cx25821_i2c_read(&dev->i2c_bus[0], out_ctrl, &tmp);
value &= 0xFFFFFF7F; // clear BLUE_FIELD_EN
if (enable)
value |= 0x00000080; // set BLUE_FIELD_EN
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], out_ctrl, value);
value = cx25821_i2c_read(&dev->i2c_bus[0], out_ctrl_ns, &tmp);
value &= 0xFFFFFF7F;
if (enable)
value |= 0x00000080; // set BLUE_FIELD_EN
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], out_ctrl_ns, value);
}
static int medusa_initialize_ntsc(struct cx25821_dev *dev)
{
int ret_val = 0;
int i = 0;
u32 value = 0;
u32 tmp = 0;
mutex_lock(&dev->lock);
for (i=0; i < MAX_DECODERS; i++)
{
// set video format NTSC-M
value = cx25821_i2c_read(&dev->i2c_bus[0], MODE_CTRL+(0x200*i), &tmp);
value &= 0xFFFFFFF0;
value |= 0x10001; // enable the fast locking mode bit[16]
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], MODE_CTRL+(0x200*i), value);
// resolution NTSC 720x480
value = cx25821_i2c_read(&dev->i2c_bus[0], HORIZ_TIM_CTRL+(0x200*i), &tmp);
value &= 0x00C00C00;
value |= 0x612D0074;
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], HORIZ_TIM_CTRL+(0x200*i), value);
value = cx25821_i2c_read(&dev->i2c_bus[0], VERT_TIM_CTRL+(0x200*i), &tmp);
value &= 0x00C00C00;
value |= 0x1C1E001A; // vblank_cnt + 2 to get camera ID
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], VERT_TIM_CTRL+(0x200*i), value);
// chroma subcarrier step size
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], SC_STEP_SIZE+(0x200*i), 0x43E00000);
// enable VIP optional active
value = cx25821_i2c_read(&dev->i2c_bus[0], OUT_CTRL_NS+(0x200*i), &tmp);
value &= 0xFFFBFFFF;
value |= 0x00040000;
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], OUT_CTRL_NS+(0x200*i), value);
// enable VIP optional active (VIP_OPT_AL) for direct output.
value = cx25821_i2c_read(&dev->i2c_bus[0], OUT_CTRL1+(0x200*i), &tmp);
value &= 0xFFFBFFFF;
value |= 0x00040000;
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], OUT_CTRL1+(0x200*i), value);
// clear VPRES_VERT_EN bit, fixes the chroma run away problem
// when the input switching rate < 16 fields
//
value = cx25821_i2c_read(&dev->i2c_bus[0], MISC_TIM_CTRL+(0x200*i), &tmp);
value = setBitAtPos(value, 14); // disable special play detection
value = clearBitAtPos(value, 15);
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], MISC_TIM_CTRL+(0x200*i), value);
// set vbi_gate_en to 0
value = cx25821_i2c_read(&dev->i2c_bus[0], DFE_CTRL1+(0x200*i), &tmp);
value = clearBitAtPos(value, 29);
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], DFE_CTRL1+(0x200*i), value);
// Enable the generation of blue field output if no video
medusa_enable_bluefield_output(dev, i, 1);
}
for (i=0; i < MAX_ENCODERS; i++)
{
// NTSC hclock
value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_A_REG_1+(0x100*i), &tmp);
value &= 0xF000FC00;
value |= 0x06B402D0;
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], DENC_A_REG_1+(0x100*i), value);
// burst begin and burst end
value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_A_REG_2+(0x100*i), &tmp);
value &= 0xFF000000;
value |= 0x007E9054;
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], DENC_A_REG_2+(0x100*i), value);
value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_A_REG_3+(0x100*i), &tmp);
value &= 0xFC00FE00;
value |= 0x00EC00F0;
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], DENC_A_REG_3+(0x100*i), value);
// set NTSC vblank, no phase alternation, 7.5 IRE pedestal
value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_A_REG_4+(0x100*i), &tmp);
value &= 0x00FCFFFF;
value |= 0x13020000;
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], DENC_A_REG_4+(0x100*i), value);
value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_A_REG_5+(0x100*i), &tmp);
value &= 0xFFFF0000;
value |= 0x0000E575;
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], DENC_A_REG_5+(0x100*i), value);
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], DENC_A_REG_6+(0x100*i), 0x009A89C1);
// Subcarrier Increment
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], DENC_A_REG_7+(0x100*i), 0x21F07C1F);
}
//set picture resolutions
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], HSCALE_CTRL, 0x0); //0 - 720
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], VSCALE_CTRL, 0x0); //0 - 480
// set Bypass input format to NTSC 525 lines
value = cx25821_i2c_read(&dev->i2c_bus[0], BYP_AB_CTRL, &tmp);
value |= 0x00080200;
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], BYP_AB_CTRL, value);
mutex_unlock(&dev->lock);
return ret_val;
}
static int medusa_PALCombInit(struct cx25821_dev *dev, int dec)
{
int ret_val = -1;
u32 value = 0, tmp = 0;
// Setup for 2D threshold
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], COMB_2D_HFS_CFG+(0x200*dec), 0x20002861);
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], COMB_2D_HFD_CFG+(0x200*dec), 0x20002861);
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], COMB_2D_LF_CFG+(0x200*dec), 0x200A1023);
// Setup flat chroma and luma thresholds
value = cx25821_i2c_read(&dev->i2c_bus[0], COMB_FLAT_THRESH_CTRL+(0x200*dec), &tmp);
value &= 0x06230000;
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], COMB_FLAT_THRESH_CTRL+(0x200*dec), value);
// set comb 2D blend
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], COMB_2D_BLEND+(0x200*dec), 0x210F0F0F);
// COMB MISC CONTROL
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], COMB_MISC_CTRL+(0x200*dec), 0x41120A7F);
return ret_val;
}
static int medusa_initialize_pal(struct cx25821_dev *dev)
{
int ret_val = 0;
int i = 0;
u32 value = 0;
u32 tmp = 0;
mutex_lock(&dev->lock);
for (i=0; i < MAX_DECODERS; i++)
{
// set video format PAL-BDGHI
value = cx25821_i2c_read(&dev->i2c_bus[0], MODE_CTRL+(0x200*i), &tmp);
value &= 0xFFFFFFF0;
value |= 0x10004; // enable the fast locking mode bit[16]
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], MODE_CTRL+(0x200*i), value);
// resolution PAL 720x576
value = cx25821_i2c_read(&dev->i2c_bus[0], HORIZ_TIM_CTRL+(0x200*i), &tmp);
value &= 0x00C00C00;
value |= 0x632D007D;
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], HORIZ_TIM_CTRL+(0x200*i), value);
// vblank656_cnt=x26, vactive_cnt=240h, vblank_cnt=x24
value = cx25821_i2c_read(&dev->i2c_bus[0], VERT_TIM_CTRL+(0x200*i), &tmp);
value &= 0x00C00C00;
value |= 0x28240026; // vblank_cnt + 2 to get camera ID
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], VERT_TIM_CTRL+(0x200*i), value);
// chroma subcarrier step size
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], SC_STEP_SIZE+(0x200*i), 0x5411E2D0);
// enable VIP optional active
value = cx25821_i2c_read(&dev->i2c_bus[0], OUT_CTRL_NS+(0x200*i), &tmp);
value &= 0xFFFBFFFF;
value |= 0x00040000;
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], OUT_CTRL_NS+(0x200*i), value);
// enable VIP optional active (VIP_OPT_AL) for direct output.
value = cx25821_i2c_read(&dev->i2c_bus[0], OUT_CTRL1+(0x200*i), &tmp);
value &= 0xFFFBFFFF;
value |= 0x00040000;
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], OUT_CTRL1+(0x200*i), value);
// clear VPRES_VERT_EN bit, fixes the chroma run away problem
// when the input switching rate < 16 fields
value = cx25821_i2c_read(&dev->i2c_bus[0], MISC_TIM_CTRL+(0x200*i), &tmp);
value = setBitAtPos(value, 14); // disable special play detection
value = clearBitAtPos(value, 15);
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], MISC_TIM_CTRL+(0x200*i), value);
// set vbi_gate_en to 0
value = cx25821_i2c_read(&dev->i2c_bus[0], DFE_CTRL1+(0x200*i), &tmp);
value = clearBitAtPos(value, 29);
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], DFE_CTRL1+(0x200*i), value);
medusa_PALCombInit(dev, i);
// Enable the generation of blue field output if no video
medusa_enable_bluefield_output(dev, i, 1);
}
for (i=0; i < MAX_ENCODERS; i++)
{
// PAL hclock
value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_A_REG_1+(0x100*i), &tmp);
value &= 0xF000FC00;
value |= 0x06C002D0;
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], DENC_A_REG_1+(0x100*i), value);
// burst begin and burst end
value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_A_REG_2+(0x100*i), &tmp);
value &= 0xFF000000;
value |= 0x007E9754;
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], DENC_A_REG_2+(0x100*i), value);
// hblank and vactive
value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_A_REG_3+(0x100*i), &tmp);
value &= 0xFC00FE00;
value |= 0x00FC0120;
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], DENC_A_REG_3+(0x100*i), value);
// set PAL vblank, phase alternation, 0 IRE pedestal
value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_A_REG_4+(0x100*i), &tmp);
value &= 0x00FCFFFF;
value |= 0x14010000;
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], DENC_A_REG_4+(0x100*i), value);
value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_A_REG_5+(0x100*i), &tmp);
value &= 0xFFFF0000;
value |= 0x0000F078;
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], DENC_A_REG_5+(0x100*i), value);
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], DENC_A_REG_6+(0x100*i), 0x00A493CF);
// Subcarrier Increment
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], DENC_A_REG_7+(0x100*i), 0x2A098ACB);
}
//set picture resolutions
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], HSCALE_CTRL, 0x0); //0 - 720
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], VSCALE_CTRL, 0x0); //0 - 576
// set Bypass input format to PAL 625 lines
value = cx25821_i2c_read(&dev->i2c_bus[0], BYP_AB_CTRL, &tmp);
value &= 0xFFF7FDFF;
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], BYP_AB_CTRL, value);
mutex_unlock(&dev->lock);
return ret_val;
}
int medusa_set_videostandard(struct cx25821_dev *dev)
{
int status = STATUS_SUCCESS;
u32 value = 0, tmp = 0;
if(dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK)
{
status = medusa_initialize_pal(dev);
}
else
{
status = medusa_initialize_ntsc(dev);
}
// Enable DENC_A output
value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_A_REG_4, &tmp);
value = setBitAtPos(value, 4);
status = cx25821_i2c_write(&dev->i2c_bus[0], DENC_A_REG_4, value);
// Enable DENC_B output
value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_B_REG_4, &tmp);
value = setBitAtPos(value, 4);
status = cx25821_i2c_write(&dev->i2c_bus[0], DENC_B_REG_4, value);
return status;
}
void medusa_set_resolution(struct cx25821_dev *dev, int width, int decoder_select)
{
int decoder = 0;
int decoder_count = 0;
int ret_val = 0;
u32 hscale = 0x0;
u32 vscale = 0x0;
const int MAX_WIDTH = 720;
mutex_lock(&dev->lock);
// validate the width - cannot be negative
if (width > MAX_WIDTH)
{
printk("cx25821 %s() : width %d > MAX_WIDTH %d ! resetting to MAX_WIDTH \n", __func__, width, MAX_WIDTH);
width = MAX_WIDTH;
}
if( decoder_select <= 7 && decoder_select >= 0 )
{
decoder = decoder_select;
decoder_count = decoder_select + 1;
}
else
{
decoder = 0;
decoder_count = _num_decoders;
}
switch( width )
{
case 320:
hscale = 0x13E34B;
vscale = 0x0;
break;
case 352:
hscale = 0x10A273;
vscale = 0x0;
break;
case 176:
hscale = 0x3115B2;
vscale = 0x1E00;
break;
case 160:
hscale = 0x378D84;
vscale = 0x1E00;
break;
default: //720
hscale = 0x0;
vscale = 0x0;
break;
}
for( ; decoder < decoder_count; decoder++)
{
// write scaling values for each decoder
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], HSCALE_CTRL+(0x200*decoder), hscale);
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], VSCALE_CTRL+(0x200*decoder), vscale);
}
mutex_unlock(&dev->lock);
}
static void medusa_set_decoderduration(struct cx25821_dev *dev, int decoder, int duration)
{
int ret_val = 0;
u32 fld_cnt = 0;
u32 tmp = 0;
u32 disp_cnt_reg = DISP_AB_CNT;
mutex_lock(&dev->lock);
// no support
if (decoder < VDEC_A && decoder > VDEC_H)
{
mutex_unlock(&dev->lock);
return;
}
switch (decoder)
{
default:
break;
case VDEC_C:
case VDEC_D:
disp_cnt_reg = DISP_CD_CNT;
break;
case VDEC_E:
case VDEC_F:
disp_cnt_reg = DISP_EF_CNT;
break;
case VDEC_G:
case VDEC_H:
disp_cnt_reg = DISP_GH_CNT;
break;
}
_display_field_cnt[decoder] = duration;
// update hardware
fld_cnt = cx25821_i2c_read(&dev->i2c_bus[0], disp_cnt_reg, &tmp);
if (!(decoder % 2)) // EVEN decoder
{
fld_cnt &= 0xFFFF0000;
fld_cnt |= duration;
}
else
{
fld_cnt &= 0x0000FFFF;
fld_cnt |= ((u32)duration) << 16;
}
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], disp_cnt_reg, fld_cnt);
mutex_unlock(&dev->lock);
}
/////////////////////////////////////////////////////////////////////////////////////////
// Map to Medusa register setting
static int mapM(
int srcMin,
int srcMax,
int srcVal,
int dstMin,
int dstMax,
int* dstVal
)
{
int numerator;
int denominator;
int quotient;
if((srcMin == srcMax) || (srcVal < srcMin) || (srcVal > srcMax))
{
return -1;
}
// This is the overall expression used:
// *dstVal = (srcVal - srcMin)*(dstMax - dstMin) / (srcMax - srcMin) + dstMin;
// but we need to account for rounding so below we use the modulus
// operator to find the remainder and increment if necessary.
numerator = (srcVal - srcMin)*(dstMax - dstMin);
denominator = srcMax - srcMin;
quotient = numerator/denominator;
if(2 * ( numerator % denominator ) >= denominator)
{
quotient++;
}
*dstVal = quotient + dstMin;
return 0;
}
static unsigned long convert_to_twos(long numeric, unsigned long bits_len)
{
unsigned char temp;
if (numeric >= 0)
return numeric;
else
{
temp = ~(abs(numeric) & 0xFF);
temp += 1;
return temp;
}
}
/////////////////////////////////////////////////////////////////////////////////////////
int medusa_set_brightness(struct cx25821_dev *dev, int brightness, int decoder)
{
int ret_val = 0;
int value = 0;
u32 val = 0, tmp = 0;
mutex_lock(&dev->lock);
if((brightness > VIDEO_PROCAMP_MAX) || (brightness < VIDEO_PROCAMP_MIN))
{
mutex_unlock(&dev->lock);
return -1;
}
ret_val = mapM(VIDEO_PROCAMP_MIN, VIDEO_PROCAMP_MAX, brightness, SIGNED_BYTE_MIN, SIGNED_BYTE_MAX, &value);
value = convert_to_twos(value, 8);
val = cx25821_i2c_read(&dev->i2c_bus[0], VDEC_A_BRITE_CTRL+(0x200*decoder), &tmp);
val &= 0xFFFFFF00;
ret_val |= cx25821_i2c_write(&dev->i2c_bus[0], VDEC_A_BRITE_CTRL+(0x200*decoder), val | value);
mutex_unlock(&dev->lock);
return ret_val;
}
/////////////////////////////////////////////////////////////////////////////////////////
int medusa_set_contrast(struct cx25821_dev *dev, int contrast, int decoder)
{
int ret_val = 0;
int value = 0;
u32 val = 0, tmp = 0;
mutex_lock(&dev->lock);
if((contrast > VIDEO_PROCAMP_MAX) || (contrast < VIDEO_PROCAMP_MIN))
{
mutex_unlock(&dev->lock);
return -1;
}
ret_val = mapM(VIDEO_PROCAMP_MIN, VIDEO_PROCAMP_MAX, contrast, UNSIGNED_BYTE_MIN, UNSIGNED_BYTE_MAX, &value);
val = cx25821_i2c_read(&dev->i2c_bus[0], VDEC_A_CNTRST_CTRL+(0x200*decoder), &tmp);
val &= 0xFFFFFF00;
ret_val |= cx25821_i2c_write(&dev->i2c_bus[0], VDEC_A_CNTRST_CTRL+(0x200*decoder), val | value);
mutex_unlock(&dev->lock);
return ret_val;
}
/////////////////////////////////////////////////////////////////////////////////////////
int medusa_set_hue(struct cx25821_dev *dev, int hue, int decoder)
{
int ret_val = 0;
int value = 0;
u32 val = 0, tmp = 0;
mutex_lock(&dev->lock);
if((hue > VIDEO_PROCAMP_MAX) || (hue < VIDEO_PROCAMP_MIN))
{
mutex_unlock(&dev->lock);
return -1;
}
ret_val = mapM(VIDEO_PROCAMP_MIN, VIDEO_PROCAMP_MAX, hue, SIGNED_BYTE_MIN, SIGNED_BYTE_MAX, &value);
value = convert_to_twos(value, 8);
val = cx25821_i2c_read(&dev->i2c_bus[0], VDEC_A_HUE_CTRL+(0x200*decoder), &tmp);
val &= 0xFFFFFF00;
ret_val |= cx25821_i2c_write(&dev->i2c_bus[0], VDEC_A_HUE_CTRL+(0x200*decoder), val | value);
mutex_unlock(&dev->lock);
return ret_val;
}
/////////////////////////////////////////////////////////////////////////////////////////
int medusa_set_saturation(struct cx25821_dev *dev, int saturation, int decoder)
{
int ret_val = 0;
int value = 0;
u32 val = 0, tmp = 0;
mutex_lock(&dev->lock);
if((saturation > VIDEO_PROCAMP_MAX) || (saturation < VIDEO_PROCAMP_MIN))
{
mutex_unlock(&dev->lock);
return -1;
}
ret_val = mapM(VIDEO_PROCAMP_MIN, VIDEO_PROCAMP_MAX, saturation, UNSIGNED_BYTE_MIN, UNSIGNED_BYTE_MAX, &value);
val = cx25821_i2c_read(&dev->i2c_bus[0], VDEC_A_USAT_CTRL+(0x200*decoder), &tmp);
val &= 0xFFFFFF00;
ret_val |= cx25821_i2c_write(&dev->i2c_bus[0], VDEC_A_USAT_CTRL+(0x200*decoder), val | value);
val = cx25821_i2c_read(&dev->i2c_bus[0], VDEC_A_VSAT_CTRL+(0x200*decoder), &tmp);
val &= 0xFFFFFF00;
ret_val |= cx25821_i2c_write(&dev->i2c_bus[0], VDEC_A_VSAT_CTRL+(0x200*decoder), val | value);
mutex_unlock(&dev->lock);
return ret_val;
}
/////////////////////////////////////////////////////////////////////////////////////////
// Program the display sequence and monitor output.
//
int medusa_video_init(struct cx25821_dev *dev)
{
u32 value = 0, tmp = 0;
int ret_val = 0;
int i=0;
mutex_lock(&dev->lock);
_num_decoders = dev->_max_num_decoders;
// disable Auto source selection on all video decoders
value = cx25821_i2c_read(&dev->i2c_bus[0], MON_A_CTRL, &tmp);
value &= 0xFFFFF0FF;
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], MON_A_CTRL, value);
if (ret_val < 0)
{
mutex_unlock(&dev->lock);
return -EINVAL;
}
// Turn off Master source switch enable
value = cx25821_i2c_read(&dev->i2c_bus[0], MON_A_CTRL, &tmp);
value &= 0xFFFFFFDF;
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], MON_A_CTRL, value);
if (ret_val < 0)
{
mutex_unlock(&dev->lock);
return -EINVAL;
}
mutex_unlock(&dev->lock);
for (i=0; i < _num_decoders; i++)
{
medusa_set_decoderduration(dev, i, _display_field_cnt[i]);
}
mutex_lock(&dev->lock);
// Select monitor as DENC A input, power up the DAC
value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_AB_CTRL, &tmp);
value &= 0xFF70FF70;
value |= 0x00090008; // set en_active
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], DENC_AB_CTRL, value);
if (ret_val < 0)
{
mutex_unlock(&dev->lock);
return -EINVAL;
}
// enable input is VIP/656
value = cx25821_i2c_read(&dev->i2c_bus[0], BYP_AB_CTRL, &tmp);
value |= 0x00040100; // enable VIP
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], BYP_AB_CTRL, value);
if (ret_val < 0)
{
mutex_unlock(&dev->lock);
return -EINVAL;
}
// select AFE clock to output mode
value = cx25821_i2c_read(&dev->i2c_bus[0], AFE_AB_DIAG_CTRL, &tmp);
value &= 0x83FFFFFF;
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], AFE_AB_DIAG_CTRL, value | 0x10000000);
if (ret_val < 0)
{
mutex_unlock(&dev->lock);
return -EINVAL;
}
// Turn on all of the data out and control output pins.
value = cx25821_i2c_read(&dev->i2c_bus[0], PIN_OE_CTRL, &tmp);
value &= 0xFEF0FE00;
if (_num_decoders == MAX_DECODERS)
{
// Note: The octal board does not support control pins(bit16-19).
// These bits are ignored in the octal board.
value |= 0x010001F8; // disable VDEC A-C port, default to Mobilygen Interface
}
else
{
value |= 0x010F0108; // disable VDEC A-C port, default to Mobilygen Interface
}
value |= 7;
ret_val = cx25821_i2c_write(&dev->i2c_bus[0], PIN_OE_CTRL, value);
if (ret_val < 0)
{
mutex_unlock(&dev->lock);
return -EINVAL;
}
mutex_unlock(&dev->lock);
ret_val = medusa_set_videostandard(dev);
if (ret_val < 0)
{
mutex_unlock(&dev->lock);
return -EINVAL;
}
return 1;
}

View File

@ -0,0 +1,51 @@
/*
* Driver for the Conexant CX25821 PCIe bridge
*
* Copyright (C) 2009 Conexant Systems Inc.
* Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _MEDUSA_VIDEO_H
#define _MEDUSA_VIDEO_H
#include "cx25821-medusa-defines.h"
// Color control constants
#define VIDEO_PROCAMP_MIN 0
#define VIDEO_PROCAMP_MAX 10000
#define UNSIGNED_BYTE_MIN 0
#define UNSIGNED_BYTE_MAX 0xFF
#define SIGNED_BYTE_MIN -128
#define SIGNED_BYTE_MAX 127
// Default video color settings
#define SHARPNESS_DEFAULT 50
#define SATURATION_DEFAULT 5000
#define BRIGHTNESS_DEFAULT 6200
#define CONTRAST_DEFAULT 5000
#define HUE_DEFAULT 5000
unsigned short _num_decoders;
unsigned short _num_cameras;
unsigned int _video_standard;
int _display_field_cnt[MAX_DECODERS];
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,266 @@
/*
* Driver for the Conexant CX25821 PCIe bridge
*
* Copyright (C) 2009 Conexant Systems Inc.
* Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __ATHENA_SRAM_H__
#define __ATHENA_SRAM_H__
//#define RX_SRAM_START_SIZE = 0; // Start of reserved SRAM
#define VID_CMDS_SIZE 80 // Video CMDS size in bytes
#define AUDIO_CMDS_SIZE 80 // AUDIO CMDS size in bytes
#define MBIF_CMDS_SIZE 80 // MBIF CMDS size in bytes
//#define RX_SRAM_POOL_START_SIZE = 0; // Start of useable RX SRAM for buffers
#define VID_IQ_SIZE 64 // VID instruction queue size in bytes
#define MBIF_IQ_SIZE 64
#define AUDIO_IQ_SIZE 64 // AUD instruction queue size in bytes
#define VID_CDT_SIZE 64 // VID cluster descriptor table size in bytes
#define MBIF_CDT_SIZE 64 // MBIF/HBI cluster descriptor table size in bytes
#define AUDIO_CDT_SIZE 48 // AUD cluster descriptor table size in bytes
//#define RX_SRAM_POOL_FREE_SIZE = 16; // Start of available RX SRAM
//#define RX_SRAM_END_SIZE = 0; // End of RX SRAM
//#define TX_SRAM_POOL_START_SIZE = 0; // Start of transmit pool SRAM
//#define MSI_DATA_SIZE = 64; // Reserved (MSI Data, RISC working stora
#define VID_CLUSTER_SIZE 1440 // VID cluster data line
#define AUDIO_CLUSTER_SIZE 128 // AUDIO cluster data line
#define MBIF_CLUSTER_SIZE 1440 // MBIF/HBI cluster data line
//#define TX_SRAM_POOL_FREE_SIZE = 704; // Start of available TX SRAM
//#define TX_SRAM_END_SIZE = 0; // End of TX SRAM
// Receive SRAM
#define RX_SRAM_START 0x10000
#define VID_A_DOWN_CMDS 0x10000
#define VID_B_DOWN_CMDS 0x10050
#define VID_C_DOWN_CMDS 0x100A0
#define VID_D_DOWN_CMDS 0x100F0
#define VID_E_DOWN_CMDS 0x10140
#define VID_F_DOWN_CMDS 0x10190
#define VID_G_DOWN_CMDS 0x101E0
#define VID_H_DOWN_CMDS 0x10230
#define VID_A_UP_CMDS 0x10280
#define VID_B_UP_CMDS 0x102D0
#define VID_C_UP_CMDS 0x10320
#define VID_D_UP_CMDS 0x10370
#define VID_E_UP_CMDS 0x103C0
#define VID_F_UP_CMDS 0x10410
#define VID_I_UP_CMDS 0x10460
#define VID_J_UP_CMDS 0x104B0
#define AUD_A_DOWN_CMDS 0x10500
#define AUD_B_DOWN_CMDS 0x10550
#define AUD_C_DOWN_CMDS 0x105A0
#define AUD_D_DOWN_CMDS 0x105F0
#define AUD_A_UP_CMDS 0x10640
#define AUD_B_UP_CMDS 0x10690
#define AUD_C_UP_CMDS 0x106E0
#define AUD_E_UP_CMDS 0x10730
#define MBIF_A_DOWN_CMDS 0x10780
#define MBIF_B_DOWN_CMDS 0x107D0
#define DMA_SCRATCH_PAD 0x10820 // Scratch pad area from 0x10820 to 0x10B40
//#define RX_SRAM_POOL_START = 0x105B0;
#define VID_A_IQ 0x11000
#define VID_B_IQ 0x11040
#define VID_C_IQ 0x11080
#define VID_D_IQ 0x110C0
#define VID_E_IQ 0x11100
#define VID_F_IQ 0x11140
#define VID_G_IQ 0x11180
#define VID_H_IQ 0x111C0
#define VID_I_IQ 0x11200
#define VID_J_IQ 0x11240
#define AUD_A_IQ 0x11280
#define AUD_B_IQ 0x112C0
#define AUD_C_IQ 0x11300
#define AUD_D_IQ 0x11340
#define AUD_E_IQ 0x11380
#define MBIF_A_IQ 0x11000
#define MBIF_B_IQ 0x110C0
#define VID_A_CDT 0x10C00
#define VID_B_CDT 0x10C40
#define VID_C_CDT 0x10C80
#define VID_D_CDT 0x10CC0
#define VID_E_CDT 0x10D00
#define VID_F_CDT 0x10D40
#define VID_G_CDT 0x10D80
#define VID_H_CDT 0x10DC0
#define VID_I_CDT 0x10E00
#define VID_J_CDT 0x10E40
#define AUD_A_CDT 0x10E80
#define AUD_B_CDT 0x10EB0
#define AUD_C_CDT 0x10EE0
#define AUD_D_CDT 0x10F10
#define AUD_E_CDT 0x10F40
#define MBIF_A_CDT 0x10C00
#define MBIF_B_CDT 0x10CC0
// Cluster Buffer for RX
#define VID_A_UP_CLUSTER_1 0x11400
#define VID_A_UP_CLUSTER_2 0x119A0
#define VID_A_UP_CLUSTER_3 0x11F40
#define VID_A_UP_CLUSTER_4 0x124E0
#define VID_B_UP_CLUSTER_1 0x12A80
#define VID_B_UP_CLUSTER_2 0x13020
#define VID_B_UP_CLUSTER_3 0x135C0
#define VID_B_UP_CLUSTER_4 0x13B60
#define VID_C_UP_CLUSTER_1 0x14100
#define VID_C_UP_CLUSTER_2 0x146A0
#define VID_C_UP_CLUSTER_3 0x14C40
#define VID_C_UP_CLUSTER_4 0x151E0
#define VID_D_UP_CLUSTER_1 0x15780
#define VID_D_UP_CLUSTER_2 0x15D20
#define VID_D_UP_CLUSTER_3 0x162C0
#define VID_D_UP_CLUSTER_4 0x16860
#define VID_E_UP_CLUSTER_1 0x16E00
#define VID_E_UP_CLUSTER_2 0x173A0
#define VID_E_UP_CLUSTER_3 0x17940
#define VID_E_UP_CLUSTER_4 0x17EE0
#define VID_F_UP_CLUSTER_1 0x18480
#define VID_F_UP_CLUSTER_2 0x18A20
#define VID_F_UP_CLUSTER_3 0x18FC0
#define VID_F_UP_CLUSTER_4 0x19560
#define VID_I_UP_CLUSTER_1 0x19B00
#define VID_I_UP_CLUSTER_2 0x1A0A0
#define VID_I_UP_CLUSTER_3 0x1A640
#define VID_I_UP_CLUSTER_4 0x1ABE0
#define VID_J_UP_CLUSTER_1 0x1B180
#define VID_J_UP_CLUSTER_2 0x1B720
#define VID_J_UP_CLUSTER_3 0x1BCC0
#define VID_J_UP_CLUSTER_4 0x1C260
#define AUD_A_UP_CLUSTER_1 0x1C800
#define AUD_A_UP_CLUSTER_2 0x1C880
#define AUD_A_UP_CLUSTER_3 0x1C900
#define AUD_B_UP_CLUSTER_1 0x1C980
#define AUD_B_UP_CLUSTER_2 0x1CA00
#define AUD_B_UP_CLUSTER_3 0x1CA80
#define AUD_C_UP_CLUSTER_1 0x1CB00
#define AUD_C_UP_CLUSTER_2 0x1CB80
#define AUD_C_UP_CLUSTER_3 0x1CC00
#define AUD_E_UP_CLUSTER_1 0x1CC80
#define AUD_E_UP_CLUSTER_2 0x1CD00
#define AUD_E_UP_CLUSTER_3 0x1CD80
#define RX_SRAM_POOL_FREE 0x1CE00
#define RX_SRAM_END 0x1D000
// Free Receive SRAM 144 Bytes
// Transmit SRAM
#define TX_SRAM_POOL_START 0x00000
#define VID_A_DOWN_CLUSTER_1 0x00040
#define VID_A_DOWN_CLUSTER_2 0x005E0
#define VID_A_DOWN_CLUSTER_3 0x00B80
#define VID_A_DOWN_CLUSTER_4 0x01120
#define VID_B_DOWN_CLUSTER_1 0x016C0
#define VID_B_DOWN_CLUSTER_2 0x01C60
#define VID_B_DOWN_CLUSTER_3 0x02200
#define VID_B_DOWN_CLUSTER_4 0x027A0
#define VID_C_DOWN_CLUSTER_1 0x02D40
#define VID_C_DOWN_CLUSTER_2 0x032E0
#define VID_C_DOWN_CLUSTER_3 0x03880
#define VID_C_DOWN_CLUSTER_4 0x03E20
#define VID_D_DOWN_CLUSTER_1 0x043C0
#define VID_D_DOWN_CLUSTER_2 0x04960
#define VID_D_DOWN_CLUSTER_3 0x04F00
#define VID_D_DOWN_CLUSTER_4 0x054A0
#define VID_E_DOWN_CLUSTER_1 0x05a40
#define VID_E_DOWN_CLUSTER_2 0x05FE0
#define VID_E_DOWN_CLUSTER_3 0x06580
#define VID_E_DOWN_CLUSTER_4 0x06B20
#define VID_F_DOWN_CLUSTER_1 0x070C0
#define VID_F_DOWN_CLUSTER_2 0x07660
#define VID_F_DOWN_CLUSTER_3 0x07C00
#define VID_F_DOWN_CLUSTER_4 0x081A0
#define VID_G_DOWN_CLUSTER_1 0x08740
#define VID_G_DOWN_CLUSTER_2 0x08CE0
#define VID_G_DOWN_CLUSTER_3 0x09280
#define VID_G_DOWN_CLUSTER_4 0x09820
#define VID_H_DOWN_CLUSTER_1 0x09DC0
#define VID_H_DOWN_CLUSTER_2 0x0A360
#define VID_H_DOWN_CLUSTER_3 0x0A900
#define VID_H_DOWN_CLUSTER_4 0x0AEA0
#define AUD_A_DOWN_CLUSTER_1 0x0B500
#define AUD_A_DOWN_CLUSTER_2 0x0B580
#define AUD_A_DOWN_CLUSTER_3 0x0B600
#define AUD_B_DOWN_CLUSTER_1 0x0B680
#define AUD_B_DOWN_CLUSTER_2 0x0B700
#define AUD_B_DOWN_CLUSTER_3 0x0B780
#define AUD_C_DOWN_CLUSTER_1 0x0B800
#define AUD_C_DOWN_CLUSTER_2 0x0B880
#define AUD_C_DOWN_CLUSTER_3 0x0B900
#define AUD_D_DOWN_CLUSTER_1 0x0B980
#define AUD_D_DOWN_CLUSTER_2 0x0BA00
#define AUD_D_DOWN_CLUSTER_3 0x0BA80
#define TX_SRAM_POOL_FREE 0x0BB00
#define TX_SRAM_END 0x0C000
#define BYTES_TO_DWORDS(bcount) ((bcount) >> 2)
#define BYTES_TO_QWORDS(bcount) ((bcount) >> 3)
#define BYTES_TO_OWORDS(bcount) ((bcount) >> 4)
#define VID_IQ_SIZE_DW BYTES_TO_DWORDS(VID_IQ_SIZE)
#define VID_CDT_SIZE_QW BYTES_TO_QWORDS(VID_CDT_SIZE)
#define VID_CLUSTER_SIZE_OW BYTES_TO_OWORDS(VID_CLUSTER_SIZE)
#define AUDIO_IQ_SIZE_DW BYTES_TO_DWORDS(AUDIO_IQ_SIZE)
#define AUDIO_CDT_SIZE_QW BYTES_TO_QWORDS(AUDIO_CDT_SIZE)
#define AUDIO_CLUSTER_SIZE_QW BYTES_TO_QWORDS(AUDIO_CLUSTER_SIZE)
#define MBIF_IQ_SIZE_DW BYTES_TO_DWORDS(MBIF_IQ_SIZE)
#define MBIF_CDT_SIZE_QW BYTES_TO_QWORDS(MBIF_CDT_SIZE)
#define MBIF_CLUSTER_SIZE_OW BYTES_TO_OWORDS(MBIF_CLUSTER_SIZE)
#endif

View File

@ -0,0 +1,847 @@
/*
* Driver for the Conexant CX25821 PCIe bridge
*
* Copyright (C) 2009 Conexant Systems Inc.
* Authors <hiep.huynh@conexant.com>, <shu.lin@conexant.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "cx25821-video.h"
#include "cx25821-video-upstream-ch2.h"
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/syscalls.h>
#include <linux/file.h>
#include <linux/fcntl.h>
#include <asm/uaccess.h>
MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards");
MODULE_AUTHOR("Hiep Huynh <hiep.huynh@conexant.com>");
MODULE_LICENSE("GPL");
static int _intr_msk = FLD_VID_SRC_RISC1 | FLD_VID_SRC_UF | FLD_VID_SRC_SYNC | FLD_VID_SRC_OPC_ERR;
static __le32 *cx25821_update_riscprogram_ch2( struct cx25821_dev *dev,
__le32 *rp, unsigned int offset, unsigned int bpl,
u32 sync_line, unsigned int lines, int fifo_enable, int field_type)
{
unsigned int line, i;
int dist_betwn_starts = bpl * 2;
*(rp++) = cpu_to_le32(RISC_RESYNC | sync_line);
if( USE_RISC_NOOP_VIDEO )
{
for( i = 0; i < NUM_NO_OPS; i++ )
{
*(rp++) = cpu_to_le32(RISC_NOOP);
}
}
/* scan lines */
for (line = 0; line < lines; line++)
{
*(rp++) = cpu_to_le32(RISC_READ|RISC_SOL|RISC_EOL|bpl);
*(rp++) = cpu_to_le32(dev->_data_buf_phys_addr_ch2+offset);
*(rp++) = cpu_to_le32(0); /* bits 63-32 */
if ( (lines <= NTSC_FIELD_HEIGHT) || (line < (NTSC_FIELD_HEIGHT-1)) || !(dev->_isNTSC_ch2) )
{
offset += dist_betwn_starts;
}
}
return rp;
}
static __le32 *cx25821_risc_field_upstream_ch2( struct cx25821_dev *dev,
__le32 *rp,
dma_addr_t databuf_phys_addr,
unsigned int offset, u32 sync_line, unsigned int bpl,
unsigned int lines, int fifo_enable, int field_type)
{
unsigned int line, i;
struct sram_channel *sram_ch = &dev->sram_channels[dev->_channel2_upstream_select];
int dist_betwn_starts = bpl * 2;
/* sync instruction */
if (sync_line != NO_SYNC_LINE)
{
*(rp++) = cpu_to_le32(RISC_RESYNC | sync_line);
}
if( USE_RISC_NOOP_VIDEO )
{
for( i = 0; i < NUM_NO_OPS; i++ )
{
*(rp++) = cpu_to_le32(RISC_NOOP);
}
}
/* scan lines */
for (line = 0; line < lines; line++)
{
*(rp++) = cpu_to_le32(RISC_READ|RISC_SOL|RISC_EOL|bpl);
*(rp++) = cpu_to_le32(databuf_phys_addr+offset);
*(rp++) = cpu_to_le32(0); /* bits 63-32 */
if ( (lines <= NTSC_FIELD_HEIGHT) || (line < (NTSC_FIELD_HEIGHT-1)) || !(dev->_isNTSC_ch2) )
{
offset += dist_betwn_starts;
}
// check if we need to enable the FIFO after the first 4 lines
// For the upstream video channel, the risc engine will enable the FIFO.
if ( fifo_enable && line == 3 )
{
*(rp++) = RISC_WRITECR;
*(rp++) = sram_ch->dma_ctl;
*(rp++) = FLD_VID_FIFO_EN;
*(rp++) = 0x00000001;
}
}
return rp;
}
int cx25821_risc_buffer_upstream_ch2( struct cx25821_dev *dev, struct pci_dev *pci,
unsigned int top_offset,
unsigned int bpl, unsigned int lines)
{
__le32 *rp;
int fifo_enable = 0;
int singlefield_lines = lines >> 1; //get line count for single field
int odd_num_lines = singlefield_lines;
int frame = 0;
int frame_size = 0;
int databuf_offset = 0;
int risc_program_size = 0;
int risc_flag = RISC_CNT_RESET;
unsigned int bottom_offset = bpl;
dma_addr_t risc_phys_jump_addr;
if( dev->_isNTSC_ch2 )
{
odd_num_lines = singlefield_lines + 1;
risc_program_size = FRAME1_VID_PROG_SIZE;
frame_size = (bpl == Y411_LINE_SZ) ? FRAME_SIZE_NTSC_Y411 : FRAME_SIZE_NTSC_Y422;
}
else
{
risc_program_size = PAL_VID_PROG_SIZE;
frame_size = (bpl == Y411_LINE_SZ) ? FRAME_SIZE_PAL_Y411 : FRAME_SIZE_PAL_Y422;
}
/* Virtual address of Risc buffer program */
rp = dev->_dma_virt_addr_ch2;
for( frame = 0; frame < NUM_FRAMES; frame++ )
{
databuf_offset = frame_size * frame;
if (UNSET != top_offset)
{
fifo_enable = (frame == 0) ? FIFO_ENABLE : FIFO_DISABLE;
rp = cx25821_risc_field_upstream_ch2(dev, rp, dev->_data_buf_phys_addr_ch2 + databuf_offset, top_offset, 0, bpl, odd_num_lines, fifo_enable, ODD_FIELD);
}
fifo_enable = FIFO_DISABLE;
//Even field
rp = cx25821_risc_field_upstream_ch2(dev, rp, dev->_data_buf_phys_addr_ch2 + databuf_offset, bottom_offset, 0x200, bpl, singlefield_lines, fifo_enable, EVEN_FIELD);
if( frame == 0 )
{
risc_flag = RISC_CNT_RESET;
risc_phys_jump_addr = dev->_dma_phys_start_addr_ch2 + risc_program_size;
}
else
{
risc_flag = RISC_CNT_INC;
risc_phys_jump_addr = dev->_dma_phys_start_addr_ch2;
}
// Loop to 2ndFrameRISC or to Start of Risc program & generate IRQ
*(rp++) = cpu_to_le32(RISC_JUMP|RISC_IRQ1|risc_flag);
*(rp++) = cpu_to_le32(risc_phys_jump_addr);
*(rp++) = cpu_to_le32(0);
}
return 0;
}
void cx25821_stop_upstream_video_ch2(struct cx25821_dev *dev)
{
struct sram_channel *sram_ch = &dev->sram_channels[VID_UPSTREAM_SRAM_CHANNEL_J];
u32 tmp = 0;
if( !dev->_is_running_ch2 )
{
printk("cx25821: No video file is currently running so return!\n");
return;
}
//Disable RISC interrupts
tmp = cx_read( sram_ch->int_msk );
cx_write( sram_ch->int_msk, tmp & ~_intr_msk);
//Turn OFF risc and fifo
tmp = cx_read( sram_ch->dma_ctl );
cx_write( sram_ch->dma_ctl, tmp & ~(FLD_VID_FIFO_EN | FLD_VID_RISC_EN) );
//Clear data buffer memory
if( dev->_data_buf_virt_addr_ch2 )
memset( dev->_data_buf_virt_addr_ch2, 0, dev->_data_buf_size_ch2 );
dev->_is_running_ch2 = 0;
dev->_is_first_frame_ch2 = 0;
dev->_frame_count_ch2 = 0;
dev->_file_status_ch2 = END_OF_FILE;
if( dev->_irq_queues_ch2 )
{
kfree(dev->_irq_queues_ch2);
dev->_irq_queues_ch2 = NULL;
}
if( dev->_filename_ch2 != NULL )
kfree(dev->_filename_ch2);
tmp = cx_read( VID_CH_MODE_SEL );
cx_write( VID_CH_MODE_SEL, tmp & 0xFFFFFE00);
}
void cx25821_free_mem_upstream_ch2(struct cx25821_dev *dev)
{
if( dev->_is_running_ch2 )
{
cx25821_stop_upstream_video_ch2(dev);
}
if (dev->_dma_virt_addr_ch2)
{
pci_free_consistent(dev->pci, dev->_risc_size_ch2, dev->_dma_virt_addr_ch2, dev->_dma_phys_addr_ch2);
dev->_dma_virt_addr_ch2 = NULL;
}
if (dev->_data_buf_virt_addr_ch2)
{
pci_free_consistent(dev->pci, dev->_data_buf_size_ch2, dev->_data_buf_virt_addr_ch2, dev->_data_buf_phys_addr_ch2);
dev->_data_buf_virt_addr_ch2 = NULL;
}
}
int cx25821_get_frame_ch2(struct cx25821_dev *dev, struct sram_channel *sram_ch )
{
struct file * myfile;
int frame_index_temp = dev->_frame_index_ch2;
int i = 0;
int line_size = (dev->_pixel_format_ch2 == PIXEL_FRMT_411) ? Y411_LINE_SZ : Y422_LINE_SZ;
int frame_size = 0;
int frame_offset = 0;
ssize_t vfs_read_retval = 0;
char mybuf[line_size];
loff_t file_offset;
loff_t pos;
mm_segment_t old_fs;
if( dev->_file_status_ch2 == END_OF_FILE )
return 0;
if( dev->_isNTSC_ch2 )
{
frame_size = (line_size == Y411_LINE_SZ) ? FRAME_SIZE_NTSC_Y411 : FRAME_SIZE_NTSC_Y422;
}
else
{
frame_size = (line_size == Y411_LINE_SZ) ? FRAME_SIZE_PAL_Y411 : FRAME_SIZE_PAL_Y422;
}
frame_offset = (frame_index_temp > 0) ? frame_size : 0;
file_offset = dev->_frame_count_ch2 * frame_size;
myfile = filp_open( dev->_filename_ch2, O_RDONLY | O_LARGEFILE, 0 );
if (IS_ERR(myfile))
{
const int open_errno = -PTR_ERR(myfile);
printk("%s(): ERROR opening file(%s) with errno = %d! \n", __func__, dev->_filename_ch2, open_errno);
return PTR_ERR(myfile);
}
else
{
if( !(myfile->f_op) )
{
printk("%s: File has no file operations registered!", __func__);
filp_close(myfile, NULL);
return -EIO;
}
if( !myfile->f_op->read )
{
printk("%s: File has no READ operations registered!", __func__);
filp_close(myfile, NULL);
return -EIO;
}
pos = myfile->f_pos;
old_fs = get_fs();
set_fs(KERNEL_DS);
for( i = 0; i < dev->_lines_count_ch2; i++ )
{
pos = file_offset;
vfs_read_retval = vfs_read(myfile, mybuf, line_size, &pos);
if( vfs_read_retval > 0 && vfs_read_retval == line_size && dev->_data_buf_virt_addr_ch2 != NULL )
{
memcpy( (void*)(dev->_data_buf_virt_addr_ch2+frame_offset/4), mybuf, vfs_read_retval);
}
file_offset += vfs_read_retval;
frame_offset += vfs_read_retval;
if( vfs_read_retval < line_size )
{
printk(KERN_INFO "Done: exit %s() since no more bytes to read from Video file.\n", __func__ );
break;
}
}
if( i > 0 )
dev->_frame_count_ch2++;
dev->_file_status_ch2 = (vfs_read_retval == line_size) ? IN_PROGRESS : END_OF_FILE;
set_fs(old_fs);
filp_close(myfile, NULL);
}
return 0;
}
static void cx25821_vidups_handler_ch2(struct work_struct *work)
{
struct cx25821_dev *dev = container_of(work, struct cx25821_dev, _irq_work_entry_ch2);
if( !dev )
{
printk("ERROR %s(): since container_of(work_struct) FAILED! \n", __func__ );
return;
}
cx25821_get_frame_ch2( dev, &dev->sram_channels[dev->_channel2_upstream_select] );
}
int cx25821_openfile_ch2(struct cx25821_dev *dev, struct sram_channel *sram_ch)
{
struct file * myfile;
int i = 0, j = 0;
int line_size = (dev->_pixel_format_ch2 == PIXEL_FRMT_411) ? Y411_LINE_SZ : Y422_LINE_SZ;
ssize_t vfs_read_retval = 0;
char mybuf[line_size];
loff_t pos;
loff_t offset = (unsigned long)0;
mm_segment_t old_fs;
myfile = filp_open( dev->_filename_ch2, O_RDONLY | O_LARGEFILE, 0 );
if (IS_ERR(myfile))
{
const int open_errno = -PTR_ERR(myfile);
printk("%s(): ERROR opening file(%s) with errno = %d! \n", __func__, dev->_filename_ch2, open_errno);
return PTR_ERR(myfile);
}
else
{
if( !(myfile->f_op) )
{
printk("%s: File has no file operations registered!", __func__);
filp_close(myfile, NULL);
return -EIO;
}
if( !myfile->f_op->read )
{
printk("%s: File has no READ operations registered! Returning.", __func__);
filp_close(myfile, NULL);
return -EIO;
}
pos = myfile->f_pos;
old_fs = get_fs();
set_fs(KERNEL_DS);
for( j = 0; j < NUM_FRAMES; j++ )
{
for( i = 0; i < dev->_lines_count_ch2; i++ )
{
pos = offset;
vfs_read_retval = vfs_read(myfile, mybuf, line_size, &pos);
if( vfs_read_retval > 0 && vfs_read_retval == line_size && dev->_data_buf_virt_addr_ch2 != NULL )
{
memcpy( (void*)(dev->_data_buf_virt_addr_ch2+offset/4), mybuf, vfs_read_retval);
}
offset += vfs_read_retval;
if( vfs_read_retval < line_size )
{
printk(KERN_INFO "Done: exit %s() since no more bytes to read from Video file.\n", __func__ );
break;
}
}
if( i > 0 )
dev->_frame_count_ch2++;
if( vfs_read_retval < line_size )
{
break;
}
}
dev->_file_status_ch2 = (vfs_read_retval == line_size) ? IN_PROGRESS : END_OF_FILE;
set_fs(old_fs);
myfile->f_pos = 0;
filp_close(myfile, NULL);
}
return 0;
}
static int cx25821_upstream_buffer_prepare_ch2(struct cx25821_dev *dev,
struct sram_channel *sram_ch,
int bpl)
{
int ret = 0;
dma_addr_t dma_addr;
dma_addr_t data_dma_addr;
if( dev->_dma_virt_addr_ch2 != NULL )
{
pci_free_consistent(dev->pci, dev->upstream_riscbuf_size_ch2, dev->_dma_virt_addr_ch2, dev->_dma_phys_addr_ch2);
}
dev->_dma_virt_addr_ch2 = pci_alloc_consistent(dev->pci, dev->upstream_riscbuf_size_ch2, &dma_addr);
dev->_dma_virt_start_addr_ch2 = dev->_dma_virt_addr_ch2;
dev->_dma_phys_start_addr_ch2 = dma_addr;
dev->_dma_phys_addr_ch2 = dma_addr;
dev->_risc_size_ch2 = dev->upstream_riscbuf_size_ch2;
if (!dev->_dma_virt_addr_ch2)
{
printk("cx25821: FAILED to allocate memory for Risc buffer! Returning.\n");
return -ENOMEM;
}
//Iniitize at this address until n bytes to 0
memset( dev->_dma_virt_addr_ch2, 0, dev->_risc_size_ch2 );
if( dev->_data_buf_virt_addr_ch2 != NULL )
{
pci_free_consistent(dev->pci, dev->upstream_databuf_size_ch2, dev->_data_buf_virt_addr_ch2, dev->_data_buf_phys_addr_ch2);
}
//For Video Data buffer allocation
dev->_data_buf_virt_addr_ch2 = pci_alloc_consistent(dev->pci, dev->upstream_databuf_size_ch2, &data_dma_addr);
dev->_data_buf_phys_addr_ch2 = data_dma_addr;
dev->_data_buf_size_ch2 = dev->upstream_databuf_size_ch2;
if (!dev->_data_buf_virt_addr_ch2)
{
printk("cx25821: FAILED to allocate memory for data buffer! Returning.\n");
return -ENOMEM;
}
//Initialize at this address until n bytes to 0
memset( dev->_data_buf_virt_addr_ch2, 0, dev->_data_buf_size_ch2 );
ret = cx25821_openfile_ch2(dev, sram_ch);
if( ret < 0 )
return ret;
//Creating RISC programs
ret = cx25821_risc_buffer_upstream_ch2(dev, dev->pci, 0, bpl, dev->_lines_count_ch2 );
if (ret < 0)
{
printk(KERN_INFO "cx25821: Failed creating Video Upstream Risc programs! \n");
goto error;
}
return 0;
error:
return ret;
}
int cx25821_video_upstream_irq_ch2(struct cx25821_dev *dev, int chan_num, u32 status)
{
u32 int_msk_tmp;
struct sram_channel *channel = &dev->sram_channels[chan_num];
int singlefield_lines = NTSC_FIELD_HEIGHT;
int line_size_in_bytes = Y422_LINE_SZ;
int odd_risc_prog_size = 0;
dma_addr_t risc_phys_jump_addr;
__le32 * rp;
if (status & FLD_VID_SRC_RISC1)
{
// We should only process one program per call
u32 prog_cnt = cx_read( channel->gpcnt );
//Since we've identified our IRQ, clear our bits from the interrupt mask and interrupt status registers
int_msk_tmp = cx_read(channel->int_msk);
cx_write(channel->int_msk, int_msk_tmp & ~_intr_msk);
cx_write( channel->int_stat, _intr_msk );
spin_lock(&dev->slock);
dev->_frame_index_ch2 = prog_cnt;
queue_work(dev->_irq_queues_ch2, &dev->_irq_work_entry_ch2);
if ( dev->_is_first_frame_ch2 )
{
dev->_is_first_frame_ch2 = 0;
if( dev->_isNTSC_ch2 )
{
singlefield_lines += 1;
odd_risc_prog_size = ODD_FLD_NTSC_PROG_SIZE;
}
else
{
singlefield_lines = PAL_FIELD_HEIGHT;
odd_risc_prog_size = ODD_FLD_PAL_PROG_SIZE;
}
if( dev->_dma_virt_start_addr_ch2 != NULL )
{
line_size_in_bytes = (dev->_pixel_format_ch2 == PIXEL_FRMT_411) ? Y411_LINE_SZ : Y422_LINE_SZ;
risc_phys_jump_addr = dev->_dma_phys_start_addr_ch2 + odd_risc_prog_size;
rp = cx25821_update_riscprogram_ch2(dev, dev->_dma_virt_start_addr_ch2, TOP_OFFSET, line_size_in_bytes, 0x0, singlefield_lines, FIFO_DISABLE, ODD_FIELD);
// Jump to Even Risc program of 1st Frame
*(rp++) = cpu_to_le32(RISC_JUMP);
*(rp++) = cpu_to_le32(risc_phys_jump_addr);
*(rp++) = cpu_to_le32(0);
}
}
spin_unlock(&dev->slock);
}
if( dev->_file_status_ch2 == END_OF_FILE )
{
printk("cx25821: EOF Channel 2 Framecount = %d\n", dev->_frame_count_ch2 );
return -1;
}
//ElSE, set the interrupt mask register, re-enable irq.
int_msk_tmp = cx_read( channel->int_msk );
cx_write( channel->int_msk, int_msk_tmp |= _intr_msk );
return 0;
}
static irqreturn_t cx25821_upstream_irq_ch2(int irq, void *dev_id)
{
struct cx25821_dev *dev = dev_id;
u32 msk_stat, vid_status;
int handled = 0;
int channel_num = 0;
struct sram_channel *sram_ch;
if( !dev )
return -1;
channel_num = VID_UPSTREAM_SRAM_CHANNEL_J;
sram_ch = &dev->sram_channels[channel_num];
msk_stat = cx_read(sram_ch->int_mstat);
vid_status = cx_read(sram_ch->int_stat);
// Only deal with our interrupt
if(vid_status)
{
handled = cx25821_video_upstream_irq_ch2(dev, channel_num, vid_status);
}
if( handled < 0 )
{
cx25821_stop_upstream_video_ch2(dev);
}
else
{
handled += handled;
}
return IRQ_RETVAL(handled);
}
static void cx25821_set_pixelengine_ch2(struct cx25821_dev *dev, struct sram_channel *ch, int pix_format)
{
int width = WIDTH_D1;
int height = dev->_lines_count_ch2;
int num_lines, odd_num_lines;
u32 value;
int vip_mode = PIXEL_ENGINE_VIP1;
value = ( (pix_format & 0x3) << 12 ) | ( vip_mode & 0x7 );
value &= 0xFFFFFFEF;
value |= dev->_isNTSC_ch2 ? 0 : 0x10;
cx_write( ch->vid_fmt_ctl, value );
// set number of active pixels in each line. Default is 720 pixels in both NTSC and PAL format
cx_write( ch->vid_active_ctl1, width );
num_lines = (height / 2) & 0x3FF;
odd_num_lines = num_lines;
if(dev->_isNTSC_ch2)
{
odd_num_lines += 1;
}
value = (num_lines << 16) | odd_num_lines;
// set number of active lines in field 0 (top) and field 1 (bottom)
cx_write( ch->vid_active_ctl2, value );
cx_write( ch->vid_cdt_size, VID_CDT_SIZE >> 3 );
}
int cx25821_start_video_dma_upstream_ch2(struct cx25821_dev *dev,
struct sram_channel *sram_ch)
{
u32 tmp = 0;
int err = 0;
// 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface for channel A-C
tmp = cx_read( VID_CH_MODE_SEL );
cx_write( VID_CH_MODE_SEL, tmp | 0x1B0001FF);
// Set the physical start address of the RISC program in the initial program counter(IPC) member of the cmds.
cx_write(sram_ch->cmds_start + 0, dev->_dma_phys_addr_ch2);
cx_write(sram_ch->cmds_start + 4, 0); /* Risc IPC High 64 bits 63-32 */
/* reset counter */
cx_write(sram_ch->gpcnt_ctl, 3);
// Clear our bits from the interrupt status register.
cx_write( sram_ch->int_stat, _intr_msk );
//Set the interrupt mask register, enable irq.
cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << sram_ch->irq_bit));
tmp = cx_read( sram_ch->int_msk );
cx_write( sram_ch->int_msk, tmp |= _intr_msk );
err = request_irq(dev->pci->irq, cx25821_upstream_irq_ch2, IRQF_SHARED | IRQF_DISABLED, dev->name, dev);
if (err < 0)
{
printk(KERN_ERR "%s: can't get upstream IRQ %d\n", dev->name, dev->pci->irq);
goto fail_irq;
}
// Start the DMA engine
tmp = cx_read( sram_ch->dma_ctl );
cx_set( sram_ch->dma_ctl, tmp | FLD_VID_RISC_EN );
dev->_is_running_ch2 = 1;
dev->_is_first_frame_ch2 = 1;
return 0;
fail_irq:
cx25821_dev_unregister(dev);
return err;
}
int cx25821_vidupstream_init_ch2(struct cx25821_dev *dev, int channel_select, int pixel_format)
{
struct sram_channel *sram_ch;
u32 tmp;
int retval = 0;
int err = 0;
int data_frame_size = 0;
int risc_buffer_size = 0;
int str_length = 0;
if( dev->_is_running_ch2 )
{
printk("Video Channel is still running so return!\n");
return 0;
}
dev->_channel2_upstream_select = channel_select;
sram_ch = &dev->sram_channels[channel_select];
INIT_WORK(&dev->_irq_work_entry_ch2, cx25821_vidups_handler_ch2);
dev->_irq_queues_ch2 = create_singlethread_workqueue("cx25821_workqueue2");
if(!dev->_irq_queues_ch2)
{
printk("cx25821: create_singlethread_workqueue() for Video FAILED!\n");
return -ENOMEM;
}
// 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface for channel A-C
tmp = cx_read( VID_CH_MODE_SEL );
cx_write( VID_CH_MODE_SEL, tmp | 0x1B0001FF);
dev->_is_running_ch2 = 0;
dev->_frame_count_ch2 = 0;
dev->_file_status_ch2 = RESET_STATUS;
dev->_lines_count_ch2 = dev->_isNTSC_ch2 ? 480 : 576;
dev->_pixel_format_ch2 = pixel_format;
dev->_line_size_ch2 = (dev->_pixel_format_ch2 == PIXEL_FRMT_422) ? (WIDTH_D1 * 2) : (WIDTH_D1 * 3) / 2;
data_frame_size = dev->_isNTSC_ch2 ? NTSC_DATA_BUF_SZ : PAL_DATA_BUF_SZ;
risc_buffer_size = dev->_isNTSC_ch2 ? NTSC_RISC_BUF_SIZE : PAL_RISC_BUF_SIZE;
if( dev->input_filename_ch2 )
{
str_length = strlen(dev->input_filename_ch2);
dev->_filename_ch2 = (char *) kmalloc(str_length + 1, GFP_KERNEL);
if( !dev->_filename_ch2 )
goto error;
memcpy(dev->_filename_ch2, dev->input_filename_ch2, str_length + 1);
}
else
{
str_length = strlen(dev->_defaultname_ch2);
dev->_filename_ch2 = (char *) kmalloc(str_length + 1, GFP_KERNEL);
if( !dev->_filename_ch2 )
goto error;
memcpy(dev->_filename_ch2, dev->_defaultname_ch2, str_length + 1);
}
//Default if filename is empty string
if( strcmp(dev->input_filename_ch2,"") == 0)
{
if( dev->_isNTSC_ch2 )
{
dev->_filename_ch2 = (dev->_pixel_format_ch2 == PIXEL_FRMT_411) ? "/root/vid411.yuv" : "/root/vidtest.yuv";
}
else
{
dev->_filename_ch2 = (dev->_pixel_format_ch2 == PIXEL_FRMT_411) ? "/root/pal411.yuv" : "/root/pal422.yuv";
}
}
retval = cx25821_sram_channel_setup_upstream(dev, sram_ch, dev->_line_size_ch2, 0);
/* setup fifo + format */
cx25821_set_pixelengine_ch2(dev, sram_ch, dev->_pixel_format_ch2);
dev->upstream_riscbuf_size_ch2 = risc_buffer_size * 2;
dev->upstream_databuf_size_ch2 = data_frame_size * 2;
//Allocating buffers and prepare RISC program
retval = cx25821_upstream_buffer_prepare_ch2(dev, sram_ch, dev->_line_size_ch2);
if (retval < 0)
{
printk(KERN_ERR "%s: Failed to set up Video upstream buffers!\n", dev->name);
goto error;
}
cx25821_start_video_dma_upstream_ch2(dev, sram_ch);
return 0;
error:
cx25821_dev_unregister(dev);
return err;
}

View File

@ -0,0 +1,107 @@
/*
* Driver for the Conexant CX25821 PCIe bridge
*
* Copyright (C) 2009 Conexant Systems Inc.
* Authors <hiep.huynh@conexant.com>, <shu.lin@conexant.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/mutex.h>
#include <linux/workqueue.h>
#define OPEN_FILE_1 0
#define NUM_PROGS 8
#define NUM_FRAMES 2
#define ODD_FIELD 0
#define EVEN_FIELD 1
#define TOP_OFFSET 0
#define FIFO_DISABLE 0
#define FIFO_ENABLE 1
#define TEST_FRAMES 5
#define END_OF_FILE 0
#define IN_PROGRESS 1
#define RESET_STATUS -1
#define NUM_NO_OPS 5
// PAL and NTSC line sizes and number of lines.
#define WIDTH_D1 720
#define NTSC_LINES_PER_FRAME 480
#define PAL_LINES_PER_FRAME 576
#define PAL_LINE_SZ 1440
#define Y422_LINE_SZ 1440
#define Y411_LINE_SZ 1080
#define NTSC_FIELD_HEIGHT 240
#define NTSC_ODD_FLD_LINES 241
#define PAL_FIELD_HEIGHT 288
#define FRAME_SIZE_NTSC_Y422 (NTSC_LINES_PER_FRAME * Y422_LINE_SZ)
#define FRAME_SIZE_NTSC_Y411 (NTSC_LINES_PER_FRAME * Y411_LINE_SZ)
#define FRAME_SIZE_PAL_Y422 (PAL_LINES_PER_FRAME * Y422_LINE_SZ)
#define FRAME_SIZE_PAL_Y411 (PAL_LINES_PER_FRAME * Y411_LINE_SZ)
#define NTSC_DATA_BUF_SZ (Y422_LINE_SZ * NTSC_LINES_PER_FRAME)
#define PAL_DATA_BUF_SZ (Y422_LINE_SZ * PAL_LINES_PER_FRAME)
#define RISC_WRITECR_INSTRUCTION_SIZE 16
#define RISC_SYNC_INSTRUCTION_SIZE 4
#define JUMP_INSTRUCTION_SIZE 12
#define MAXSIZE_NO_OPS 36
#define DWORD_SIZE 4
#define USE_RISC_NOOP_VIDEO 1
#ifdef USE_RISC_NOOP_VIDEO
#define PAL_US_VID_PROG_SIZE ((PAL_FIELD_HEIGHT) * 3 * DWORD_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \
RISC_SYNC_INSTRUCTION_SIZE + NUM_NO_OPS*DWORD_SIZE)
#define PAL_RISC_BUF_SIZE (2 * PAL_US_VID_PROG_SIZE)
#define PAL_VID_PROG_SIZE ((PAL_FIELD_HEIGHT*2) * 3 * DWORD_SIZE + 2*RISC_SYNC_INSTRUCTION_SIZE + \
RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE + 2*NUM_NO_OPS*DWORD_SIZE)
#define ODD_FLD_PAL_PROG_SIZE ((PAL_FIELD_HEIGHT) * 3 * DWORD_SIZE + RISC_SYNC_INSTRUCTION_SIZE + \
RISC_WRITECR_INSTRUCTION_SIZE + NUM_NO_OPS*DWORD_SIZE)
#define NTSC_US_VID_PROG_SIZE ((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \
JUMP_INSTRUCTION_SIZE + NUM_NO_OPS*DWORD_SIZE)
#define NTSC_RISC_BUF_SIZE (2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE))
#define FRAME1_VID_PROG_SIZE ((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) * 3 * DWORD_SIZE + 2*RISC_SYNC_INSTRUCTION_SIZE + \
RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE + 2*NUM_NO_OPS*DWORD_SIZE)
#define ODD_FLD_NTSC_PROG_SIZE ((NTSC_ODD_FLD_LINES) * 3 * DWORD_SIZE + RISC_SYNC_INSTRUCTION_SIZE + \
RISC_WRITECR_INSTRUCTION_SIZE + NUM_NO_OPS*DWORD_SIZE)
#endif
#ifndef USE_RISC_NOOP_VIDEO
#define PAL_US_VID_PROG_SIZE ((PAL_FIELD_HEIGHT + 1) * 3 * DWORD_SIZE + RISC_WRITECR_INSTRUCTION_SIZE )
#define PAL_RISC_BUF_SIZE ( 2 * (RISC_SYNC_INSTRUCTION_SIZE + PAL_US_VID_PROG_SIZE) )
#define PAL_VID_PROG_SIZE ((PAL_FIELD_HEIGHT*2) * 3 * DWORD_SIZE + 2*RISC_SYNC_INSTRUCTION_SIZE + \
RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE )
#define ODD_FLD_PAL_PROG_SIZE ((PAL_FIELD_HEIGHT) * 3 * DWORD_SIZE + RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE )
#define ODD_FLD_NTSC_PROG_SIZE ((NTSC_ODD_FLD_LINES) * 3 * DWORD_SIZE + RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE )
#define NTSC_US_VID_PROG_SIZE ((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE)
#define NTSC_RISC_BUF_SIZE (2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE) )
#define FRAME1_VID_PROG_SIZE ((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) * 3 * DWORD_SIZE + 2*RISC_SYNC_INSTRUCTION_SIZE + \
RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE )
#endif

View File

@ -0,0 +1,923 @@
/*
* Driver for the Conexant CX25821 PCIe bridge
*
* Copyright (C) 2009 Conexant Systems Inc.
* Authors <hiep.huynh@conexant.com>, <shu.lin@conexant.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "cx25821-video.h"
#include "cx25821-video-upstream.h"
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/syscalls.h>
#include <linux/file.h>
#include <linux/fcntl.h>
#include <asm/uaccess.h>
MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards");
MODULE_AUTHOR("Hiep Huynh <hiep.huynh@conexant.com>");
MODULE_LICENSE("GPL");
static int _intr_msk = FLD_VID_SRC_RISC1 | FLD_VID_SRC_UF | FLD_VID_SRC_SYNC | FLD_VID_SRC_OPC_ERR;
int cx25821_sram_channel_setup_upstream(struct cx25821_dev *dev,
struct sram_channel *ch,
unsigned int bpl, u32 risc)
{
unsigned int i, lines;
u32 cdt;
if (ch->cmds_start == 0) {
cx_write(ch->ptr1_reg, 0);
cx_write(ch->ptr2_reg, 0);
cx_write(ch->cnt2_reg, 0);
cx_write(ch->cnt1_reg, 0);
return 0;
}
bpl = (bpl + 7) & ~7; /* alignment */
cdt = ch->cdt;
lines = ch->fifo_size / bpl;
if (lines > 4)
{
lines = 4;
}
BUG_ON(lines < 2);
/* write CDT */
for (i = 0; i < lines; i++) {
cx_write(cdt + 16*i, ch->fifo_start + bpl*i);
cx_write(cdt + 16*i + 4, 0);
cx_write(cdt + 16*i + 8, 0);
cx_write(cdt + 16*i + 12, 0);
}
/* write CMDS */
cx_write(ch->cmds_start + 0, risc);
cx_write(ch->cmds_start + 4, 0);
cx_write(ch->cmds_start + 8, cdt);
cx_write(ch->cmds_start + 12, (lines*16) >> 3);
cx_write(ch->cmds_start + 16, ch->ctrl_start);
cx_write(ch->cmds_start + 20, VID_IQ_SIZE_DW);
for (i = 24; i < 80; i += 4)
cx_write(ch->cmds_start + i, 0);
/* fill registers */
cx_write(ch->ptr1_reg, ch->fifo_start);
cx_write(ch->ptr2_reg, cdt);
cx_write(ch->cnt2_reg, (lines*16) >> 3);
cx_write(ch->cnt1_reg, (bpl >> 3) - 1);
return 0;
}
static __le32 *cx25821_update_riscprogram( struct cx25821_dev *dev,
__le32 *rp, unsigned int offset, unsigned int bpl,
u32 sync_line, unsigned int lines, int fifo_enable, int field_type)
{
unsigned int line, i;
int dist_betwn_starts = bpl * 2;
*(rp++) = cpu_to_le32(RISC_RESYNC | sync_line);
if( USE_RISC_NOOP_VIDEO )
{
for( i = 0; i < NUM_NO_OPS; i++ )
{
*(rp++) = cpu_to_le32(RISC_NOOP);
}
}
/* scan lines */
for (line = 0; line < lines; line++)
{
*(rp++) = cpu_to_le32(RISC_READ|RISC_SOL|RISC_EOL|bpl);
*(rp++) = cpu_to_le32(dev->_data_buf_phys_addr+offset);
*(rp++) = cpu_to_le32(0); /* bits 63-32 */
if ( (lines <= NTSC_FIELD_HEIGHT) || (line < (NTSC_FIELD_HEIGHT-1)) || !(dev->_isNTSC) )
{
offset += dist_betwn_starts;
}
}
return rp;
}
static __le32 *cx25821_risc_field_upstream( struct cx25821_dev *dev, __le32 *rp,
dma_addr_t databuf_phys_addr,
unsigned int offset, u32 sync_line,
unsigned int bpl, unsigned int lines, int fifo_enable, int field_type)
{
unsigned int line, i;
struct sram_channel *sram_ch = &dev->sram_channels[dev->_channel_upstream_select];
int dist_betwn_starts = bpl * 2;
/* sync instruction */
if (sync_line != NO_SYNC_LINE)
{
*(rp++) = cpu_to_le32(RISC_RESYNC | sync_line);
}
if( USE_RISC_NOOP_VIDEO )
{
for( i = 0; i < NUM_NO_OPS; i++ )
{
*(rp++) = cpu_to_le32(RISC_NOOP);
}
}
/* scan lines */
for (line = 0; line < lines; line++)
{
*(rp++) = cpu_to_le32(RISC_READ|RISC_SOL|RISC_EOL|bpl);
*(rp++) = cpu_to_le32(databuf_phys_addr+offset);
*(rp++) = cpu_to_le32(0); /* bits 63-32 */
if ( (lines <= NTSC_FIELD_HEIGHT) || (line < (NTSC_FIELD_HEIGHT-1)) || !(dev->_isNTSC) )
{
offset += dist_betwn_starts; //to skip the other field line
}
// check if we need to enable the FIFO after the first 4 lines
// For the upstream video channel, the risc engine will enable the FIFO.
if ( fifo_enable && line == 3 )
{
*(rp++) = RISC_WRITECR;
*(rp++) = sram_ch->dma_ctl;
*(rp++) = FLD_VID_FIFO_EN;
*(rp++) = 0x00000001;
}
}
return rp;
}
int cx25821_risc_buffer_upstream( struct cx25821_dev *dev,
struct pci_dev *pci,
unsigned int top_offset,
unsigned int bpl, unsigned int lines)
{
__le32 *rp;
int fifo_enable = 0;
int singlefield_lines = lines >> 1; //get line count for single field
int odd_num_lines = singlefield_lines;
int frame = 0;
int frame_size = 0;
int databuf_offset = 0;
int risc_program_size = 0;
int risc_flag = RISC_CNT_RESET;
unsigned int bottom_offset = bpl;
dma_addr_t risc_phys_jump_addr;
if( dev->_isNTSC )
{
odd_num_lines = singlefield_lines + 1;
risc_program_size = FRAME1_VID_PROG_SIZE;
frame_size = (bpl == Y411_LINE_SZ) ? FRAME_SIZE_NTSC_Y411 : FRAME_SIZE_NTSC_Y422;
}
else
{
risc_program_size = PAL_VID_PROG_SIZE;
frame_size = (bpl == Y411_LINE_SZ) ? FRAME_SIZE_PAL_Y411 : FRAME_SIZE_PAL_Y422;
}
/* Virtual address of Risc buffer program */
rp = dev->_dma_virt_addr;
for( frame = 0; frame < NUM_FRAMES; frame++ )
{
databuf_offset = frame_size * frame;
if (UNSET != top_offset)
{
fifo_enable = (frame == 0) ? FIFO_ENABLE : FIFO_DISABLE;
rp = cx25821_risc_field_upstream(dev, rp, dev->_data_buf_phys_addr+databuf_offset, top_offset, 0, bpl, odd_num_lines, fifo_enable, ODD_FIELD);
}
fifo_enable = FIFO_DISABLE;
//Even Field
rp = cx25821_risc_field_upstream(dev, rp, dev->_data_buf_phys_addr+databuf_offset, bottom_offset, 0x200, bpl, singlefield_lines, fifo_enable, EVEN_FIELD);
if( frame == 0 )
{
risc_flag = RISC_CNT_RESET;
risc_phys_jump_addr = dev->_dma_phys_start_addr + risc_program_size;
}
else
{
risc_phys_jump_addr = dev->_dma_phys_start_addr;
risc_flag = RISC_CNT_INC;
}
// Loop to 2ndFrameRISC or to Start of Risc program & generate IRQ
*(rp++) = cpu_to_le32(RISC_JUMP|RISC_IRQ1|risc_flag);
*(rp++) = cpu_to_le32(risc_phys_jump_addr);
*(rp++) = cpu_to_le32(0);
}
return 0;
}
void cx25821_stop_upstream_video_ch1(struct cx25821_dev *dev)
{
struct sram_channel *sram_ch = &dev->sram_channels[VID_UPSTREAM_SRAM_CHANNEL_I];
u32 tmp = 0;
if( !dev->_is_running )
{
printk("cx25821: No video file is currently running so return!\n");
return;
}
//Disable RISC interrupts
tmp = cx_read( sram_ch->int_msk );
cx_write( sram_ch->int_msk, tmp & ~_intr_msk);
//Turn OFF risc and fifo enable
tmp = cx_read( sram_ch->dma_ctl );
cx_write( sram_ch->dma_ctl, tmp & ~(FLD_VID_FIFO_EN | FLD_VID_RISC_EN) );
//Clear data buffer memory
if( dev->_data_buf_virt_addr )
memset( dev->_data_buf_virt_addr, 0, dev->_data_buf_size );
dev->_is_running = 0;
dev->_is_first_frame = 0;
dev->_frame_count = 0;
dev->_file_status = END_OF_FILE;
if( dev->_irq_queues )
{
kfree(dev->_irq_queues);
dev->_irq_queues = NULL;
}
if( dev->_filename != NULL )
kfree(dev->_filename);
tmp = cx_read( VID_CH_MODE_SEL );
cx_write( VID_CH_MODE_SEL, tmp & 0xFFFFFE00);
}
void cx25821_free_mem_upstream_ch1(struct cx25821_dev *dev)
{
if( dev->_is_running )
{
cx25821_stop_upstream_video_ch1(dev);
}
if (dev->_dma_virt_addr)
{
pci_free_consistent(dev->pci, dev->_risc_size, dev->_dma_virt_addr, dev->_dma_phys_addr);
dev->_dma_virt_addr = NULL;
}
if (dev->_data_buf_virt_addr)
{
pci_free_consistent(dev->pci, dev->_data_buf_size, dev->_data_buf_virt_addr, dev->_data_buf_phys_addr);
dev->_data_buf_virt_addr = NULL;
}
}
int cx25821_get_frame(struct cx25821_dev *dev, struct sram_channel *sram_ch )
{
struct file * myfile;
int frame_index_temp = dev->_frame_index;
int i = 0;
int line_size = (dev->_pixel_format == PIXEL_FRMT_411) ? Y411_LINE_SZ : Y422_LINE_SZ;
int frame_size = 0;
int frame_offset = 0;
ssize_t vfs_read_retval = 0;
char mybuf[line_size];
loff_t file_offset;
loff_t pos;
mm_segment_t old_fs;
if( dev->_file_status == END_OF_FILE )
return 0;
if( dev->_isNTSC )
{
frame_size = (line_size == Y411_LINE_SZ) ? FRAME_SIZE_NTSC_Y411 : FRAME_SIZE_NTSC_Y422;
}
else
{
frame_size = (line_size == Y411_LINE_SZ) ? FRAME_SIZE_PAL_Y411 : FRAME_SIZE_PAL_Y422;
}
frame_offset = (frame_index_temp > 0) ? frame_size : 0;
file_offset = dev->_frame_count * frame_size;
myfile = filp_open( dev->_filename, O_RDONLY | O_LARGEFILE, 0 );
if (IS_ERR(myfile))
{
const int open_errno = -PTR_ERR(myfile);
printk("%s(): ERROR opening file(%s) with errno = %d! \n", __func__, dev->_filename, open_errno);
return PTR_ERR(myfile);
}
else
{
if( !(myfile->f_op) )
{
printk("%s: File has no file operations registered!", __func__);
filp_close(myfile, NULL);
return -EIO;
}
if( !myfile->f_op->read )
{
printk("%s: File has no READ operations registered!", __func__);
filp_close(myfile, NULL);
return -EIO;
}
pos = myfile->f_pos;
old_fs = get_fs();
set_fs(KERNEL_DS);
for( i = 0; i < dev->_lines_count; i++ )
{
pos = file_offset;
vfs_read_retval = vfs_read(myfile, mybuf, line_size, &pos);
if( vfs_read_retval > 0 && vfs_read_retval == line_size && dev->_data_buf_virt_addr != NULL )
{
memcpy( (void*)(dev->_data_buf_virt_addr+frame_offset/4), mybuf, vfs_read_retval);
}
file_offset += vfs_read_retval;
frame_offset += vfs_read_retval;
if( vfs_read_retval < line_size )
{
printk(KERN_INFO "Done: exit %s() since no more bytes to read from Video file.\n", __func__ );
break;
}
}
if( i > 0 )
dev->_frame_count++;
dev->_file_status = (vfs_read_retval == line_size) ? IN_PROGRESS : END_OF_FILE;
set_fs(old_fs);
filp_close(myfile, NULL);
}
return 0;
}
static void cx25821_vidups_handler(struct work_struct *work)
{
struct cx25821_dev *dev = container_of(work, struct cx25821_dev, _irq_work_entry);
if( !dev )
{
printk("ERROR %s(): since container_of(work_struct) FAILED! \n", __func__ );
return;
}
cx25821_get_frame( dev, &dev->sram_channels[dev->_channel_upstream_select] );
}
int cx25821_openfile(struct cx25821_dev *dev, struct sram_channel *sram_ch)
{
struct file * myfile;
int i = 0, j = 0;
int line_size = (dev->_pixel_format == PIXEL_FRMT_411) ? Y411_LINE_SZ : Y422_LINE_SZ;
ssize_t vfs_read_retval = 0;
char mybuf[line_size];
loff_t pos;
loff_t offset = (unsigned long)0;
mm_segment_t old_fs;
myfile = filp_open( dev->_filename, O_RDONLY | O_LARGEFILE, 0 );
if (IS_ERR(myfile))
{
const int open_errno = -PTR_ERR(myfile);
printk("%s(): ERROR opening file(%s) with errno = %d! \n", __func__, dev->_filename, open_errno);
return PTR_ERR(myfile);
}
else
{
if( !(myfile->f_op) )
{
printk("%s: File has no file operations registered!", __func__);
filp_close(myfile, NULL);
return -EIO;
}
if( !myfile->f_op->read )
{
printk("%s: File has no READ operations registered! Returning.", __func__);
filp_close(myfile, NULL);
return -EIO;
}
pos = myfile->f_pos;
old_fs = get_fs();
set_fs(KERNEL_DS);
for( j = 0; j < NUM_FRAMES; j++ )
{
for( i = 0; i < dev->_lines_count; i++ )
{
pos = offset;
vfs_read_retval = vfs_read(myfile, mybuf, line_size, &pos);
if( vfs_read_retval > 0 && vfs_read_retval == line_size && dev->_data_buf_virt_addr != NULL )
{
memcpy( (void*)(dev->_data_buf_virt_addr+offset/4), mybuf, vfs_read_retval);
}
offset += vfs_read_retval;
if( vfs_read_retval < line_size )
{
printk(KERN_INFO "Done: exit %s() since no more bytes to read from Video file.\n", __func__ );
break;
}
}
if( i > 0 )
dev->_frame_count++;
if( vfs_read_retval < line_size )
{
break;
}
}
dev->_file_status = (vfs_read_retval == line_size) ? IN_PROGRESS : END_OF_FILE;
set_fs(old_fs);
myfile->f_pos = 0;
filp_close(myfile, NULL);
}
return 0;
}
int cx25821_upstream_buffer_prepare(struct cx25821_dev *dev,
struct sram_channel *sram_ch,
int bpl)
{
int ret = 0;
dma_addr_t dma_addr;
dma_addr_t data_dma_addr;
if( dev->_dma_virt_addr != NULL )
{
pci_free_consistent(dev->pci, dev->upstream_riscbuf_size, dev->_dma_virt_addr, dev->_dma_phys_addr);
}
dev->_dma_virt_addr = pci_alloc_consistent(dev->pci, dev->upstream_riscbuf_size, &dma_addr);
dev->_dma_virt_start_addr = dev->_dma_virt_addr;
dev->_dma_phys_start_addr = dma_addr;
dev->_dma_phys_addr = dma_addr;
dev->_risc_size = dev->upstream_riscbuf_size;
if (!dev->_dma_virt_addr)
{
printk("cx25821: FAILED to allocate memory for Risc buffer! Returning.\n");
return -ENOMEM;
}
//Clear memory at address
memset( dev->_dma_virt_addr, 0, dev->_risc_size );
if( dev->_data_buf_virt_addr != NULL )
{
pci_free_consistent(dev->pci, dev->upstream_databuf_size, dev->_data_buf_virt_addr, dev->_data_buf_phys_addr);
}
//For Video Data buffer allocation
dev->_data_buf_virt_addr = pci_alloc_consistent(dev->pci, dev->upstream_databuf_size, &data_dma_addr);
dev->_data_buf_phys_addr = data_dma_addr;
dev->_data_buf_size = dev->upstream_databuf_size;
if (!dev->_data_buf_virt_addr)
{
printk("cx25821: FAILED to allocate memory for data buffer! Returning.\n");
return -ENOMEM;
}
//Clear memory at address
memset( dev->_data_buf_virt_addr, 0, dev->_data_buf_size );
ret = cx25821_openfile(dev, sram_ch);
if( ret < 0 )
return ret;
//Create RISC programs
ret = cx25821_risc_buffer_upstream(dev, dev->pci, 0, bpl, dev->_lines_count );
if (ret < 0)
{
printk(KERN_INFO "cx25821: Failed creating Video Upstream Risc programs! \n");
goto error;
}
return 0;
error:
return ret;
}
int cx25821_video_upstream_irq(struct cx25821_dev *dev, int chan_num, u32 status)
{
u32 int_msk_tmp;
struct sram_channel *channel = &dev->sram_channels[chan_num];
int singlefield_lines = NTSC_FIELD_HEIGHT;
int line_size_in_bytes = Y422_LINE_SZ;
int odd_risc_prog_size = 0;
dma_addr_t risc_phys_jump_addr;
__le32 * rp;
if (status & FLD_VID_SRC_RISC1)
{
// We should only process one program per call
u32 prog_cnt = cx_read( channel->gpcnt );
//Since we've identified our IRQ, clear our bits from the interrupt mask and interrupt status registers
int_msk_tmp = cx_read(channel->int_msk);
cx_write(channel->int_msk, int_msk_tmp & ~_intr_msk);
cx_write( channel->int_stat, _intr_msk );
spin_lock(&dev->slock);
dev->_frame_index = prog_cnt;
queue_work(dev->_irq_queues, &dev->_irq_work_entry);
if ( dev->_is_first_frame )
{
dev->_is_first_frame = 0;
if( dev->_isNTSC )
{
singlefield_lines += 1;
odd_risc_prog_size = ODD_FLD_NTSC_PROG_SIZE;
}
else
{
singlefield_lines = PAL_FIELD_HEIGHT;
odd_risc_prog_size = ODD_FLD_PAL_PROG_SIZE;
}
if( dev->_dma_virt_start_addr != NULL )
{
line_size_in_bytes = (dev->_pixel_format == PIXEL_FRMT_411) ? Y411_LINE_SZ : Y422_LINE_SZ;
risc_phys_jump_addr = dev->_dma_phys_start_addr + odd_risc_prog_size;
rp = cx25821_update_riscprogram(dev, dev->_dma_virt_start_addr, TOP_OFFSET, line_size_in_bytes, 0x0, singlefield_lines, FIFO_DISABLE, ODD_FIELD);
// Jump to Even Risc program of 1st Frame
*(rp++) = cpu_to_le32(RISC_JUMP);
*(rp++) = cpu_to_le32(risc_phys_jump_addr);
*(rp++) = cpu_to_le32(0);
}
}
spin_unlock(&dev->slock);
}
else
{
if(status & FLD_VID_SRC_UF)
printk("%s: Video Received Underflow Error Interrupt!\n", __func__);
if(status & FLD_VID_SRC_SYNC)
printk("%s: Video Received Sync Error Interrupt!\n", __func__);
if(status & FLD_VID_SRC_OPC_ERR)
printk("%s: Video Received OpCode Error Interrupt!\n", __func__);
}
if( dev->_file_status == END_OF_FILE )
{
printk("cx25821: EOF Channel 1 Framecount = %d\n", dev->_frame_count );
return -1;
}
//ElSE, set the interrupt mask register, re-enable irq.
int_msk_tmp = cx_read( channel->int_msk );
cx_write( channel->int_msk, int_msk_tmp |= _intr_msk );
return 0;
}
static irqreturn_t cx25821_upstream_irq(int irq, void *dev_id)
{
struct cx25821_dev *dev = dev_id;
u32 msk_stat, vid_status;
int handled = 0;
int channel_num = 0;
struct sram_channel *sram_ch;
if( !dev )
return -1;
channel_num = VID_UPSTREAM_SRAM_CHANNEL_I;
sram_ch = &dev->sram_channels[channel_num];
msk_stat = cx_read(sram_ch->int_mstat);
vid_status = cx_read(sram_ch->int_stat);
// Only deal with our interrupt
if(vid_status)
{
handled = cx25821_video_upstream_irq(dev, channel_num, vid_status);
}
if( handled < 0 )
{
cx25821_stop_upstream_video_ch1(dev);
}
else
{
handled += handled;
}
return IRQ_RETVAL(handled);
}
void cx25821_set_pixelengine(struct cx25821_dev *dev, struct sram_channel *ch, int pix_format)
{
int width = WIDTH_D1;
int height = dev->_lines_count;
int num_lines, odd_num_lines;
u32 value;
int vip_mode = OUTPUT_FRMT_656;
value = ( (pix_format & 0x3) << 12 ) | ( vip_mode & 0x7 );
value &= 0xFFFFFFEF;
value |= dev->_isNTSC ? 0 : 0x10;
cx_write( ch->vid_fmt_ctl, value );
// set number of active pixels in each line. Default is 720 pixels in both NTSC and PAL format
cx_write( ch->vid_active_ctl1, width );
num_lines = (height / 2) & 0x3FF;
odd_num_lines = num_lines;
if(dev->_isNTSC)
{
odd_num_lines += 1;
}
value = (num_lines << 16) | odd_num_lines;
// set number of active lines in field 0 (top) and field 1 (bottom)
cx_write( ch->vid_active_ctl2, value );
cx_write( ch->vid_cdt_size, VID_CDT_SIZE >> 3 );
}
int cx25821_start_video_dma_upstream(struct cx25821_dev *dev,
struct sram_channel *sram_ch)
{
u32 tmp = 0;
int err = 0;
// 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface for channel A-C
tmp = cx_read( VID_CH_MODE_SEL );
cx_write( VID_CH_MODE_SEL, tmp | 0x1B0001FF);
// Set the physical start address of the RISC program in the initial program counter(IPC) member of the cmds.
cx_write(sram_ch->cmds_start + 0, dev->_dma_phys_addr);
cx_write(sram_ch->cmds_start + 4, 0); /* Risc IPC High 64 bits 63-32 */
/* reset counter */
cx_write(sram_ch->gpcnt_ctl, 3);
// Clear our bits from the interrupt status register.
cx_write( sram_ch->int_stat, _intr_msk );
//Set the interrupt mask register, enable irq.
cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << sram_ch->irq_bit));
tmp = cx_read( sram_ch->int_msk );
cx_write( sram_ch->int_msk, tmp |= _intr_msk );
err = request_irq(dev->pci->irq, cx25821_upstream_irq, IRQF_SHARED | IRQF_DISABLED, dev->name, dev);
if (err < 0)
{
printk(KERN_ERR "%s: can't get upstream IRQ %d\n", dev->name, dev->pci->irq);
goto fail_irq;
}
// Start the DMA engine
tmp = cx_read( sram_ch->dma_ctl );
cx_set( sram_ch->dma_ctl, tmp | FLD_VID_RISC_EN );
dev->_is_running = 1;
dev->_is_first_frame = 1;
return 0;
fail_irq:
cx25821_dev_unregister(dev);
return err;
}
int cx25821_vidupstream_init_ch1(struct cx25821_dev *dev, int channel_select, int pixel_format)
{
struct sram_channel *sram_ch;
u32 tmp;
int retval = 0;
int err = 0;
int data_frame_size = 0;
int risc_buffer_size = 0;
int str_length = 0;
if( dev->_is_running )
{
printk("Video Channel is still running so return!\n");
return 0;
}
dev->_channel_upstream_select = channel_select;
sram_ch = &dev->sram_channels[channel_select];
INIT_WORK(&dev->_irq_work_entry, cx25821_vidups_handler);
dev->_irq_queues = create_singlethread_workqueue("cx25821_workqueue");
if(!dev->_irq_queues)
{
printk("cx25821: create_singlethread_workqueue() for Video FAILED!\n");
return -ENOMEM;
}
// 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface for channel A-C
tmp = cx_read( VID_CH_MODE_SEL );
cx_write( VID_CH_MODE_SEL, tmp | 0x1B0001FF);
dev->_is_running = 0;
dev->_frame_count = 0;
dev->_file_status = RESET_STATUS;
dev->_lines_count = dev->_isNTSC ? 480 : 576;
dev->_pixel_format = pixel_format;
dev->_line_size = (dev->_pixel_format == PIXEL_FRMT_422) ? (WIDTH_D1 * 2) : (WIDTH_D1 * 3) / 2;
data_frame_size = dev->_isNTSC ? NTSC_DATA_BUF_SZ : PAL_DATA_BUF_SZ;
risc_buffer_size = dev->_isNTSC ? NTSC_RISC_BUF_SIZE : PAL_RISC_BUF_SIZE;
if( dev->input_filename )
{
str_length = strlen(dev->input_filename);
dev->_filename = (char *) kmalloc(str_length + 1, GFP_KERNEL);
if( !dev->_filename )
goto error;
memcpy(dev->_filename, dev->input_filename, str_length + 1);
}
else
{
str_length = strlen(dev->_defaultname);
dev->_filename = (char *) kmalloc(str_length + 1, GFP_KERNEL);
if( !dev->_filename )
goto error;
memcpy(dev->_filename, dev->_defaultname, str_length + 1);
}
//Default if filename is empty string
if( strcmp(dev->input_filename,"") == 0)
{
if( dev->_isNTSC )
{
dev->_filename = (dev->_pixel_format == PIXEL_FRMT_411) ? "/root/vid411.yuv" : "/root/vidtest.yuv";
}
else
{
dev->_filename = (dev->_pixel_format == PIXEL_FRMT_411) ? "/root/pal411.yuv" : "/root/pal422.yuv";
}
}
dev->_is_running = 0;
dev->_frame_count = 0;
dev->_file_status = RESET_STATUS;
dev->_lines_count = dev->_isNTSC ? 480 : 576;
dev->_pixel_format = pixel_format;
dev->_line_size = (dev->_pixel_format == PIXEL_FRMT_422) ? (WIDTH_D1 * 2) : (WIDTH_D1 * 3) / 2;
retval = cx25821_sram_channel_setup_upstream(dev, sram_ch, dev->_line_size, 0);
/* setup fifo + format */
cx25821_set_pixelengine(dev, sram_ch, dev->_pixel_format);
dev->upstream_riscbuf_size = risc_buffer_size * 2;
dev->upstream_databuf_size = data_frame_size * 2;
//Allocating buffers and prepare RISC program
retval = cx25821_upstream_buffer_prepare(dev, sram_ch, dev->_line_size);
if (retval < 0)
{
printk(KERN_ERR "%s: Failed to set up Video upstream buffers!\n", dev->name);
goto error;
}
cx25821_start_video_dma_upstream(dev, sram_ch);
return 0;
error:
cx25821_dev_unregister(dev);
return err;
}

View File

@ -0,0 +1,113 @@
/*
* Driver for the Conexant CX25821 PCIe bridge
*
* Copyright (C) 2009 Conexant Systems Inc.
* Authors <hiep.huynh@conexant.com>, <shu.lin@conexant.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/mutex.h>
#include <linux/workqueue.h>
#define OUTPUT_FRMT_656 0
#define OPEN_FILE_1 0
#define NUM_PROGS 8
#define NUM_FRAMES 2
#define ODD_FIELD 0
#define EVEN_FIELD 1
#define TOP_OFFSET 0
#define FIFO_DISABLE 0
#define FIFO_ENABLE 1
#define TEST_FRAMES 5
#define END_OF_FILE 0
#define IN_PROGRESS 1
#define RESET_STATUS -1
#define NUM_NO_OPS 5
// PAL and NTSC line sizes and number of lines.
#define WIDTH_D1 720
#define NTSC_LINES_PER_FRAME 480
#define PAL_LINES_PER_FRAME 576
#define PAL_LINE_SZ 1440
#define Y422_LINE_SZ 1440
#define Y411_LINE_SZ 1080
#define NTSC_FIELD_HEIGHT 240
#define NTSC_ODD_FLD_LINES 241
#define PAL_FIELD_HEIGHT 288
#define FRAME_SIZE_NTSC_Y422 (NTSC_LINES_PER_FRAME * Y422_LINE_SZ)
#define FRAME_SIZE_NTSC_Y411 (NTSC_LINES_PER_FRAME * Y411_LINE_SZ)
#define FRAME_SIZE_PAL_Y422 (PAL_LINES_PER_FRAME * Y422_LINE_SZ)
#define FRAME_SIZE_PAL_Y411 (PAL_LINES_PER_FRAME * Y411_LINE_SZ)
#define NTSC_DATA_BUF_SZ (Y422_LINE_SZ * NTSC_LINES_PER_FRAME)
#define PAL_DATA_BUF_SZ (Y422_LINE_SZ * PAL_LINES_PER_FRAME)
#define RISC_WRITECR_INSTRUCTION_SIZE 16
#define RISC_SYNC_INSTRUCTION_SIZE 4
#define JUMP_INSTRUCTION_SIZE 12
#define MAXSIZE_NO_OPS 36
#define DWORD_SIZE 4
#define USE_RISC_NOOP_VIDEO 1
#ifdef USE_RISC_NOOP_VIDEO
#define PAL_US_VID_PROG_SIZE ((PAL_FIELD_HEIGHT) * 3 * DWORD_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \
RISC_SYNC_INSTRUCTION_SIZE + NUM_NO_OPS*DWORD_SIZE)
#define PAL_RISC_BUF_SIZE (2 * PAL_US_VID_PROG_SIZE)
#define PAL_VID_PROG_SIZE ((PAL_FIELD_HEIGHT*2) * 3 * DWORD_SIZE + 2*RISC_SYNC_INSTRUCTION_SIZE + \
RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE + 2*NUM_NO_OPS*DWORD_SIZE)
#define ODD_FLD_PAL_PROG_SIZE ((PAL_FIELD_HEIGHT) * 3 * DWORD_SIZE + RISC_SYNC_INSTRUCTION_SIZE + \
RISC_WRITECR_INSTRUCTION_SIZE + NUM_NO_OPS*DWORD_SIZE)
#define ODD_FLD_NTSC_PROG_SIZE ((NTSC_ODD_FLD_LINES) * 3 * DWORD_SIZE + RISC_SYNC_INSTRUCTION_SIZE + \
RISC_WRITECR_INSTRUCTION_SIZE + NUM_NO_OPS*DWORD_SIZE)
#define NTSC_US_VID_PROG_SIZE ((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \
JUMP_INSTRUCTION_SIZE + NUM_NO_OPS*DWORD_SIZE)
#define NTSC_RISC_BUF_SIZE (2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE))
#define FRAME1_VID_PROG_SIZE ((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) * 3 * DWORD_SIZE + 2*RISC_SYNC_INSTRUCTION_SIZE + \
RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE + 2*NUM_NO_OPS*DWORD_SIZE)
#endif
#ifndef USE_RISC_NOOP_VIDEO
#define PAL_US_VID_PROG_SIZE ((PAL_FIELD_HEIGHT) * 3 * DWORD_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \
RISC_SYNC_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE)
#define PAL_RISC_BUF_SIZE (2 * PAL_US_VID_PROG_SIZE)
#define PAL_VID_PROG_SIZE ((PAL_FIELD_HEIGHT*2) * 3 * DWORD_SIZE + 2*RISC_SYNC_INSTRUCTION_SIZE + \
RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE )
#define ODD_FLD_PAL_PROG_SIZE ((PAL_FIELD_HEIGHT) * 3 * DWORD_SIZE + RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE )
#define ODD_FLD_NTSC_PROG_SIZE ((NTSC_ODD_FLD_LINES) * 3 * DWORD_SIZE + RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE )
#define NTSC_US_VID_PROG_SIZE ((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE)
#define NTSC_RISC_BUF_SIZE ( 2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE) )
#define FRAME1_VID_PROG_SIZE ((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) * 3 * DWORD_SIZE + 2*RISC_SYNC_INSTRUCTION_SIZE + \
RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE )
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,172 @@
/*
* Driver for the Conexant CX25821 PCIe bridge
*
* Copyright (C) 2009 Conexant Systems Inc.
* Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
* Based on Steven Toth <stoth@linuxtv.org> cx23885 driver
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef CX25821_VIDEO_H_
#define CX25821_VIDEO_H_
#include <linux/init.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kmod.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/kthread.h>
#include <asm/div64.h>
#include "cx25821.h"
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#ifdef CONFIG_VIDEO_V4L1_COMPAT
/* Include V4L1 specific functions. Should be removed soon */
#include <linux/videodev.h>
#endif
#define TUNER_FLAG
#define VIDEO_DEBUG 0
#define dprintk(level, fmt, arg...)\
do { if (VIDEO_DEBUG >= level)\
printk(KERN_DEBUG "%s/0: " fmt, dev->name, ## arg);\
} while (0)
//For IOCTL to identify running upstream
#define UPSTREAM_START_VIDEO 700
#define UPSTREAM_STOP_VIDEO 701
#define UPSTREAM_START_AUDIO 702
#define UPSTREAM_STOP_AUDIO 703
#define UPSTREAM_DUMP_REGISTERS 702
#define SET_VIDEO_STD 800
#define SET_PIXEL_FORMAT 1000
#define ENABLE_CIF_RESOLUTION 1001
#define REG_READ 900
#define REG_WRITE 901
#define MEDUSA_READ 910
#define MEDUSA_WRITE 911
extern struct sram_channel *channel0;
extern struct sram_channel *channel1;
extern struct sram_channel *channel2;
extern struct sram_channel *channel3;
extern struct sram_channel *channel4;
extern struct sram_channel *channel5;
extern struct sram_channel *channel6;
extern struct sram_channel *channel7;
extern struct sram_channel *channel9;
extern struct sram_channel *channel10;
extern struct sram_channel *channel11;
extern struct video_device cx25821_video_template0;
extern struct video_device cx25821_video_template1;
extern struct video_device cx25821_video_template2;
extern struct video_device cx25821_video_template3;
extern struct video_device cx25821_video_template4;
extern struct video_device cx25821_video_template5;
extern struct video_device cx25821_video_template6;
extern struct video_device cx25821_video_template7;
extern struct video_device cx25821_video_template9;
extern struct video_device cx25821_video_template10;
extern struct video_device cx25821_video_template11;
extern struct video_device cx25821_videoioctl_template;
//extern const u32 *ctrl_classes[];
extern unsigned int vid_limit;
#define FORMAT_FLAGS_PACKED 0x01
extern struct cx25821_fmt formats[];
extern struct cx25821_fmt *format_by_fourcc(unsigned int fourcc);
extern struct cx25821_data timeout_data[MAX_VID_CHANNEL_NUM];
extern void dump_video_queue(struct cx25821_dev *dev, struct cx25821_dmaqueue *q);
extern void cx25821_video_wakeup(struct cx25821_dev *dev, struct cx25821_dmaqueue *q, u32 count);
#ifdef TUNER_FLAG
extern int cx25821_set_tvnorm(struct cx25821_dev *dev, v4l2_std_id norm);
#endif
extern int res_get(struct cx25821_dev *dev, struct cx25821_fh *fh, unsigned int bit);
extern int res_check(struct cx25821_fh *fh, unsigned int bit);
extern int res_locked(struct cx25821_dev *dev, unsigned int bit);
extern void res_free(struct cx25821_dev *dev, struct cx25821_fh *fh, unsigned int bits);
extern int cx25821_video_mux(struct cx25821_dev *dev, unsigned int input);
extern int cx25821_start_video_dma(struct cx25821_dev *dev,
struct cx25821_dmaqueue *q,
struct cx25821_buffer *buf,
struct sram_channel *channel);
extern int cx25821_set_scale(struct cx25821_dev *dev, unsigned int width, unsigned int height, enum v4l2_field field);
extern int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status);
extern void cx25821_video_unregister(struct cx25821_dev *dev, int chan_num);
extern int cx25821_video_register(struct cx25821_dev *dev, int chan_num, struct video_device *video_template);
extern int get_format_size(void);
extern int buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size);
extern int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, enum v4l2_field field);
extern void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb);
extern struct videobuf_queue *get_queue(struct cx25821_fh *fh);
extern int get_resource(struct cx25821_fh *fh, int resource);
extern int video_mmap(struct file *file, struct vm_area_struct *vma);
extern int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f);
extern int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap);
extern int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f);
extern int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf);
extern int vidioc_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *p);
extern int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p);
extern int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p);
extern int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *tvnorms);
extern int cx25821_enum_input(struct cx25821_dev *dev, struct v4l2_input *i);
extern int vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *i);
extern int vidioc_g_input(struct file *file, void *priv, unsigned int *i);
extern int vidioc_s_input(struct file *file, void *priv, unsigned int i);
extern int vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctl);
extern int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f);
extern int vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *f);
extern int cx25821_set_freq(struct cx25821_dev *dev, struct v4l2_frequency *f);
extern int vidioc_s_frequency(struct file *file, void *priv, struct v4l2_frequency *f);
extern int vidioc_g_register(struct file *file, void *fh, struct v4l2_dbg_register *reg);
extern int vidioc_s_register(struct file *file, void *fh, struct v4l2_dbg_register *reg);
extern int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t);
extern int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t);
extern int is_valid_width(u32 width, v4l2_std_id tvnorm);
extern int is_valid_height(u32 height, v4l2_std_id tvnorm);
extern int vidioc_g_priority(struct file *file, void *f, enum v4l2_priority *p);
extern int vidioc_s_priority(struct file *file, void *f, enum v4l2_priority prio);
extern int vidioc_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *qctrl);
extern int cx25821_set_control(struct cx25821_dev *dev, struct v4l2_control *ctrl, int chan_num);
extern int vidioc_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropcap);
extern int vidioc_s_crop(struct file *file, void *priv, struct v4l2_crop *crop);
extern int vidioc_g_crop(struct file *file, void *priv, struct v4l2_crop *crop);
extern int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *norm);
#endif

View File

@ -0,0 +1,457 @@
/*
* Driver for the Conexant CX25821 PCIe bridge
*
* Copyright (C) 2009 Conexant Systems Inc.
* Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
* Based on Steven Toth <stoth@linuxtv.org> cx23885 driver
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "cx25821-video.h"
static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
{
struct cx25821_buffer *buf = container_of(vb, struct cx25821_buffer, vb);
struct cx25821_buffer *prev;
struct cx25821_fh *fh = vq->priv_data;
struct cx25821_dev *dev = fh->dev;
struct cx25821_dmaqueue *q = &dev->vidq[SRAM_CH00];
/* 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);
buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]);
if (!list_empty(&q->queued)) {
list_add_tail(&buf->vb.queue, &q->queued);
buf->vb.state = VIDEOBUF_QUEUED;
dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, buf->vb.i);
} else if (list_empty(&q->active)) {
list_add_tail(&buf->vb.queue, &q->active);
cx25821_start_video_dma(dev, q, buf, &dev->sram_channels[SRAM_CH00]);
buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
dprintk(2, "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n",
buf, buf->vb. i, buf->count, q->count);
} else {
prev = list_entry(q->active.prev, struct cx25821_buffer, vb.queue);
if (prev->vb.width == buf->vb.width &&
prev->vb.height == buf->vb.height &&
prev->fmt == buf->fmt) {
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);
/* 64 bit bits 63-32 */
prev->risc.jmp[2] = cpu_to_le32(0);
dprintk(2, "[%p/%d] buffer_queue - append to active, buf->count=%d\n", buf, buf->vb.i, buf->count);
} else {
list_add_tail(&buf->vb.queue, &q->queued);
buf->vb.state = VIDEOBUF_QUEUED;
dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, buf->vb.i);
}
}
if (list_empty(&q->active))
{
dprintk(2, "active queue empty!\n");
}
}
static struct videobuf_queue_ops cx25821_video_qops = {
.buf_setup = buffer_setup,
.buf_prepare = buffer_prepare,
.buf_queue = buffer_queue,
.buf_release = buffer_release,
};
static int video_open(struct file *file)
{
int minor = video_devdata(file)->minor;
struct cx25821_dev *h, *dev = NULL;
struct cx25821_fh *fh;
struct list_head *list;
enum v4l2_buf_type type = 0;
u32 pix_format;
lock_kernel();
list_for_each(list, &cx25821_devlist)
{
h = list_entry(list, struct cx25821_dev, devlist);
if (h->video_dev[SRAM_CH00] && h->video_dev[SRAM_CH00]->minor == minor)
{
dev = h;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
}
}
if (NULL == dev) {
unlock_kernel();
return -ENODEV;
}
printk("open minor=%d type=%s\n", minor, v4l2_type_names[type]);
/* allocate + initialize per filehandle data */
fh = kzalloc(sizeof(*fh), GFP_KERNEL);
if (NULL == fh) {
unlock_kernel();
return -ENOMEM;
}
file->private_data = fh;
fh->dev = dev;
fh->type = type;
fh->width = 720;
if(dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK)
fh->height = 576;
else
fh->height = 480;
dev->channel_opened = SRAM_CH00;
pix_format = (dev->pixel_formats[dev->channel_opened] == PIXEL_FRMT_411) ? V4L2_PIX_FMT_Y41P : V4L2_PIX_FMT_YUYV;
fh->fmt = format_by_fourcc(pix_format);
v4l2_prio_open(&dev->prio,&fh->prio);
videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops,
&dev->pci->dev, &dev->slock,
V4L2_BUF_TYPE_VIDEO_CAPTURE,
V4L2_FIELD_INTERLACED,
sizeof(struct cx25821_buffer),
fh);
dprintk(1, "post videobuf_queue_init()\n");
unlock_kernel();
return 0;
}
static ssize_t video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
{
struct cx25821_fh *fh = file->private_data;
switch (fh->type)
{
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
if (res_locked(fh->dev, RESOURCE_VIDEO0))
return -EBUSY;
return videobuf_read_one(&fh->vidq, data, count, ppos, file->f_flags & O_NONBLOCK);
default:
BUG();
return 0;
}
}
static unsigned int video_poll(struct file *file, struct poll_table_struct *wait)
{
struct cx25821_fh *fh = file->private_data;
struct cx25821_buffer *buf;
if (res_check(fh, RESOURCE_VIDEO0)) {
/* streaming capture */
if (list_empty(&fh->vidq.stream))
return POLLERR;
buf = list_entry(fh->vidq.stream.next,
struct cx25821_buffer, vb.stream);
} else {
/* read() capture */
buf = (struct cx25821_buffer *)fh->vidq.read_buf;
if (NULL == buf)
return POLLERR;
}
poll_wait(file, &buf->vb.done, wait);
if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR)
{
if( buf->vb.state == VIDEOBUF_DONE )
{
struct cx25821_dev *dev = fh->dev;
if( dev && dev->use_cif_resolution[SRAM_CH00] )
{
u8 cam_id = *((char*)buf->vb.baddr+3);
memcpy((char*)buf->vb.baddr, (char*)buf->vb.baddr + (fh->width * 2), (fh->width * 2));
*((char*)buf->vb.baddr+3) = cam_id;
}
}
return POLLIN|POLLRDNORM;
}
return 0;
}
static int video_release(struct file *file)
{
struct cx25821_fh *fh = file->private_data;
struct cx25821_dev *dev = fh->dev;
//stop the risc engine and fifo
cx_write(channel0->dma_ctl, 0); /* FIFO and RISC disable */
/* stop video capture */
if (res_check(fh, RESOURCE_VIDEO0)) {
videobuf_queue_cancel(&fh->vidq);
res_free(dev, fh, RESOURCE_VIDEO0);
}
if (fh->vidq.read_buf) {
buffer_release(&fh->vidq, fh->vidq.read_buf);
kfree(fh->vidq.read_buf);
}
videobuf_mmap_free(&fh->vidq);
v4l2_prio_close(&dev->prio,&fh->prio);
file->private_data = NULL;
kfree(fh);
return 0;
}
static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = fh->dev;
if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE))
{
return -EINVAL;
}
if (unlikely(i != fh->type))
{
return -EINVAL;
}
if (unlikely(!res_get(dev, fh, get_resource(fh, RESOURCE_VIDEO0))))
{
return -EBUSY;
}
return videobuf_streamon(get_queue(fh));
}
static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = fh->dev;
int err, res;
if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
if (i != fh->type)
return -EINVAL;
res = get_resource(fh, RESOURCE_VIDEO0);
err = videobuf_streamoff(get_queue(fh));
if (err < 0)
return err;
res_free(dev, fh, res);
return 0;
}
static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
int err;
int pix_format = PIXEL_FRMT_422;
if (fh)
{
err = v4l2_prio_check(&dev->prio, &fh->prio);
if (0 != err)
return err;
}
dprintk(2, "%s()\n", __func__);
err = vidioc_try_fmt_vid_cap(file, priv, f);
if (0 != err)
return err;
fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
fh->vidq.field = f->fmt.pix.field;
// check if width and height is valid based on set standard
if (is_valid_width(f->fmt.pix.width, dev->tvnorm))
{
fh->width = f->fmt.pix.width;
}
if (is_valid_height(f->fmt.pix.height, dev->tvnorm))
{
fh->height = f->fmt.pix.height;
}
if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P)
pix_format = PIXEL_FRMT_411;
else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV)
pix_format = PIXEL_FRMT_422;
else
return -EINVAL;
cx25821_set_pixel_format( dev, SRAM_CH00, pix_format );
// check if cif resolution
if (fh->width == 320 || fh->width == 352)
{
dev->use_cif_resolution[SRAM_CH00] = 1;
}else
{
dev->use_cif_resolution[SRAM_CH00] = 0;
}
dev->cif_width[SRAM_CH00] = fh->width;
medusa_set_resolution( dev, fh->width, SRAM_CH00 );
dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, fh->height, fh->vidq.field);
cx25821_call_all(dev, video, s_fmt, f);
return 0;
}
static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
int ret_val = 0;
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
ret_val = videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK);
p->sequence = dev->vidq[SRAM_CH00].count;
return ret_val;
}
static int vidioc_log_status (struct file *file, void *priv)
{
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
char name[32 + 2];
struct sram_channel *sram_ch = &dev->sram_channels[SRAM_CH00];
u32 tmp = 0;
snprintf(name, sizeof(name), "%s/2", dev->name);
printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n",
dev->name);
cx25821_call_all(dev, core, log_status);
tmp = cx_read(sram_ch->dma_ctl);
printk(KERN_INFO "Video input 0 is %s\n", (tmp & 0x11)?"streaming" : "stopped");
printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n",
dev->name);
return 0;
}
static int vidioc_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctl)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
int err;
if (fh) {
err = v4l2_prio_check(&dev->prio, &fh->prio);
if (0 != err)
return err;
}
return cx25821_set_control(dev, ctl, SRAM_CH00);
}
// exported stuff
static const struct v4l2_file_operations video_fops = {
.owner = THIS_MODULE,
.open = video_open,
.release = video_release,
.read = video_read,
.poll = video_poll,
.mmap = video_mmap,
.ioctl = video_ioctl2,
};
static const struct v4l2_ioctl_ops video_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
.vidioc_reqbufs = vidioc_reqbufs,
.vidioc_querybuf = vidioc_querybuf,
.vidioc_qbuf = vidioc_qbuf,
.vidioc_dqbuf = vidioc_dqbuf,
#ifdef TUNER_FLAG
.vidioc_s_std = vidioc_s_std,
.vidioc_querystd = vidioc_querystd,
#endif
.vidioc_cropcap = vidioc_cropcap,
.vidioc_s_crop = vidioc_s_crop,
.vidioc_g_crop = vidioc_g_crop,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
.vidioc_g_ctrl = vidioc_g_ctrl,
.vidioc_s_ctrl = vidioc_s_ctrl,
.vidioc_queryctrl = vidioc_queryctrl,
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
.vidioc_log_status = vidioc_log_status,
.vidioc_g_priority = vidioc_g_priority,
.vidioc_s_priority = vidioc_s_priority,
#ifdef CONFIG_VIDEO_V4L1_COMPAT
.vidiocgmbuf = vidiocgmbuf,
#endif
#ifdef TUNER_FLAG
.vidioc_g_tuner = vidioc_g_tuner,
.vidioc_s_tuner = vidioc_s_tuner,
.vidioc_g_frequency = vidioc_g_frequency,
.vidioc_s_frequency = vidioc_s_frequency,
#endif
#ifdef CONFIG_VIDEO_ADV_DEBUG
.vidioc_g_register = vidioc_g_register,
.vidioc_s_register = vidioc_s_register,
#endif
};
struct video_device cx25821_video_template0 = {
.name = "cx25821-video",
.fops = &video_fops,
.minor = -1,
.ioctl_ops = &video_ioctl_ops,
.tvnorms = CX25821_NORMS,
.current_norm = V4L2_STD_NTSC_M,
};

View File

@ -0,0 +1,456 @@
/*
* Driver for the Conexant CX25821 PCIe bridge
*
* Copyright (C) 2009 Conexant Systems Inc.
* Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
* Based on Steven Toth <stoth@linuxtv.org> cx23885 driver
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "cx25821-video.h"
static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
{
struct cx25821_buffer *buf = container_of(vb, struct cx25821_buffer, vb);
struct cx25821_buffer *prev;
struct cx25821_fh *fh = vq->priv_data;
struct cx25821_dev *dev = fh->dev;
struct cx25821_dmaqueue *q = &dev->vidq[SRAM_CH01];
/* 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);
buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]);
if (!list_empty(&q->queued)) {
list_add_tail(&buf->vb.queue, &q->queued);
buf->vb.state = VIDEOBUF_QUEUED;
dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, buf->vb.i);
} else if (list_empty(&q->active)) {
list_add_tail(&buf->vb.queue, &q->active);
cx25821_start_video_dma(dev, q, buf, &dev->sram_channels[SRAM_CH01]);
buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
dprintk(2, "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n",
buf, buf->vb. i, buf->count, q->count);
} else {
prev = list_entry(q->active.prev, struct cx25821_buffer, vb.queue);
if (prev->vb.width == buf->vb.width &&
prev->vb.height == buf->vb.height &&
prev->fmt == buf->fmt) {
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);
/* 64 bit bits 63-32 */
prev->risc.jmp[2] = cpu_to_le32(0);
dprintk(2, "[%p/%d] buffer_queue - append to active, buf->count=%d\n", buf, buf->vb.i, buf->count);
} else {
list_add_tail(&buf->vb.queue, &q->queued);
buf->vb.state = VIDEOBUF_QUEUED;
dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, buf->vb.i);
}
}
if (list_empty(&q->active))
{
dprintk(2, "active queue empty!\n");
}
}
static struct videobuf_queue_ops cx25821_video_qops = {
.buf_setup = buffer_setup,
.buf_prepare = buffer_prepare,
.buf_queue = buffer_queue,
.buf_release = buffer_release,
};
static int video_open(struct file *file)
{
int minor = video_devdata(file)->minor;
struct cx25821_dev *h, *dev = NULL;
struct cx25821_fh *fh;
struct list_head *list;
enum v4l2_buf_type type = 0;
u32 pix_format;
lock_kernel();
list_for_each(list, &cx25821_devlist)
{
h = list_entry(list, struct cx25821_dev, devlist);
if (h->video_dev[SRAM_CH01] && h->video_dev[SRAM_CH01]->minor == minor) {
dev = h;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
}
}
if (NULL == dev) {
unlock_kernel();
return -ENODEV;
}
printk("open minor=%d type=%s\n", minor, v4l2_type_names[type]);
/* allocate + initialize per filehandle data */
fh = kzalloc(sizeof(*fh), GFP_KERNEL);
if (NULL == fh) {
unlock_kernel();
return -ENOMEM;
}
file->private_data = fh;
fh->dev = dev;
fh->type = type;
fh->width = 720;
if(dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK)
fh->height = 576;
else
fh->height = 480;
dev->channel_opened = SRAM_CH01;
pix_format = (dev->pixel_formats[dev->channel_opened] == PIXEL_FRMT_411) ? V4L2_PIX_FMT_Y41P : V4L2_PIX_FMT_YUYV;
fh->fmt = format_by_fourcc(pix_format);
v4l2_prio_open(&dev->prio,&fh->prio);
videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops,
&dev->pci->dev, &dev->slock,
V4L2_BUF_TYPE_VIDEO_CAPTURE,
V4L2_FIELD_INTERLACED,
sizeof(struct cx25821_buffer),
fh);
dprintk(1, "post videobuf_queue_init()\n");
unlock_kernel();
return 0;
}
static ssize_t video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
{
struct cx25821_fh *fh = file->private_data;
switch (fh->type)
{
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
if (res_locked(fh->dev, RESOURCE_VIDEO1))
return -EBUSY;
return videobuf_read_one(&fh->vidq, data, count, ppos, file->f_flags & O_NONBLOCK);
default:
BUG();
return 0;
}
}
static unsigned int video_poll(struct file *file, struct poll_table_struct *wait)
{
struct cx25821_fh *fh = file->private_data;
struct cx25821_buffer *buf;
if (res_check(fh, RESOURCE_VIDEO1)) {
/* streaming capture */
if (list_empty(&fh->vidq.stream))
return POLLERR;
buf = list_entry(fh->vidq.stream.next,
struct cx25821_buffer, vb.stream);
} else {
/* read() capture */
buf = (struct cx25821_buffer *)fh->vidq.read_buf;
if (NULL == buf)
return POLLERR;
}
poll_wait(file, &buf->vb.done, wait);
if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR)
{
if( buf->vb.state == VIDEOBUF_DONE )
{
struct cx25821_dev *dev = fh->dev;
if( dev && dev->use_cif_resolution[SRAM_CH01] )
{
u8 cam_id = *((char*)buf->vb.baddr+3);
memcpy((char*)buf->vb.baddr, (char*)buf->vb.baddr + (fh->width * 2), (fh->width * 2));
*((char*)buf->vb.baddr+3) = cam_id;
}
}
return POLLIN|POLLRDNORM;
}
return 0;
}
static int video_release(struct file *file)
{
struct cx25821_fh *fh = file->private_data;
struct cx25821_dev *dev = fh->dev;
//stop the risc engine and fifo
cx_write(channel1->dma_ctl, 0); /* FIFO and RISC disable */
/* stop video capture */
if (res_check(fh, RESOURCE_VIDEO1)) {
videobuf_queue_cancel(&fh->vidq);
res_free(dev, fh, RESOURCE_VIDEO1);
}
if (fh->vidq.read_buf) {
buffer_release(&fh->vidq, fh->vidq.read_buf);
kfree(fh->vidq.read_buf);
}
videobuf_mmap_free(&fh->vidq);
v4l2_prio_close(&dev->prio,&fh->prio);
file->private_data = NULL;
kfree(fh);
return 0;
}
static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = fh->dev;
if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE))
{
return -EINVAL;
}
if (unlikely(i != fh->type))
{
return -EINVAL;
}
if (unlikely(!res_get(dev, fh, get_resource(fh, RESOURCE_VIDEO1))))
{
return -EBUSY;
}
return videobuf_streamon(get_queue(fh));
}
static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = fh->dev;
int err, res;
if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
if (i != fh->type)
return -EINVAL;
res = get_resource(fh, RESOURCE_VIDEO1);
err = videobuf_streamoff(get_queue(fh));
if (err < 0)
return err;
res_free(dev, fh, res);
return 0;
}
static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
int err;
int pix_format = 0;
if (fh)
{
err = v4l2_prio_check(&dev->prio, &fh->prio);
if (0 != err)
return err;
}
dprintk(2, "%s()\n", __func__);
err = vidioc_try_fmt_vid_cap(file, priv, f);
if (0 != err)
return err;
fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
fh->vidq.field = f->fmt.pix.field;
// check if width and height is valid based on set standard
if (is_valid_width(f->fmt.pix.width, dev->tvnorm))
{
fh->width = f->fmt.pix.width;
}
if (is_valid_height(f->fmt.pix.height, dev->tvnorm))
{
fh->height = f->fmt.pix.height;
}
if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P)
pix_format = PIXEL_FRMT_411;
else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV)
pix_format = PIXEL_FRMT_422;
else
return -EINVAL;
cx25821_set_pixel_format( dev, SRAM_CH01, pix_format );
// check if cif resolution
if (fh->width == 320 || fh->width == 352)
{
dev->use_cif_resolution[SRAM_CH01] = 1;
}else
{
dev->use_cif_resolution[SRAM_CH01] = 0;
}
dev->cif_width[SRAM_CH01] = fh->width;
medusa_set_resolution( dev, fh->width, SRAM_CH01 );
dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, fh->height, fh->vidq.field);
cx25821_call_all(dev, video, s_fmt, f);
return 0;
}
static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
int ret_val = 0;
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
ret_val = videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK);
p->sequence = dev->vidq[SRAM_CH01].count;
return ret_val;
}
static int vidioc_log_status (struct file *file, void *priv)
{
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
char name[32 + 2];
struct sram_channel *sram_ch = &dev->sram_channels[SRAM_CH01];
u32 tmp = 0;
snprintf(name, sizeof(name), "%s/2", dev->name);
printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n",
dev->name);
cx25821_call_all(dev, core, log_status);
tmp = cx_read(sram_ch->dma_ctl);
printk(KERN_INFO "Video input 1 is %s\n", (tmp & 0x11)?"streaming" : "stopped");
printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n",
dev->name);
return 0;
}
static int vidioc_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctl)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
int err;
if (fh) {
err = v4l2_prio_check(&dev->prio, &fh->prio);
if (0 != err)
return err;
}
return cx25821_set_control(dev, ctl, SRAM_CH01);
}
//exported stuff
static const struct v4l2_file_operations video_fops = {
.owner = THIS_MODULE,
.open = video_open,
.release = video_release,
.read = video_read,
.poll = video_poll,
.mmap = video_mmap,
.ioctl = video_ioctl2,
};
static const struct v4l2_ioctl_ops video_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
.vidioc_reqbufs = vidioc_reqbufs,
.vidioc_querybuf = vidioc_querybuf,
.vidioc_qbuf = vidioc_qbuf,
.vidioc_dqbuf = vidioc_dqbuf,
#ifdef TUNER_FLAG
.vidioc_s_std = vidioc_s_std,
.vidioc_querystd = vidioc_querystd,
#endif
.vidioc_cropcap = vidioc_cropcap,
.vidioc_s_crop = vidioc_s_crop,
.vidioc_g_crop = vidioc_g_crop,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
.vidioc_g_ctrl = vidioc_g_ctrl,
.vidioc_s_ctrl = vidioc_s_ctrl,
.vidioc_queryctrl = vidioc_queryctrl,
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
.vidioc_log_status = vidioc_log_status,
.vidioc_g_priority = vidioc_g_priority,
.vidioc_s_priority = vidioc_s_priority,
#ifdef CONFIG_VIDEO_V4L1_COMPAT
.vidiocgmbuf = vidiocgmbuf,
#endif
#ifdef TUNER_FLAG
.vidioc_g_tuner = vidioc_g_tuner,
.vidioc_s_tuner = vidioc_s_tuner,
.vidioc_g_frequency = vidioc_g_frequency,
.vidioc_s_frequency = vidioc_s_frequency,
#endif
#ifdef CONFIG_VIDEO_ADV_DEBUG
.vidioc_g_register = vidioc_g_register,
.vidioc_s_register = vidioc_s_register,
#endif
};
struct video_device cx25821_video_template1 = {
.name = "cx25821-video",
.fops = &video_fops,
.minor = -1,
.ioctl_ops = &video_ioctl_ops,
.tvnorms = CX25821_NORMS,
.current_norm = V4L2_STD_NTSC_M,
};

View File

@ -0,0 +1,459 @@
/*
* Driver for the Conexant CX25821 PCIe bridge
*
* Copyright (C) 2009 Conexant Systems Inc.
* Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
* Based on Steven Toth <stoth@linuxtv.org> cx23885 driver
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "cx25821-video.h"
static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
{
struct cx25821_buffer *buf = container_of(vb, struct cx25821_buffer, vb);
struct cx25821_buffer *prev;
struct cx25821_fh *fh = vq->priv_data;
struct cx25821_dev *dev = fh->dev;
struct cx25821_dmaqueue *q = &dev->vidq[SRAM_CH02];
/* 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);
buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]);
if (!list_empty(&q->queued)) {
list_add_tail(&buf->vb.queue, &q->queued);
buf->vb.state = VIDEOBUF_QUEUED;
dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, buf->vb.i);
} else if (list_empty(&q->active)) {
list_add_tail(&buf->vb.queue, &q->active);
cx25821_start_video_dma(dev, q, buf, &dev->sram_channels[SRAM_CH02]);
buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
dprintk(2, "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n",
buf, buf->vb. i, buf->count, q->count);
} else {
prev = list_entry(q->active.prev, struct cx25821_buffer, vb.queue);
if (prev->vb.width == buf->vb.width &&
prev->vb.height == buf->vb.height &&
prev->fmt == buf->fmt) {
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);
/* 64 bit bits 63-32 */
prev->risc.jmp[2] = cpu_to_le32(0);
dprintk(2, "[%p/%d] buffer_queue - append to active, buf->count=%d\n", buf, buf->vb.i, buf->count);
} else {
list_add_tail(&buf->vb.queue, &q->queued);
buf->vb.state = VIDEOBUF_QUEUED;
dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, buf->vb.i);
}
}
if (list_empty(&q->active))
{
dprintk(2, "active queue empty!\n");
}
}
static struct videobuf_queue_ops cx25821_video_qops = {
.buf_setup = buffer_setup,
.buf_prepare = buffer_prepare,
.buf_queue = buffer_queue,
.buf_release = buffer_release,
};
static int video_open(struct file *file)
{
int minor = video_devdata(file)->minor;
struct cx25821_dev *h, *dev = NULL;
struct cx25821_fh *fh;
struct list_head *list;
enum v4l2_buf_type type = 0;
u32 pix_format;
lock_kernel();
list_for_each(list, &cx25821_devlist)
{
h = list_entry(list, struct cx25821_dev, devlist);
if (h->video_dev[SRAM_CH02] && h->video_dev[SRAM_CH02]->minor == minor) {
dev = h;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
}
}
if (NULL == dev) {
unlock_kernel();
return -ENODEV;
}
printk("open minor=%d type=%s\n", minor, v4l2_type_names[type]);
/* allocate + initialize per filehandle data */
fh = kzalloc(sizeof(*fh), GFP_KERNEL);
if (NULL == fh) {
unlock_kernel();
return -ENOMEM;
}
file->private_data = fh;
fh->dev = dev;
fh->type = type;
fh->width = 720;
if(dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK)
fh->height = 576;
else
fh->height = 480;
dev->channel_opened = SRAM_CH02;
pix_format = (dev->pixel_formats[dev->channel_opened] == PIXEL_FRMT_411) ? V4L2_PIX_FMT_Y41P : V4L2_PIX_FMT_YUYV;
fh->fmt = format_by_fourcc(pix_format);
v4l2_prio_open(&dev->prio,&fh->prio);
videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops,
&dev->pci->dev, &dev->slock,
V4L2_BUF_TYPE_VIDEO_CAPTURE,
V4L2_FIELD_INTERLACED,
sizeof(struct cx25821_buffer),
fh);
dprintk(1, "post videobuf_queue_init()\n");
unlock_kernel();
return 0;
}
static ssize_t video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
{
struct cx25821_fh *fh = file->private_data;
switch (fh->type)
{
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
if (res_locked(fh->dev, RESOURCE_VIDEO2))
return -EBUSY;
return videobuf_read_one(&fh->vidq, data, count, ppos, file->f_flags & O_NONBLOCK);
default:
BUG();
return 0;
}
}
static unsigned int video_poll(struct file *file, struct poll_table_struct *wait)
{
struct cx25821_fh *fh = file->private_data;
struct cx25821_buffer *buf;
if (res_check(fh, RESOURCE_VIDEO2)) {
/* streaming capture */
if (list_empty(&fh->vidq.stream))
return POLLERR;
buf = list_entry(fh->vidq.stream.next,
struct cx25821_buffer, vb.stream);
} else {
/* read() capture */
buf = (struct cx25821_buffer *)fh->vidq.read_buf;
if (NULL == buf)
return POLLERR;
}
poll_wait(file, &buf->vb.done, wait);
if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR)
{
if( buf->vb.state == VIDEOBUF_DONE )
{
struct cx25821_dev *dev = fh->dev;
if( dev && dev->use_cif_resolution[SRAM_CH02] )
{
u8 cam_id = *((char*)buf->vb.baddr+3);
memcpy((char*)buf->vb.baddr, (char*)buf->vb.baddr + (fh->width * 2), (fh->width * 2));
*((char*)buf->vb.baddr+3) = cam_id;
}
}
return POLLIN|POLLRDNORM;
}
return 0;
}
static int video_release(struct file *file)
{
struct cx25821_fh *fh = file->private_data;
struct cx25821_dev *dev = fh->dev;
//stop the risc engine and fifo
cx_write(channel2->dma_ctl, 0); /* FIFO and RISC disable */
/* stop video capture */
if (res_check(fh, RESOURCE_VIDEO2)) {
videobuf_queue_cancel(&fh->vidq);
res_free(dev, fh, RESOURCE_VIDEO2);
}
if (fh->vidq.read_buf) {
buffer_release(&fh->vidq, fh->vidq.read_buf);
kfree(fh->vidq.read_buf);
}
videobuf_mmap_free(&fh->vidq);
v4l2_prio_close(&dev->prio,&fh->prio);
file->private_data = NULL;
kfree(fh);
return 0;
}
static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = fh->dev;
if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE))
{
return -EINVAL;
}
if (unlikely(i != fh->type))
{
return -EINVAL;
}
if (unlikely(!res_get(dev, fh, get_resource(fh, RESOURCE_VIDEO2))))
{
return -EBUSY;
}
return videobuf_streamon(get_queue(fh));
}
static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = fh->dev;
int err, res;
if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
if (i != fh->type)
return -EINVAL;
res = get_resource(fh, RESOURCE_VIDEO2);
err = videobuf_streamoff(get_queue(fh));
if (err < 0)
return err;
res_free(dev, fh, res);
return 0;
}
static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
int err;
int pix_format = 0;
if (fh)
{
err = v4l2_prio_check(&dev->prio, &fh->prio);
if (0 != err)
return err;
}
dprintk(2, "%s()\n", __func__);
err = vidioc_try_fmt_vid_cap(file, priv, f);
if (0 != err)
return err;
fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
fh->vidq.field = f->fmt.pix.field;
// check if width and height is valid based on set standard
if (is_valid_width(f->fmt.pix.width, dev->tvnorm))
{
fh->width = f->fmt.pix.width;
}
if (is_valid_height(f->fmt.pix.height, dev->tvnorm))
{
fh->height = f->fmt.pix.height;
}
if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P)
pix_format = PIXEL_FRMT_411;
else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV)
pix_format = PIXEL_FRMT_422;
else
return -EINVAL;
cx25821_set_pixel_format( dev, SRAM_CH02, pix_format );
// check if cif resolution
if (fh->width == 320 || fh->width == 352)
{
dev->use_cif_resolution[SRAM_CH02] = 1;
}else
{
dev->use_cif_resolution[SRAM_CH02] = 0;
}
dev->cif_width[SRAM_CH02] = fh->width;
medusa_set_resolution( dev, fh->width, SRAM_CH02 );
dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, fh->height, fh->vidq.field);
cx25821_call_all(dev, video, s_fmt, f);
return 0;
}
static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
int ret_val = 0;
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
ret_val = videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK);
p->sequence = dev->vidq[SRAM_CH02].count;
return ret_val;
}
static int vidioc_log_status (struct file *file, void *priv)
{
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
char name[32 + 2];
struct sram_channel *sram_ch = &dev->sram_channels[SRAM_CH02];
u32 tmp = 0;
snprintf(name, sizeof(name), "%s/2", dev->name);
printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n",
dev->name);
cx25821_call_all(dev, core, log_status);
tmp = cx_read(sram_ch->dma_ctl);
printk(KERN_INFO "Video input 2 is %s\n", (tmp & 0x11)?"streaming" : "stopped");
printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n",
dev->name);
return 0;
}
static int vidioc_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctl)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
int err;
if (fh) {
err = v4l2_prio_check(&dev->prio, &fh->prio);
if (0 != err)
return err;
}
return cx25821_set_control(dev, ctl, SRAM_CH02);
}
// exported stuff
static const struct v4l2_file_operations video_fops = {
.owner = THIS_MODULE,
.open = video_open,
.release = video_release,
.read = video_read,
.poll = video_poll,
.mmap = video_mmap,
.ioctl = video_ioctl2,
};
static const struct v4l2_ioctl_ops video_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
.vidioc_reqbufs = vidioc_reqbufs,
.vidioc_querybuf = vidioc_querybuf,
.vidioc_qbuf = vidioc_qbuf,
.vidioc_dqbuf = vidioc_dqbuf,
#ifdef TUNER_FLAG
.vidioc_s_std = vidioc_s_std,
.vidioc_querystd = vidioc_querystd,
#endif
.vidioc_cropcap = vidioc_cropcap,
.vidioc_s_crop = vidioc_s_crop,
.vidioc_g_crop = vidioc_g_crop,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
.vidioc_g_ctrl = vidioc_g_ctrl,
.vidioc_s_ctrl = vidioc_s_ctrl,
.vidioc_queryctrl = vidioc_queryctrl,
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
.vidioc_log_status = vidioc_log_status,
.vidioc_g_priority = vidioc_g_priority,
.vidioc_s_priority = vidioc_s_priority,
#ifdef CONFIG_VIDEO_V4L1_COMPAT
.vidiocgmbuf = vidiocgmbuf,
#endif
#ifdef TUNER_FLAG
.vidioc_g_tuner = vidioc_g_tuner,
.vidioc_s_tuner = vidioc_s_tuner,
.vidioc_g_frequency = vidioc_g_frequency,
.vidioc_s_frequency = vidioc_s_frequency,
#endif
#ifdef CONFIG_VIDEO_ADV_DEBUG
.vidioc_g_register = vidioc_g_register,
.vidioc_s_register = vidioc_s_register,
#endif
};
struct video_device cx25821_video_template2 = {
.name = "cx25821-video",
.fops = &video_fops,
.minor = -1,
.ioctl_ops = &video_ioctl_ops,
.tvnorms = CX25821_NORMS,
.current_norm = V4L2_STD_NTSC_M,
};

View File

@ -0,0 +1,458 @@
/*
* Driver for the Conexant CX25821 PCIe bridge
*
* Copyright (C) 2009 Conexant Systems Inc.
* Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
* Based on Steven Toth <stoth@linuxtv.org> cx23885 driver
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "cx25821-video.h"
static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
{
struct cx25821_buffer *buf = container_of(vb, struct cx25821_buffer, vb);
struct cx25821_buffer *prev;
struct cx25821_fh *fh = vq->priv_data;
struct cx25821_dev *dev = fh->dev;
struct cx25821_dmaqueue *q = &dev->vidq[SRAM_CH03];
/* 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);
buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]);
if (!list_empty(&q->queued)) {
list_add_tail(&buf->vb.queue, &q->queued);
buf->vb.state = VIDEOBUF_QUEUED;
dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, buf->vb.i);
} else if (list_empty(&q->active)) {
list_add_tail(&buf->vb.queue, &q->active);
cx25821_start_video_dma(dev, q, buf, &dev->sram_channels[SRAM_CH03]);
buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
dprintk(2, "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n",
buf, buf->vb. i, buf->count, q->count);
} else {
prev = list_entry(q->active.prev, struct cx25821_buffer, vb.queue);
if (prev->vb.width == buf->vb.width &&
prev->vb.height == buf->vb.height &&
prev->fmt == buf->fmt) {
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);
/* 64 bit bits 63-32 */
prev->risc.jmp[2] = cpu_to_le32(0);
dprintk(2, "[%p/%d] buffer_queue - append to active, buf->count=%d\n", buf, buf->vb.i, buf->count);
} else {
list_add_tail(&buf->vb.queue, &q->queued);
buf->vb.state = VIDEOBUF_QUEUED;
dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, buf->vb.i);
}
}
if (list_empty(&q->active))
{
dprintk(2, "active queue empty!\n");
}
}
static struct videobuf_queue_ops cx25821_video_qops = {
.buf_setup = buffer_setup,
.buf_prepare = buffer_prepare,
.buf_queue = buffer_queue,
.buf_release = buffer_release,
};
static int video_open(struct file *file)
{
int minor = video_devdata(file)->minor;
struct cx25821_dev *h, *dev = NULL;
struct cx25821_fh *fh;
struct list_head *list;
enum v4l2_buf_type type = 0;
u32 pix_format;
lock_kernel();
list_for_each(list, &cx25821_devlist)
{
h = list_entry(list, struct cx25821_dev, devlist);
if (h->video_dev[SRAM_CH03] && h->video_dev[SRAM_CH03]->minor == minor) {
dev = h;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
}
}
if (NULL == dev) {
unlock_kernel();
return -ENODEV;
}
printk("open minor=%d type=%s\n", minor, v4l2_type_names[type]);
/* allocate + initialize per filehandle data */
fh = kzalloc(sizeof(*fh), GFP_KERNEL);
if (NULL == fh) {
unlock_kernel();
return -ENOMEM;
}
file->private_data = fh;
fh->dev = dev;
fh->type = type;
fh->width = 720;
if(dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK)
fh->height = 576;
else
fh->height = 480;
dev->channel_opened = SRAM_CH03;
pix_format = (dev->pixel_formats[dev->channel_opened] == PIXEL_FRMT_411) ? V4L2_PIX_FMT_Y41P : V4L2_PIX_FMT_YUYV;
fh->fmt = format_by_fourcc(pix_format);
v4l2_prio_open(&dev->prio,&fh->prio);
videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops,
&dev->pci->dev, &dev->slock,
V4L2_BUF_TYPE_VIDEO_CAPTURE,
V4L2_FIELD_INTERLACED,
sizeof(struct cx25821_buffer),
fh);
dprintk(1, "post videobuf_queue_init()\n");
unlock_kernel();
return 0;
}
static ssize_t video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
{
struct cx25821_fh *fh = file->private_data;
switch (fh->type)
{
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
if (res_locked(fh->dev, RESOURCE_VIDEO3))
return -EBUSY;
return videobuf_read_one(&fh->vidq, data, count, ppos, file->f_flags & O_NONBLOCK);
default:
BUG();
return 0;
}
}
static unsigned int video_poll(struct file *file, struct poll_table_struct *wait)
{
struct cx25821_fh *fh = file->private_data;
struct cx25821_buffer *buf;
if (res_check(fh, RESOURCE_VIDEO3)) {
/* streaming capture */
if (list_empty(&fh->vidq.stream))
return POLLERR;
buf = list_entry(fh->vidq.stream.next,
struct cx25821_buffer, vb.stream);
} else {
/* read() capture */
buf = (struct cx25821_buffer *)fh->vidq.read_buf;
if (NULL == buf)
return POLLERR;
}
poll_wait(file, &buf->vb.done, wait);
if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR)
{
if( buf->vb.state == VIDEOBUF_DONE )
{
struct cx25821_dev *dev = fh->dev;
if( dev && dev->use_cif_resolution[SRAM_CH03] )
{
u8 cam_id = *((char*)buf->vb.baddr+3);
memcpy((char*)buf->vb.baddr, (char*)buf->vb.baddr + (fh->width * 2), (fh->width * 2));
*((char*)buf->vb.baddr+3) = cam_id;
}
}
return POLLIN|POLLRDNORM;
}
return 0;
}
static int video_release(struct file *file)
{
struct cx25821_fh *fh = file->private_data;
struct cx25821_dev *dev = fh->dev;
//stop the risc engine and fifo
cx_write(channel3->dma_ctl, 0); /* FIFO and RISC disable */
/* stop video capture */
if (res_check(fh, RESOURCE_VIDEO3)) {
videobuf_queue_cancel(&fh->vidq);
res_free(dev, fh, RESOURCE_VIDEO3);
}
if (fh->vidq.read_buf) {
buffer_release(&fh->vidq, fh->vidq.read_buf);
kfree(fh->vidq.read_buf);
}
videobuf_mmap_free(&fh->vidq);
v4l2_prio_close(&dev->prio,&fh->prio);
file->private_data = NULL;
kfree(fh);
return 0;
}
static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = fh->dev;
if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE))
{
return -EINVAL;
}
if (unlikely(i != fh->type))
{
return -EINVAL;
}
if (unlikely(!res_get(dev, fh, get_resource(fh, RESOURCE_VIDEO3))))
{
return -EBUSY;
}
return videobuf_streamon(get_queue(fh));
}
static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = fh->dev;
int err, res;
if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
if (i != fh->type)
return -EINVAL;
res = get_resource(fh, RESOURCE_VIDEO3);
err = videobuf_streamoff(get_queue(fh));
if (err < 0)
return err;
res_free(dev, fh, res);
return 0;
}
static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
int err;
int pix_format = 0;
if (fh)
{
err = v4l2_prio_check(&dev->prio, &fh->prio);
if (0 != err)
return err;
}
dprintk(2, "%s()\n", __func__);
err = vidioc_try_fmt_vid_cap(file, priv, f);
if (0 != err)
return err;
fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
fh->vidq.field = f->fmt.pix.field;
// check if width and height is valid based on set standard
if (is_valid_width(f->fmt.pix.width, dev->tvnorm))
{
fh->width = f->fmt.pix.width;
}
if (is_valid_height(f->fmt.pix.height, dev->tvnorm))
{
fh->height = f->fmt.pix.height;
}
if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P)
pix_format = PIXEL_FRMT_411;
else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV)
pix_format = PIXEL_FRMT_422;
else
return -EINVAL;
cx25821_set_pixel_format( dev, SRAM_CH03, pix_format );
// check if cif resolution
if (fh->width == 320 || fh->width == 352)
{
dev->use_cif_resolution[SRAM_CH03] = 1;
}else
{
dev->use_cif_resolution[SRAM_CH03] = 0;
}
dev->cif_width[SRAM_CH03] = fh->width;
medusa_set_resolution( dev, fh->width, SRAM_CH03 );
dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, fh->height, fh->vidq.field);
cx25821_call_all(dev, video, s_fmt, f);
return 0;
}
static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
int ret_val = 0;
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
ret_val = videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK);
p->sequence = dev->vidq[SRAM_CH03].count;
return ret_val;
}
static int vidioc_log_status (struct file *file, void *priv)
{
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
char name[32 + 2];
struct sram_channel *sram_ch = &dev->sram_channels[SRAM_CH03];
u32 tmp = 0;
snprintf(name, sizeof(name), "%s/2", dev->name);
printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n",
dev->name);
cx25821_call_all(dev, core, log_status);
tmp = cx_read(sram_ch->dma_ctl);
printk(KERN_INFO "Video input 3 is %s\n", (tmp & 0x11)?"streaming" : "stopped");
printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n",
dev->name);
return 0;
}
static int vidioc_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctl)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
int err;
if (fh) {
err = v4l2_prio_check(&dev->prio, &fh->prio);
if (0 != err)
return err;
}
return cx25821_set_control(dev, ctl, SRAM_CH03);
}
// exported stuff
static const struct v4l2_file_operations video_fops = {
.owner = THIS_MODULE,
.open = video_open,
.release = video_release,
.read = video_read,
.poll = video_poll,
.mmap = video_mmap,
.ioctl = video_ioctl2,
};
static const struct v4l2_ioctl_ops video_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
.vidioc_reqbufs = vidioc_reqbufs,
.vidioc_querybuf = vidioc_querybuf,
.vidioc_qbuf = vidioc_qbuf,
.vidioc_dqbuf = vidioc_dqbuf,
#ifdef TUNER_FLAG
.vidioc_s_std = vidioc_s_std,
.vidioc_querystd = vidioc_querystd,
#endif
.vidioc_cropcap = vidioc_cropcap,
.vidioc_s_crop = vidioc_s_crop,
.vidioc_g_crop = vidioc_g_crop,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
.vidioc_g_ctrl = vidioc_g_ctrl,
.vidioc_s_ctrl = vidioc_s_ctrl,
.vidioc_queryctrl = vidioc_queryctrl,
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
.vidioc_log_status = vidioc_log_status,
.vidioc_g_priority = vidioc_g_priority,
.vidioc_s_priority = vidioc_s_priority,
#ifdef CONFIG_VIDEO_V4L1_COMPAT
.vidiocgmbuf = vidiocgmbuf,
#endif
#ifdef TUNER_FLAG
.vidioc_g_tuner = vidioc_g_tuner,
.vidioc_s_tuner = vidioc_s_tuner,
.vidioc_g_frequency = vidioc_g_frequency,
.vidioc_s_frequency = vidioc_s_frequency,
#endif
#ifdef CONFIG_VIDEO_ADV_DEBUG
.vidioc_g_register = vidioc_g_register,
.vidioc_s_register = vidioc_s_register,
#endif
};
struct video_device cx25821_video_template3 = {
.name = "cx25821-video",
.fops = &video_fops,
.minor = -1,
.ioctl_ops = &video_ioctl_ops,
.tvnorms = CX25821_NORMS,
.current_norm = V4L2_STD_NTSC_M,
};

View File

@ -0,0 +1,456 @@
/*
* Driver for the Conexant CX25821 PCIe bridge
*
* Copyright (C) 2009 Conexant Systems Inc.
* Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
* Based on Steven Toth <stoth@linuxtv.org> cx23885 driver
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "cx25821-video.h"
static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
{
struct cx25821_buffer *buf = container_of(vb, struct cx25821_buffer, vb);
struct cx25821_buffer *prev;
struct cx25821_fh *fh = vq->priv_data;
struct cx25821_dev *dev = fh->dev;
struct cx25821_dmaqueue *q = &dev->vidq[SRAM_CH04];
/* 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);
buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]);
if (!list_empty(&q->queued)) {
list_add_tail(&buf->vb.queue, &q->queued);
buf->vb.state = VIDEOBUF_QUEUED;
dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, buf->vb.i);
} else if (list_empty(&q->active)) {
list_add_tail(&buf->vb.queue, &q->active);
cx25821_start_video_dma(dev, q, buf, &dev->sram_channels[SRAM_CH04]);
buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
dprintk(2, "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n",
buf, buf->vb. i, buf->count, q->count);
} else {
prev = list_entry(q->active.prev, struct cx25821_buffer, vb.queue);
if (prev->vb.width == buf->vb.width &&
prev->vb.height == buf->vb.height &&
prev->fmt == buf->fmt) {
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);
/* 64 bit bits 63-32 */
prev->risc.jmp[2] = cpu_to_le32(0);
dprintk(2, "[%p/%d] buffer_queue - append to active, buf->count=%d\n", buf, buf->vb.i, buf->count);
} else {
list_add_tail(&buf->vb.queue, &q->queued);
buf->vb.state = VIDEOBUF_QUEUED;
dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, buf->vb.i);
}
}
if (list_empty(&q->active))
{
dprintk(2, "active queue empty!\n");
}
}
static struct videobuf_queue_ops cx25821_video_qops = {
.buf_setup = buffer_setup,
.buf_prepare = buffer_prepare,
.buf_queue = buffer_queue,
.buf_release = buffer_release,
};
static int video_open(struct file *file)
{
int minor = video_devdata(file)->minor;
struct cx25821_dev *h, *dev = NULL;
struct cx25821_fh *fh;
struct list_head *list;
enum v4l2_buf_type type = 0;
u32 pix_format;
lock_kernel();
list_for_each(list, &cx25821_devlist)
{
h = list_entry(list, struct cx25821_dev, devlist);
if (h->video_dev[SRAM_CH04] && h->video_dev[SRAM_CH04]->minor == minor) {
dev = h;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
}
}
if (NULL == dev) {
unlock_kernel();
return -ENODEV;
}
printk("open minor=%d type=%s\n", minor, v4l2_type_names[type]);
/* allocate + initialize per filehandle data */
fh = kzalloc(sizeof(*fh), GFP_KERNEL);
if (NULL == fh) {
unlock_kernel();
return -ENOMEM;
}
file->private_data = fh;
fh->dev = dev;
fh->type = type;
fh->width = 720;
if(dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK)
fh->height = 576;
else
fh->height = 480;
dev->channel_opened = SRAM_CH04;
pix_format = (dev->pixel_formats[dev->channel_opened] == PIXEL_FRMT_411) ? V4L2_PIX_FMT_Y41P : V4L2_PIX_FMT_YUYV;
fh->fmt = format_by_fourcc(pix_format);
v4l2_prio_open(&dev->prio,&fh->prio);
videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops,
&dev->pci->dev, &dev->slock,
V4L2_BUF_TYPE_VIDEO_CAPTURE,
V4L2_FIELD_INTERLACED,
sizeof(struct cx25821_buffer),
fh);
dprintk(1, "post videobuf_queue_init()\n");
unlock_kernel();
return 0;
}
static ssize_t video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
{
struct cx25821_fh *fh = file->private_data;
switch (fh->type)
{
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
if (res_locked(fh->dev, RESOURCE_VIDEO4))
return -EBUSY;
return videobuf_read_one(&fh->vidq, data, count, ppos, file->f_flags & O_NONBLOCK);
default:
BUG();
return 0;
}
}
static unsigned int video_poll(struct file *file, struct poll_table_struct *wait)
{
struct cx25821_fh *fh = file->private_data;
struct cx25821_buffer *buf;
if (res_check(fh, RESOURCE_VIDEO4)) {
/* streaming capture */
if (list_empty(&fh->vidq.stream))
return POLLERR;
buf = list_entry(fh->vidq.stream.next,
struct cx25821_buffer, vb.stream);
} else {
/* read() capture */
buf = (struct cx25821_buffer *)fh->vidq.read_buf;
if (NULL == buf)
return POLLERR;
}
poll_wait(file, &buf->vb.done, wait);
if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR)
{
if( buf->vb.state == VIDEOBUF_DONE )
{
struct cx25821_dev *dev = fh->dev;
if( dev && dev->use_cif_resolution[SRAM_CH04] )
{
u8 cam_id = *((char*)buf->vb.baddr+3);
memcpy((char*)buf->vb.baddr, (char*)buf->vb.baddr + (fh->width * 2), (fh->width * 2));
*((char*)buf->vb.baddr+3) = cam_id;
}
}
return POLLIN|POLLRDNORM;
}
return 0;
}
static int video_release(struct file *file)
{
struct cx25821_fh *fh = file->private_data;
struct cx25821_dev *dev = fh->dev;
//stop the risc engine and fifo
cx_write(channel4->dma_ctl, 0); /* FIFO and RISC disable */
/* stop video capture */
if (res_check(fh, RESOURCE_VIDEO4)) {
videobuf_queue_cancel(&fh->vidq);
res_free(dev, fh, RESOURCE_VIDEO4);
}
if (fh->vidq.read_buf) {
buffer_release(&fh->vidq, fh->vidq.read_buf);
kfree(fh->vidq.read_buf);
}
videobuf_mmap_free(&fh->vidq);
v4l2_prio_close(&dev->prio,&fh->prio);
file->private_data = NULL;
kfree(fh);
return 0;
}
static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = fh->dev;
if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE))
{
return -EINVAL;
}
if (unlikely(i != fh->type))
{
return -EINVAL;
}
if (unlikely(!res_get(dev, fh, get_resource(fh, RESOURCE_VIDEO4))))
{
return -EBUSY;
}
return videobuf_streamon(get_queue(fh));
}
static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = fh->dev;
int err, res;
if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
if (i != fh->type)
return -EINVAL;
res = get_resource(fh, RESOURCE_VIDEO4);
err = videobuf_streamoff(get_queue(fh));
if (err < 0)
return err;
res_free(dev, fh, res);
return 0;
}
static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
int err;
int pix_format = 0;
// check priority
if (fh)
{
err = v4l2_prio_check(&dev->prio, &fh->prio);
if (0 != err)
return err;
}
dprintk(2, "%s()\n", __func__);
err = vidioc_try_fmt_vid_cap(file, priv, f);
if (0 != err)
return err;
fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
fh->vidq.field = f->fmt.pix.field;
// check if width and height is valid based on set standard
if (is_valid_width(f->fmt.pix.width, dev->tvnorm))
{
fh->width = f->fmt.pix.width;
}
if (is_valid_height(f->fmt.pix.height, dev->tvnorm))
{
fh->height = f->fmt.pix.height;
}
if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P)
pix_format = PIXEL_FRMT_411;
else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV)
pix_format = PIXEL_FRMT_422;
else
return -EINVAL;
cx25821_set_pixel_format( dev, SRAM_CH04, pix_format );
// check if cif resolution
if (fh->width == 320 || fh->width == 352)
{
dev->use_cif_resolution[SRAM_CH04] = 1;
}else
{
dev->use_cif_resolution[SRAM_CH04] = 0;
}
dev->cif_width[SRAM_CH04] = fh->width;
medusa_set_resolution( dev, fh->width, SRAM_CH04);
dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, fh->height, fh->vidq.field);
cx25821_call_all(dev, video, s_fmt, f);
return 0;
}
static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
int ret_val = 0;
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
ret_val = videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK);
p->sequence = dev->vidq[SRAM_CH04].count;
return ret_val;
}
static int vidioc_log_status (struct file *file, void *priv)
{
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
char name[32 + 2];
struct sram_channel *sram_ch = &dev->sram_channels[SRAM_CH04];
u32 tmp = 0;
snprintf(name, sizeof(name), "%s/2", dev->name);
printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n",
dev->name);
cx25821_call_all(dev, core, log_status);
tmp = cx_read(sram_ch->dma_ctl);
printk(KERN_INFO "Video input 4 is %s\n", (tmp & 0x11)?"streaming" : "stopped");
printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n",
dev->name);
return 0;
}
static int vidioc_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctl)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
int err;
if (fh) {
err = v4l2_prio_check(&dev->prio, &fh->prio);
if (0 != err)
return err;
}
return cx25821_set_control(dev, ctl, SRAM_CH04);
}
// exported stuff
static const struct v4l2_file_operations video_fops = {
.owner = THIS_MODULE,
.open = video_open,
.release = video_release,
.read = video_read,
.poll = video_poll,
.mmap = video_mmap,
.ioctl = video_ioctl2,
};
static const struct v4l2_ioctl_ops video_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
.vidioc_reqbufs = vidioc_reqbufs,
.vidioc_querybuf = vidioc_querybuf,
.vidioc_qbuf = vidioc_qbuf,
.vidioc_dqbuf = vidioc_dqbuf,
#ifdef TUNER_FLAG
.vidioc_s_std = vidioc_s_std,
.vidioc_querystd = vidioc_querystd,
#endif
.vidioc_cropcap = vidioc_cropcap,
.vidioc_s_crop = vidioc_s_crop,
.vidioc_g_crop = vidioc_g_crop,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
.vidioc_g_ctrl = vidioc_g_ctrl,
.vidioc_s_ctrl = vidioc_s_ctrl,
.vidioc_queryctrl = vidioc_queryctrl,
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
.vidioc_log_status = vidioc_log_status,
.vidioc_g_priority = vidioc_g_priority,
.vidioc_s_priority = vidioc_s_priority,
#ifdef CONFIG_VIDEO_V4L1_COMPAT
.vidiocgmbuf = vidiocgmbuf,
#endif
#ifdef TUNER_FLAG
.vidioc_g_tuner = vidioc_g_tuner,
.vidioc_s_tuner = vidioc_s_tuner,
.vidioc_g_frequency = vidioc_g_frequency,
.vidioc_s_frequency = vidioc_s_frequency,
#endif
#ifdef CONFIG_VIDEO_ADV_DEBUG
.vidioc_g_register = vidioc_g_register,
.vidioc_s_register = vidioc_s_register,
#endif
};
struct video_device cx25821_video_template4 = {
.name = "cx25821-video",
.fops = &video_fops,
.minor = -1,
.ioctl_ops = &video_ioctl_ops,
.tvnorms = CX25821_NORMS,
.current_norm = V4L2_STD_NTSC_M,
};

View File

@ -0,0 +1,455 @@
/*
* Driver for the Conexant CX25821 PCIe bridge
*
* Copyright (C) 2009 Conexant Systems Inc.
* Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
* Based on Steven Toth <stoth@linuxtv.org> cx23885 driver
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "cx25821-video.h"
static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
{
struct cx25821_buffer *buf = container_of(vb, struct cx25821_buffer, vb);
struct cx25821_buffer *prev;
struct cx25821_fh *fh = vq->priv_data;
struct cx25821_dev *dev = fh->dev;
struct cx25821_dmaqueue *q = &dev->vidq[SRAM_CH05];
/* 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);
buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]);
if (!list_empty(&q->queued)) {
list_add_tail(&buf->vb.queue, &q->queued);
buf->vb.state = VIDEOBUF_QUEUED;
dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, buf->vb.i);
} else if (list_empty(&q->active)) {
list_add_tail(&buf->vb.queue, &q->active);
cx25821_start_video_dma(dev, q, buf, &dev->sram_channels[SRAM_CH05]);
buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
dprintk(2, "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n",
buf, buf->vb. i, buf->count, q->count);
} else {
prev = list_entry(q->active.prev, struct cx25821_buffer, vb.queue);
if (prev->vb.width == buf->vb.width &&
prev->vb.height == buf->vb.height &&
prev->fmt == buf->fmt) {
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);
/* 64 bit bits 63-32 */
prev->risc.jmp[2] = cpu_to_le32(0);
dprintk(2, "[%p/%d] buffer_queue - append to active, buf->count=%d\n", buf, buf->vb.i, buf->count);
} else {
list_add_tail(&buf->vb.queue, &q->queued);
buf->vb.state = VIDEOBUF_QUEUED;
dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, buf->vb.i);
}
}
if (list_empty(&q->active))
{
dprintk(2, "active queue empty!\n");
}
}
static struct videobuf_queue_ops cx25821_video_qops = {
.buf_setup = buffer_setup,
.buf_prepare = buffer_prepare,
.buf_queue = buffer_queue,
.buf_release = buffer_release,
};
static int video_open(struct file *file)
{
int minor = video_devdata(file)->minor;
struct cx25821_dev *h, *dev = NULL;
struct cx25821_fh *fh;
struct list_head *list;
enum v4l2_buf_type type = 0;
u32 pix_format;
lock_kernel();
list_for_each(list, &cx25821_devlist)
{
h = list_entry(list, struct cx25821_dev, devlist);
if (h->video_dev[SRAM_CH05] && h->video_dev[SRAM_CH05]->minor == minor) {
dev = h;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
}
}
if (NULL == dev) {
unlock_kernel();
return -ENODEV;
}
printk("open minor=%d type=%s\n", minor, v4l2_type_names[type]);
/* allocate + initialize per filehandle data */
fh = kzalloc(sizeof(*fh), GFP_KERNEL);
if (NULL == fh) {
unlock_kernel();
return -ENOMEM;
}
file->private_data = fh;
fh->dev = dev;
fh->type = type;
fh->width = 720;
if(dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK)
fh->height = 576;
else
fh->height = 480;
dev->channel_opened = SRAM_CH05;
pix_format = (dev->pixel_formats[dev->channel_opened] == PIXEL_FRMT_411) ? V4L2_PIX_FMT_Y41P : V4L2_PIX_FMT_YUYV;
fh->fmt = format_by_fourcc(pix_format);
v4l2_prio_open(&dev->prio,&fh->prio);
videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops,
&dev->pci->dev, &dev->slock,
V4L2_BUF_TYPE_VIDEO_CAPTURE,
V4L2_FIELD_INTERLACED,
sizeof(struct cx25821_buffer),
fh);
dprintk(1, "post videobuf_queue_init()\n");
unlock_kernel();
return 0;
}
static ssize_t video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
{
struct cx25821_fh *fh = file->private_data;
switch (fh->type)
{
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
if (res_locked(fh->dev, RESOURCE_VIDEO5))
return -EBUSY;
return videobuf_read_one(&fh->vidq, data, count, ppos, file->f_flags & O_NONBLOCK);
default:
BUG();
return 0;
}
}
static unsigned int video_poll(struct file *file, struct poll_table_struct *wait)
{
struct cx25821_fh *fh = file->private_data;
struct cx25821_buffer *buf;
if (res_check(fh, RESOURCE_VIDEO5)) {
/* streaming capture */
if (list_empty(&fh->vidq.stream))
return POLLERR;
buf = list_entry(fh->vidq.stream.next,
struct cx25821_buffer, vb.stream);
} else {
/* read() capture */
buf = (struct cx25821_buffer *)fh->vidq.read_buf;
if (NULL == buf)
return POLLERR;
}
poll_wait(file, &buf->vb.done, wait);
if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR)
{
if( buf->vb.state == VIDEOBUF_DONE )
{
struct cx25821_dev *dev = fh->dev;
if( dev && dev->use_cif_resolution[SRAM_CH05] )
{
u8 cam_id = *((char*)buf->vb.baddr+3);
memcpy((char*)buf->vb.baddr, (char*)buf->vb.baddr + (fh->width * 2), (fh->width * 2));
*((char*)buf->vb.baddr+3) = cam_id;
}
}
return POLLIN|POLLRDNORM;
}
return 0;
}
static int video_release(struct file *file)
{
struct cx25821_fh *fh = file->private_data;
struct cx25821_dev *dev = fh->dev;
//stop the risc engine and fifo
cx_write(channel5->dma_ctl, 0); /* FIFO and RISC disable */
/* stop video capture */
if (res_check(fh, RESOURCE_VIDEO5)) {
videobuf_queue_cancel(&fh->vidq);
res_free(dev, fh, RESOURCE_VIDEO5);
}
if (fh->vidq.read_buf) {
buffer_release(&fh->vidq, fh->vidq.read_buf);
kfree(fh->vidq.read_buf);
}
videobuf_mmap_free(&fh->vidq);
v4l2_prio_close(&dev->prio,&fh->prio);
file->private_data = NULL;
kfree(fh);
return 0;
}
static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = fh->dev;
if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE))
{
return -EINVAL;
}
if (unlikely(i != fh->type))
{
return -EINVAL;
}
if (unlikely(!res_get(dev, fh, get_resource(fh, RESOURCE_VIDEO5))))
{
return -EBUSY;
}
return videobuf_streamon(get_queue(fh));
}
static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = fh->dev;
int err, res;
if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
if (i != fh->type)
return -EINVAL;
res = get_resource(fh, RESOURCE_VIDEO5);
err = videobuf_streamoff(get_queue(fh));
if (err < 0)
return err;
res_free(dev, fh, res);
return 0;
}
static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
int err;
int pix_format = 0;
if (fh)
{
err = v4l2_prio_check(&dev->prio, &fh->prio);
if (0 != err)
return err;
}
dprintk(2, "%s()\n", __func__);
err = vidioc_try_fmt_vid_cap(file, priv, f);
if (0 != err)
return err;
fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
fh->vidq.field = f->fmt.pix.field;
// check if width and height is valid based on set standard
if (is_valid_width(f->fmt.pix.width, dev->tvnorm))
{
fh->width = f->fmt.pix.width;
}
if (is_valid_height(f->fmt.pix.height, dev->tvnorm))
{
fh->height = f->fmt.pix.height;
}
if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P)
pix_format = PIXEL_FRMT_411;
else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV)
pix_format = PIXEL_FRMT_422;
else
return -EINVAL;
cx25821_set_pixel_format( dev, SRAM_CH05, pix_format );
// check if cif resolution
if (fh->width == 320 || fh->width == 352)
{
dev->use_cif_resolution[SRAM_CH05] = 1;
}else
{
dev->use_cif_resolution[SRAM_CH05] = 0;
}
dev->cif_width[SRAM_CH05] = fh->width;
medusa_set_resolution( dev, fh->width, SRAM_CH05 );
dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, fh->height, fh->vidq.field);
cx25821_call_all(dev, video, s_fmt, f);
return 0;
}
static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
int ret_val = 0;
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
ret_val = videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK);
p->sequence = dev->vidq[SRAM_CH05].count;
return ret_val;
}
static int vidioc_log_status (struct file *file, void *priv)
{
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
char name[32 + 2];
struct sram_channel *sram_ch = &dev->sram_channels[SRAM_CH05];
u32 tmp = 0;
snprintf(name, sizeof(name), "%s/2", dev->name);
printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n",
dev->name);
cx25821_call_all(dev, core, log_status);
tmp = cx_read(sram_ch->dma_ctl);
printk(KERN_INFO "Video input 5 is %s\n", (tmp & 0x11)?"streaming" : "stopped");
printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n",
dev->name);
return 0;
}
static int vidioc_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctl)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
int err;
if (fh) {
err = v4l2_prio_check(&dev->prio, &fh->prio);
if (0 != err)
return err;
}
return cx25821_set_control(dev, ctl, SRAM_CH05);
}
// exported stuff
static const struct v4l2_file_operations video_fops = {
.owner = THIS_MODULE,
.open = video_open,
.release = video_release,
.read = video_read,
.poll = video_poll,
.mmap = video_mmap,
.ioctl = video_ioctl2,
};
static const struct v4l2_ioctl_ops video_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
.vidioc_reqbufs = vidioc_reqbufs,
.vidioc_querybuf = vidioc_querybuf,
.vidioc_qbuf = vidioc_qbuf,
.vidioc_dqbuf = vidioc_dqbuf,
#ifdef TUNER_FLAG
.vidioc_s_std = vidioc_s_std,
.vidioc_querystd = vidioc_querystd,
#endif
.vidioc_cropcap = vidioc_cropcap,
.vidioc_s_crop = vidioc_s_crop,
.vidioc_g_crop = vidioc_g_crop,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
.vidioc_g_ctrl = vidioc_g_ctrl,
.vidioc_s_ctrl = vidioc_s_ctrl,
.vidioc_queryctrl = vidioc_queryctrl,
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
.vidioc_log_status = vidioc_log_status,
.vidioc_g_priority = vidioc_g_priority,
.vidioc_s_priority = vidioc_s_priority,
#ifdef CONFIG_VIDEO_V4L1_COMPAT
.vidiocgmbuf = vidiocgmbuf,
#endif
#ifdef TUNER_FLAG
.vidioc_g_tuner = vidioc_g_tuner,
.vidioc_s_tuner = vidioc_s_tuner,
.vidioc_g_frequency = vidioc_g_frequency,
.vidioc_s_frequency = vidioc_s_frequency,
#endif
#ifdef CONFIG_VIDEO_ADV_DEBUG
.vidioc_g_register = vidioc_g_register,
.vidioc_s_register = vidioc_s_register,
#endif
};
struct video_device cx25821_video_template5 = {
.name = "cx25821-video",
.fops = &video_fops,
.minor = -1,
.ioctl_ops = &video_ioctl_ops,
.tvnorms = CX25821_NORMS,
.current_norm = V4L2_STD_NTSC_M,
};

View File

@ -0,0 +1,455 @@
/*
* Driver for the Conexant CX25821 PCIe bridge
*
* Copyright (C) 2009 Conexant Systems Inc.
* Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
* Based on Steven Toth <stoth@linuxtv.org> cx23885 driver
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "cx25821-video.h"
static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
{
struct cx25821_buffer *buf = container_of(vb, struct cx25821_buffer, vb);
struct cx25821_buffer *prev;
struct cx25821_fh *fh = vq->priv_data;
struct cx25821_dev *dev = fh->dev;
struct cx25821_dmaqueue *q = &dev->vidq[SRAM_CH06];
/* 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);
buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]);
if (!list_empty(&q->queued)) {
list_add_tail(&buf->vb.queue, &q->queued);
buf->vb.state = VIDEOBUF_QUEUED;
dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, buf->vb.i);
} else if (list_empty(&q->active)) {
list_add_tail(&buf->vb.queue, &q->active);
cx25821_start_video_dma(dev, q, buf, &dev->sram_channels[SRAM_CH06]);
buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
dprintk(2, "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n",
buf, buf->vb. i, buf->count, q->count);
} else {
prev = list_entry(q->active.prev, struct cx25821_buffer, vb.queue);
if (prev->vb.width == buf->vb.width &&
prev->vb.height == buf->vb.height &&
prev->fmt == buf->fmt) {
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);
/* 64 bit bits 63-32 */
prev->risc.jmp[2] = cpu_to_le32(0);
dprintk(2, "[%p/%d] buffer_queue - append to active, buf->count=%d\n", buf, buf->vb.i, buf->count);
} else {
list_add_tail(&buf->vb.queue, &q->queued);
buf->vb.state = VIDEOBUF_QUEUED;
dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, buf->vb.i);
}
}
if (list_empty(&q->active))
{
dprintk(2, "active queue empty!\n");
}
}
static struct videobuf_queue_ops cx25821_video_qops = {
.buf_setup = buffer_setup,
.buf_prepare = buffer_prepare,
.buf_queue = buffer_queue,
.buf_release = buffer_release,
};
static int video_open(struct file *file)
{
int minor = video_devdata(file)->minor;
struct cx25821_dev *h, *dev = NULL;
struct cx25821_fh *fh;
struct list_head *list;
enum v4l2_buf_type type = 0;
u32 pix_format;
lock_kernel();
list_for_each(list, &cx25821_devlist)
{
h = list_entry(list, struct cx25821_dev, devlist);
if (h->video_dev[SRAM_CH06] && h->video_dev[SRAM_CH06]->minor == minor) {
dev = h;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
}
}
if (NULL == dev) {
unlock_kernel();
return -ENODEV;
}
printk("open minor=%d type=%s\n", minor, v4l2_type_names[type]);
/* allocate + initialize per filehandle data */
fh = kzalloc(sizeof(*fh), GFP_KERNEL);
if (NULL == fh) {
unlock_kernel();
return -ENOMEM;
}
file->private_data = fh;
fh->dev = dev;
fh->type = type;
fh->width = 720;
if(dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK)
fh->height = 576;
else
fh->height = 480;
dev->channel_opened = SRAM_CH06;
pix_format = (dev->pixel_formats[dev->channel_opened] == PIXEL_FRMT_411) ? V4L2_PIX_FMT_Y41P : V4L2_PIX_FMT_YUYV;
fh->fmt = format_by_fourcc(pix_format);
v4l2_prio_open(&dev->prio,&fh->prio);
videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops,
&dev->pci->dev, &dev->slock,
V4L2_BUF_TYPE_VIDEO_CAPTURE,
V4L2_FIELD_INTERLACED,
sizeof(struct cx25821_buffer),
fh);
dprintk(1, "post videobuf_queue_init()\n");
unlock_kernel();
return 0;
}
static ssize_t video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
{
struct cx25821_fh *fh = file->private_data;
switch (fh->type)
{
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
if (res_locked(fh->dev, RESOURCE_VIDEO6))
return -EBUSY;
return videobuf_read_one(&fh->vidq, data, count, ppos, file->f_flags & O_NONBLOCK);
default:
BUG();
return 0;
}
}
static unsigned int video_poll(struct file *file, struct poll_table_struct *wait)
{
struct cx25821_fh *fh = file->private_data;
struct cx25821_buffer *buf;
if (res_check(fh, RESOURCE_VIDEO6)) {
/* streaming capture */
if (list_empty(&fh->vidq.stream))
return POLLERR;
buf = list_entry(fh->vidq.stream.next,
struct cx25821_buffer, vb.stream);
} else {
/* read() capture */
buf = (struct cx25821_buffer *)fh->vidq.read_buf;
if (NULL == buf)
return POLLERR;
}
poll_wait(file, &buf->vb.done, wait);
if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR)
{
if( buf->vb.state == VIDEOBUF_DONE )
{
struct cx25821_dev *dev = fh->dev;
if( dev && dev->use_cif_resolution[SRAM_CH06] )
{
u8 cam_id = *((char*)buf->vb.baddr+3);
memcpy((char*)buf->vb.baddr, (char*)buf->vb.baddr + (fh->width * 2), (fh->width * 2));
*((char*)buf->vb.baddr+3) = cam_id;
}
}
return POLLIN|POLLRDNORM;
}
return 0;
}
static int video_release(struct file *file)
{
struct cx25821_fh *fh = file->private_data;
struct cx25821_dev *dev = fh->dev;
//stop the risc engine and fifo
cx_write(channel6->dma_ctl, 0); /* FIFO and RISC disable */
/* stop video capture */
if (res_check(fh, RESOURCE_VIDEO6)) {
videobuf_queue_cancel(&fh->vidq);
res_free(dev, fh, RESOURCE_VIDEO6);
}
if (fh->vidq.read_buf) {
buffer_release(&fh->vidq, fh->vidq.read_buf);
kfree(fh->vidq.read_buf);
}
videobuf_mmap_free(&fh->vidq);
v4l2_prio_close(&dev->prio,&fh->prio);
file->private_data = NULL;
kfree(fh);
return 0;
}
static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = fh->dev;
if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE))
{
return -EINVAL;
}
if (unlikely(i != fh->type))
{
return -EINVAL;
}
if (unlikely(!res_get(dev, fh, get_resource(fh, RESOURCE_VIDEO6))))
{
return -EBUSY;
}
return videobuf_streamon(get_queue(fh));
}
static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = fh->dev;
int err, res;
if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
if (i != fh->type)
return -EINVAL;
res = get_resource(fh, RESOURCE_VIDEO6);
err = videobuf_streamoff(get_queue(fh));
if (err < 0)
return err;
res_free(dev, fh, res);
return 0;
}
static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
int err;
int pix_format = 0;
if (fh)
{
err = v4l2_prio_check(&dev->prio, &fh->prio);
if (0 != err)
return err;
}
dprintk(2, "%s()\n", __func__);
err = vidioc_try_fmt_vid_cap(file, priv, f);
if (0 != err)
return err;
fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
fh->vidq.field = f->fmt.pix.field;
// check if width and height is valid based on set standard
if (is_valid_width(f->fmt.pix.width, dev->tvnorm))
{
fh->width = f->fmt.pix.width;
}
if (is_valid_height(f->fmt.pix.height, dev->tvnorm))
{
fh->height = f->fmt.pix.height;
}
if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P)
pix_format = PIXEL_FRMT_411;
else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV)
pix_format = PIXEL_FRMT_422;
else
return -EINVAL;
cx25821_set_pixel_format( dev, SRAM_CH06, pix_format );
// check if cif resolution
if (fh->width == 320 || fh->width == 352)
{
dev->use_cif_resolution[SRAM_CH06] = 1;
}else
{
dev->use_cif_resolution[SRAM_CH06] = 0;
}
dev->cif_width[SRAM_CH06] = fh->width;
medusa_set_resolution( dev, fh->width, SRAM_CH06 );
dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, fh->height, fh->vidq.field);
cx25821_call_all(dev, video, s_fmt, f);
return 0;
}
static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
int ret_val = 0;
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
ret_val = videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK);
p->sequence = dev->vidq[SRAM_CH06].count;
return ret_val;
}
static int vidioc_log_status (struct file *file, void *priv)
{
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
char name[32 + 2];
struct sram_channel *sram_ch = &dev->sram_channels[SRAM_CH06];
u32 tmp = 0;
snprintf(name, sizeof(name), "%s/2", dev->name);
printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n",
dev->name);
cx25821_call_all(dev, core, log_status);
tmp = cx_read(sram_ch->dma_ctl);
printk(KERN_INFO "Video input 6 is %s\n", (tmp & 0x11)?"streaming" : "stopped");
printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n",
dev->name);
return 0;
}
static int vidioc_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctl)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
int err;
if (fh) {
err = v4l2_prio_check(&dev->prio, &fh->prio);
if (0 != err)
return err;
}
return cx25821_set_control(dev, ctl, SRAM_CH06);
}
// exported stuff
static const struct v4l2_file_operations video_fops = {
.owner = THIS_MODULE,
.open = video_open,
.release = video_release,
.read = video_read,
.poll = video_poll,
.mmap = video_mmap,
.ioctl = video_ioctl2,
};
static const struct v4l2_ioctl_ops video_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
.vidioc_reqbufs = vidioc_reqbufs,
.vidioc_querybuf = vidioc_querybuf,
.vidioc_qbuf = vidioc_qbuf,
.vidioc_dqbuf = vidioc_dqbuf,
#ifdef TUNER_FLAG
.vidioc_s_std = vidioc_s_std,
.vidioc_querystd = vidioc_querystd,
#endif
.vidioc_cropcap = vidioc_cropcap,
.vidioc_s_crop = vidioc_s_crop,
.vidioc_g_crop = vidioc_g_crop,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
.vidioc_g_ctrl = vidioc_g_ctrl,
.vidioc_s_ctrl = vidioc_s_ctrl,
.vidioc_queryctrl = vidioc_queryctrl,
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
.vidioc_log_status = vidioc_log_status,
.vidioc_g_priority = vidioc_g_priority,
.vidioc_s_priority = vidioc_s_priority,
#ifdef CONFIG_VIDEO_V4L1_COMPAT
.vidiocgmbuf = vidiocgmbuf,
#endif
#ifdef TUNER_FLAG
.vidioc_g_tuner = vidioc_g_tuner,
.vidioc_s_tuner = vidioc_s_tuner,
.vidioc_g_frequency = vidioc_g_frequency,
.vidioc_s_frequency = vidioc_s_frequency,
#endif
#ifdef CONFIG_VIDEO_ADV_DEBUG
.vidioc_g_register = vidioc_g_register,
.vidioc_s_register = vidioc_s_register,
#endif
};
struct video_device cx25821_video_template6 = {
.name = "cx25821-video",
.fops = &video_fops,
.minor = -1,
.ioctl_ops = &video_ioctl_ops,
.tvnorms = CX25821_NORMS,
.current_norm = V4L2_STD_NTSC_M,
};

View File

@ -0,0 +1,454 @@
/*
* Driver for the Conexant CX25821 PCIe bridge
*
* Copyright (C) 2009 Conexant Systems Inc.
* Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
* Based on Steven Toth <stoth@linuxtv.org> cx23885 driver
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "cx25821-video.h"
static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
{
struct cx25821_buffer *buf = container_of(vb, struct cx25821_buffer, vb);
struct cx25821_buffer *prev;
struct cx25821_fh *fh = vq->priv_data;
struct cx25821_dev *dev = fh->dev;
struct cx25821_dmaqueue *q = &dev->vidq[SRAM_CH07];
/* 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);
buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]);
if (!list_empty(&q->queued)) {
list_add_tail(&buf->vb.queue, &q->queued);
buf->vb.state = VIDEOBUF_QUEUED;
dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, buf->vb.i);
} else if (list_empty(&q->active)) {
list_add_tail(&buf->vb.queue, &q->active);
cx25821_start_video_dma(dev, q, buf, &dev->sram_channels[SRAM_CH07]);
buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
dprintk(2, "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n",
buf, buf->vb. i, buf->count, q->count);
} else {
prev = list_entry(q->active.prev, struct cx25821_buffer, vb.queue);
if (prev->vb.width == buf->vb.width &&
prev->vb.height == buf->vb.height &&
prev->fmt == buf->fmt) {
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);
/* 64 bit bits 63-32 */
prev->risc.jmp[2] = cpu_to_le32(0);
dprintk(2, "[%p/%d] buffer_queue - append to active, buf->count=%d\n", buf, buf->vb.i, buf->count);
} else {
list_add_tail(&buf->vb.queue, &q->queued);
buf->vb.state = VIDEOBUF_QUEUED;
dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, buf->vb.i);
}
}
if (list_empty(&q->active))
{
dprintk(2, "active queue empty!\n");
}
}
static struct videobuf_queue_ops cx25821_video_qops = {
.buf_setup = buffer_setup,
.buf_prepare = buffer_prepare,
.buf_queue = buffer_queue,
.buf_release = buffer_release,
};
static int video_open(struct file *file)
{
int minor = video_devdata(file)->minor;
struct cx25821_dev *h, *dev = NULL;
struct cx25821_fh *fh;
struct list_head *list;
enum v4l2_buf_type type = 0;
u32 pix_format;
lock_kernel();
list_for_each(list, &cx25821_devlist)
{
h = list_entry(list, struct cx25821_dev, devlist);
if (h->video_dev[SRAM_CH07] && h->video_dev[SRAM_CH07]->minor == minor) {
dev = h;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
}
}
if (NULL == dev) {
unlock_kernel();
return -ENODEV;
}
printk("open minor=%d type=%s\n", minor, v4l2_type_names[type]);
/* allocate + initialize per filehandle data */
fh = kzalloc(sizeof(*fh), GFP_KERNEL);
if (NULL == fh) {
unlock_kernel();
return -ENOMEM;
}
file->private_data = fh;
fh->dev = dev;
fh->type = type;
fh->width = 720;
if(dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK)
fh->height = 576;
else
fh->height = 480;
dev->channel_opened = SRAM_CH07;
pix_format = (dev->pixel_formats[dev->channel_opened] == PIXEL_FRMT_411) ? V4L2_PIX_FMT_Y41P : V4L2_PIX_FMT_YUYV;
fh->fmt = format_by_fourcc(pix_format);
v4l2_prio_open(&dev->prio,&fh->prio);
videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops,
&dev->pci->dev, &dev->slock,
V4L2_BUF_TYPE_VIDEO_CAPTURE,
V4L2_FIELD_INTERLACED,
sizeof(struct cx25821_buffer),
fh);
dprintk(1, "post videobuf_queue_init()\n");
unlock_kernel();
return 0;
}
static ssize_t video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
{
struct cx25821_fh *fh = file->private_data;
switch (fh->type)
{
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
if (res_locked(fh->dev, RESOURCE_VIDEO7))
return -EBUSY;
return videobuf_read_one(&fh->vidq, data, count, ppos, file->f_flags & O_NONBLOCK);
default:
BUG();
return 0;
}
}
static unsigned int video_poll(struct file *file, struct poll_table_struct *wait)
{
struct cx25821_fh *fh = file->private_data;
struct cx25821_buffer *buf;
if (res_check(fh, RESOURCE_VIDEO7)) {
/* streaming capture */
if (list_empty(&fh->vidq.stream))
return POLLERR;
buf = list_entry(fh->vidq.stream.next,
struct cx25821_buffer, vb.stream);
} else {
/* read() capture */
buf = (struct cx25821_buffer *)fh->vidq.read_buf;
if (NULL == buf)
return POLLERR;
}
poll_wait(file, &buf->vb.done, wait);
if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR)
{
if( buf->vb.state == VIDEOBUF_DONE )
{
struct cx25821_dev *dev = fh->dev;
if( dev && dev->use_cif_resolution[SRAM_CH07] )
{
u8 cam_id = *((char*)buf->vb.baddr+3);
memcpy((char*)buf->vb.baddr, (char*)buf->vb.baddr + (fh->width * 2), (fh->width * 2));
*((char*)buf->vb.baddr+3) = cam_id;
}
}
return POLLIN|POLLRDNORM;
}
return 0;
}
static int video_release(struct file *file)
{
struct cx25821_fh *fh = file->private_data;
struct cx25821_dev *dev = fh->dev;
//stop the risc engine and fifo
cx_write(channel7->dma_ctl, 0); /* FIFO and RISC disable */
/* stop video capture */
if (res_check(fh, RESOURCE_VIDEO7)) {
videobuf_queue_cancel(&fh->vidq);
res_free(dev, fh, RESOURCE_VIDEO7);
}
if (fh->vidq.read_buf) {
buffer_release(&fh->vidq, fh->vidq.read_buf);
kfree(fh->vidq.read_buf);
}
videobuf_mmap_free(&fh->vidq);
v4l2_prio_close(&dev->prio,&fh->prio);
file->private_data = NULL;
kfree(fh);
return 0;
}
static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = fh->dev;
if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE))
{
return -EINVAL;
}
if (unlikely(i != fh->type))
{
return -EINVAL;
}
if (unlikely(!res_get(dev, fh, get_resource(fh, RESOURCE_VIDEO7))))
{
return -EBUSY;
}
return videobuf_streamon(get_queue(fh));
}
static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = fh->dev;
int err, res;
if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
if (i != fh->type)
return -EINVAL;
res = get_resource(fh, RESOURCE_VIDEO7);
err = videobuf_streamoff(get_queue(fh));
if (err < 0)
return err;
res_free(dev, fh, res);
return 0;
}
static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
int err;
int pix_format = 0;
if (fh)
{
err = v4l2_prio_check(&dev->prio, &fh->prio);
if (0 != err)
return err;
}
dprintk(2, "%s()\n", __func__);
err = vidioc_try_fmt_vid_cap(file, priv, f);
if (0 != err)
return err;
fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
fh->vidq.field = f->fmt.pix.field;
// check if width and height is valid based on set standard
if (is_valid_width(f->fmt.pix.width, dev->tvnorm))
{
fh->width = f->fmt.pix.width;
}
if (is_valid_height(f->fmt.pix.height, dev->tvnorm))
{
fh->height = f->fmt.pix.height;
}
if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P)
pix_format = PIXEL_FRMT_411;
else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV)
pix_format = PIXEL_FRMT_422;
else
return -EINVAL;
cx25821_set_pixel_format( dev, SRAM_CH07, pix_format );
// check if cif resolution
if (fh->width == 320 || fh->width == 352)
{
dev->use_cif_resolution[SRAM_CH07] = 1;
}else
{
dev->use_cif_resolution[SRAM_CH07] = 0;
}
dev->cif_width[SRAM_CH07] = fh->width;
medusa_set_resolution( dev, fh->width, SRAM_CH07 );
dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, fh->height, fh->vidq.field);
cx25821_call_all(dev, video, s_fmt, f);
return 0;
}
static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
int ret_val = 0;
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
ret_val = videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK);
p->sequence = dev->vidq[SRAM_CH07].count;
return ret_val;
}
static int vidioc_log_status (struct file *file, void *priv)
{
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
char name[32 + 2];
struct sram_channel *sram_ch = &dev->sram_channels[SRAM_CH07];
u32 tmp = 0;
snprintf(name, sizeof(name), "%s/2", dev->name);
printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n",
dev->name);
cx25821_call_all(dev, core, log_status);
tmp = cx_read(sram_ch->dma_ctl);
printk(KERN_INFO "Video input 7 is %s\n", (tmp & 0x11)?"streaming" : "stopped");
printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n",
dev->name);
return 0;
}
static int vidioc_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctl)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
int err;
if (fh) {
err = v4l2_prio_check(&dev->prio, &fh->prio);
if (0 != err)
return err;
}
return cx25821_set_control(dev, ctl, SRAM_CH07);
}
// exported stuff
static const struct v4l2_file_operations video_fops = {
.owner = THIS_MODULE,
.open = video_open,
.release = video_release,
.read = video_read,
.poll = video_poll,
.mmap = video_mmap,
.ioctl = video_ioctl2,
};
static const struct v4l2_ioctl_ops video_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
.vidioc_reqbufs = vidioc_reqbufs,
.vidioc_querybuf = vidioc_querybuf,
.vidioc_qbuf = vidioc_qbuf,
.vidioc_dqbuf = vidioc_dqbuf,
#ifdef TUNER_FLAG
.vidioc_s_std = vidioc_s_std,
.vidioc_querystd = vidioc_querystd,
#endif
.vidioc_cropcap = vidioc_cropcap,
.vidioc_s_crop = vidioc_s_crop,
.vidioc_g_crop = vidioc_g_crop,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
.vidioc_g_ctrl = vidioc_g_ctrl,
.vidioc_s_ctrl = vidioc_s_ctrl,
.vidioc_queryctrl = vidioc_queryctrl,
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
.vidioc_log_status = vidioc_log_status,
.vidioc_g_priority = vidioc_g_priority,
.vidioc_s_priority = vidioc_s_priority,
#ifdef CONFIG_VIDEO_V4L1_COMPAT
.vidiocgmbuf = vidiocgmbuf,
#endif
#ifdef TUNER_FLAG
.vidioc_g_tuner = vidioc_g_tuner,
.vidioc_s_tuner = vidioc_s_tuner,
.vidioc_g_frequency = vidioc_g_frequency,
.vidioc_s_frequency = vidioc_s_frequency,
#endif
#ifdef CONFIG_VIDEO_ADV_DEBUG
.vidioc_g_register = vidioc_g_register,
.vidioc_s_register = vidioc_s_register,
#endif
};
struct video_device cx25821_video_template7 = {
.name = "cx25821-video",
.fops = &video_fops,
.minor = -1,
.ioctl_ops = &video_ioctl_ops,
.tvnorms = CX25821_NORMS,
.current_norm = V4L2_STD_NTSC_M,
};

View File

@ -0,0 +1,500 @@
/*
* Driver for the Conexant CX25821 PCIe bridge
*
* Copyright (C) 2009 Conexant Systems Inc.
* Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
* Based on Steven Toth <stoth@linuxtv.org> cx23885 driver
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "cx25821-video.h"
static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
{
struct cx25821_buffer *buf = container_of(vb, struct cx25821_buffer, vb);
struct cx25821_buffer *prev;
struct cx25821_fh *fh = vq->priv_data;
struct cx25821_dev *dev = fh->dev;
struct cx25821_dmaqueue *q = &dev->vidq[VIDEO_IOCTL_CH];
/* 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);
buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]);
if (!list_empty(&q->queued)) {
list_add_tail(&buf->vb.queue, &q->queued);
buf->vb.state = VIDEOBUF_QUEUED;
dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, buf->vb.i);
} else if (list_empty(&q->active)) {
list_add_tail(&buf->vb.queue, &q->active);
cx25821_start_video_dma(dev, q, buf, &dev->sram_channels[VIDEO_IOCTL_CH]);
buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
dprintk(2, "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n",
buf, buf->vb. i, buf->count, q->count);
} else {
prev = list_entry(q->active.prev, struct cx25821_buffer, vb.queue);
if (prev->vb.width == buf->vb.width &&
prev->vb.height == buf->vb.height &&
prev->fmt == buf->fmt) {
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);
/* 64 bit bits 63-32 */
prev->risc.jmp[2] = cpu_to_le32(0);
dprintk(2, "[%p/%d] buffer_queue - append to active, buf->count=%d\n", buf, buf->vb.i, buf->count);
} else {
list_add_tail(&buf->vb.queue, &q->queued);
buf->vb.state = VIDEOBUF_QUEUED;
dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, buf->vb.i);
}
}
if (list_empty(&q->active))
{
dprintk(2, "active queue empty!\n");
}
}
static struct videobuf_queue_ops cx25821_video_qops = {
.buf_setup = buffer_setup,
.buf_prepare = buffer_prepare,
.buf_queue = buffer_queue,
.buf_release = buffer_release,
};
static int video_open(struct file *file)
{
int minor = video_devdata(file)->minor;
struct cx25821_dev *h, *dev = NULL;
struct cx25821_fh *fh;
struct list_head *list;
enum v4l2_buf_type type = 0;
u32 pix_format;
lock_kernel();
list_for_each(list, &cx25821_devlist)
{
h = list_entry(list, struct cx25821_dev, devlist);
if (h->ioctl_dev && h->ioctl_dev->minor == minor)
{
dev = h;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
}
}
if (NULL == dev) {
unlock_kernel();
return -ENODEV;
}
printk("open minor=%d type=%s\n", minor, v4l2_type_names[type]);
/* allocate + initialize per filehandle data */
fh = kzalloc(sizeof(*fh), GFP_KERNEL);
if (NULL == fh) {
unlock_kernel();
return -ENOMEM;
}
file->private_data = fh;
fh->dev = dev;
fh->type = type;
fh->width = 720;
if(dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK)
fh->height = 576;
else
fh->height = 480;
dev->channel_opened = VIDEO_IOCTL_CH;
pix_format = V4L2_PIX_FMT_YUYV;
fh->fmt = format_by_fourcc(pix_format);
v4l2_prio_open(&dev->prio,&fh->prio);
videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops,
&dev->pci->dev, &dev->slock,
V4L2_BUF_TYPE_VIDEO_CAPTURE,
V4L2_FIELD_INTERLACED,
sizeof(struct cx25821_buffer),
fh);
dprintk(1, "post videobuf_queue_init()\n");
unlock_kernel();
return 0;
}
static ssize_t video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
{
struct cx25821_fh *fh = file->private_data;
switch (fh->type)
{
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
if (res_locked(fh->dev, RESOURCE_VIDEO_IOCTL))
return -EBUSY;
return videobuf_read_one(&fh->vidq, data, count, ppos, file->f_flags & O_NONBLOCK);
default:
BUG();
return 0;
}
}
static unsigned int video_poll(struct file *file, struct poll_table_struct *wait)
{
struct cx25821_fh *fh = file->private_data;
struct cx25821_buffer *buf;
if (res_check(fh, RESOURCE_VIDEO_IOCTL)) {
/* streaming capture */
if (list_empty(&fh->vidq.stream))
return POLLERR;
buf = list_entry(fh->vidq.stream.next,
struct cx25821_buffer, vb.stream);
} else {
/* read() capture */
buf = (struct cx25821_buffer *)fh->vidq.read_buf;
if (NULL == buf)
return POLLERR;
}
poll_wait(file, &buf->vb.done, wait);
if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR)
return POLLIN|POLLRDNORM;
return 0;
}
static int video_release(struct file *file)
{
struct cx25821_fh *fh = file->private_data;
struct cx25821_dev *dev = fh->dev;
/* stop video capture */
if (res_check(fh, RESOURCE_VIDEO_IOCTL)) {
videobuf_queue_cancel(&fh->vidq);
res_free(dev, fh, RESOURCE_VIDEO_IOCTL);
}
if (fh->vidq.read_buf) {
buffer_release(&fh->vidq, fh->vidq.read_buf);
kfree(fh->vidq.read_buf);
}
videobuf_mmap_free(&fh->vidq);
v4l2_prio_close(&dev->prio,&fh->prio);
file->private_data = NULL;
kfree(fh);
return 0;
}
static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = fh->dev;
if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE))
{
return -EINVAL;
}
if (unlikely(i != fh->type))
{
return -EINVAL;
}
if (unlikely(!res_get(dev, fh, get_resource(fh, RESOURCE_VIDEO_IOCTL))))
{
return -EBUSY;
}
return videobuf_streamon(get_queue(fh));
}
static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = fh->dev;
int err, res;
if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
if (i != fh->type)
return -EINVAL;
res = get_resource(fh, RESOURCE_VIDEO_IOCTL);
err = videobuf_streamoff(get_queue(fh));
if (err < 0)
return err;
res_free(dev, fh, res);
return 0;
}
static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
int err;
if (fh)
{
err = v4l2_prio_check(&dev->prio, &fh->prio);
if (0 != err)
return err;
}
dprintk(2, "%s()\n", __func__);
err = vidioc_try_fmt_vid_cap(file, priv, f);
if (0 != err)
return err;
fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
fh->width = f->fmt.pix.width;
fh->height = f->fmt.pix.height;
fh->vidq.field = f->fmt.pix.field;
dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, fh->height, fh->vidq.field);
cx25821_call_all(dev, video, s_fmt, f);
return 0;
}
static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
struct cx25821_fh *fh = priv;
return videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK);
}
static long video_ioctl_set(struct file *file, unsigned int cmd, unsigned long arg)
{
struct cx25821_fh *fh = file->private_data;
struct cx25821_dev *dev = fh->dev;
struct downstream_user_struct *data_from_user;
int command;
int width = 720;
int selected_channel = 0, pix_format = 0, i = 0;
int cif_enable = 0, cif_width = 0;
u32 value = 0;
data_from_user = (struct downstream_user_struct *)arg;
if( !data_from_user )
{
printk("cx25821 in %s(): User data is INVALID. Returning.\n", __func__);
return 0;
}
command = data_from_user->command;
if( command != SET_VIDEO_STD && command != SET_PIXEL_FORMAT && command != ENABLE_CIF_RESOLUTION &&
command != REG_READ && command != REG_WRITE && command != MEDUSA_READ && command != MEDUSA_WRITE)
{
return 0;
}
switch(command)
{
case SET_VIDEO_STD:
dev->tvnorm = !strcmp(data_from_user->vid_stdname,"PAL") ? V4L2_STD_PAL_BG : V4L2_STD_NTSC_M;
medusa_set_videostandard(dev);
break;
case SET_PIXEL_FORMAT:
selected_channel = data_from_user->decoder_select;
pix_format = data_from_user->pixel_format;
if( !(selected_channel <= 7 && selected_channel >= 0) )
{
selected_channel -= 4;
selected_channel = selected_channel % 8;
}
if( selected_channel >= 0 )
cx25821_set_pixel_format( dev, selected_channel, pix_format );
break;
case ENABLE_CIF_RESOLUTION:
selected_channel = data_from_user->decoder_select;
cif_enable = data_from_user->cif_resolution_enable;
cif_width = data_from_user->cif_width;
if( cif_enable )
{
if( dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK )
width = 352;
else
width = (cif_width == 320 || cif_width == 352) ? cif_width : 320;
}
if( !(selected_channel <= 7 && selected_channel >= 0) )
{
selected_channel -= 4;
selected_channel = selected_channel % 8;
}
if( selected_channel <= 7 && selected_channel >= 0 )
{
dev->use_cif_resolution[selected_channel] = cif_enable;
dev->cif_width[selected_channel] = width;
}
else
{
for( i=0; i < VID_CHANNEL_NUM; i++ )
{
dev->use_cif_resolution[i] = cif_enable;
dev->cif_width[i] = width;
}
}
medusa_set_resolution( dev, width, selected_channel );
break;
case REG_READ:
data_from_user->reg_data = cx_read(data_from_user->reg_address);
break;
case REG_WRITE:
cx_write(data_from_user->reg_address, data_from_user->reg_data);
break;
case MEDUSA_READ:
value = cx25821_i2c_read(&dev->i2c_bus[0], (u16)data_from_user->reg_address, &data_from_user->reg_data);
break;
case MEDUSA_WRITE:
cx25821_i2c_write(&dev->i2c_bus[0], (u16)data_from_user->reg_address, data_from_user->reg_data);
break;
}
return 0;
}
static int vidioc_log_status (struct file *file, void *priv)
{
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
char name[32 + 2];
snprintf(name, sizeof(name), "%s/2", dev->name);
printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n",
dev->name);
cx25821_call_all(dev, core, log_status);
printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n",
dev->name);
return 0;
}
static int vidioc_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctl)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
int err;
if (fh)
{
err = v4l2_prio_check(&dev->prio, &fh->prio);
if (0 != err)
return err;
}
return 0;
}
// exported stuff
static const struct v4l2_file_operations video_fops = {
.owner = THIS_MODULE,
.open = video_open,
.release = video_release,
.read = video_read,
.poll = video_poll,
.mmap = video_mmap,
.ioctl = video_ioctl_set,
};
static const struct v4l2_ioctl_ops video_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
.vidioc_reqbufs = vidioc_reqbufs,
.vidioc_querybuf = vidioc_querybuf,
.vidioc_qbuf = vidioc_qbuf,
.vidioc_dqbuf = vidioc_dqbuf,
#ifdef TUNER_FLAG
.vidioc_s_std = vidioc_s_std,
.vidioc_querystd = vidioc_querystd,
#endif
.vidioc_cropcap = vidioc_cropcap,
.vidioc_s_crop = vidioc_s_crop,
.vidioc_g_crop = vidioc_g_crop,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
.vidioc_g_ctrl = vidioc_g_ctrl,
.vidioc_s_ctrl = vidioc_s_ctrl,
.vidioc_queryctrl = vidioc_queryctrl,
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
.vidioc_log_status = vidioc_log_status,
.vidioc_g_priority = vidioc_g_priority,
.vidioc_s_priority = vidioc_s_priority,
#ifdef CONFIG_VIDEO_V4L1_COMPAT
.vidiocgmbuf = vidiocgmbuf,
#endif
#ifdef TUNER_FLAG
.vidioc_g_tuner = vidioc_g_tuner,
.vidioc_s_tuner = vidioc_s_tuner,
.vidioc_g_frequency = vidioc_g_frequency,
.vidioc_s_frequency = vidioc_s_frequency,
#endif
#ifdef CONFIG_VIDEO_ADV_DEBUG
.vidioc_g_register = vidioc_g_register,
.vidioc_s_register = vidioc_s_register,
#endif
};
struct video_device cx25821_videoioctl_template = {
.name = "cx25821-videoioctl",
.fops = &video_fops,
.minor = -1,
.ioctl_ops = &video_ioctl_ops,
.tvnorms = CX25821_NORMS,
.current_norm = V4L2_STD_NTSC_M,
};

View File

@ -0,0 +1,443 @@
/*
* Driver for the Conexant CX25821 PCIe bridge
*
* Copyright (C) 2009 Conexant Systems Inc.
* Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
* Based on Steven Toth <stoth@linuxtv.org> cx23885 driver
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "cx25821-video.h"
static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
{
struct cx25821_buffer *buf = container_of(vb, struct cx25821_buffer, vb);
struct cx25821_buffer *prev;
struct cx25821_fh *fh = vq->priv_data;
struct cx25821_dev *dev = fh->dev;
struct cx25821_dmaqueue *q = &dev->vidq[SRAM_CH10];
/* 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);
buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]);
if (!list_empty(&q->queued)) {
list_add_tail(&buf->vb.queue, &q->queued);
buf->vb.state = VIDEOBUF_QUEUED;
dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, buf->vb.i);
} else if (list_empty(&q->active)) {
list_add_tail(&buf->vb.queue, &q->active);
cx25821_start_video_dma(dev, q, buf, &dev->sram_channels[SRAM_CH10]);
buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
dprintk(2, "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n",
buf, buf->vb. i, buf->count, q->count);
} else {
prev = list_entry(q->active.prev, struct cx25821_buffer, vb.queue);
if (prev->vb.width == buf->vb.width &&
prev->vb.height == buf->vb.height &&
prev->fmt == buf->fmt) {
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);
/* 64 bit bits 63-32 */
prev->risc.jmp[2] = cpu_to_le32(0);
dprintk(2, "[%p/%d] buffer_queue - append to active, buf->count=%d\n", buf, buf->vb.i, buf->count);
} else {
list_add_tail(&buf->vb.queue, &q->queued);
buf->vb.state = VIDEOBUF_QUEUED;
dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, buf->vb.i);
}
}
if (list_empty(&q->active))
{
dprintk(2, "active queue empty!\n");
}
}
static struct videobuf_queue_ops cx25821_video_qops = {
.buf_setup = buffer_setup,
.buf_prepare = buffer_prepare,
.buf_queue = buffer_queue,
.buf_release = buffer_release,
};
static int video_open(struct file *file)
{
int minor = video_devdata(file)->minor;
struct cx25821_dev *h, *dev = NULL;
struct cx25821_fh *fh;
struct list_head *list;
enum v4l2_buf_type type = 0;
lock_kernel();
list_for_each(list, &cx25821_devlist)
{
h = list_entry(list, struct cx25821_dev, devlist);
if (h->video_dev[SRAM_CH10] && h->video_dev[SRAM_CH10]->minor == minor) {
dev = h;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
}
}
if (NULL == dev) {
unlock_kernel();
return -ENODEV;
}
printk("open minor=%d type=%s\n", minor, v4l2_type_names[type]);
/* allocate + initialize per filehandle data */
fh = kzalloc(sizeof(*fh), GFP_KERNEL);
if (NULL == fh) {
unlock_kernel();
return -ENOMEM;
}
file->private_data = fh;
fh->dev = dev;
fh->type = type;
fh->width = 720;
if(dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK)
fh->height = 576;
else
fh->height = 480;
dev->channel_opened = 9;
fh->fmt = format_by_fourcc(V4L2_PIX_FMT_YUYV);
v4l2_prio_open(&dev->prio,&fh->prio);
videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops,
&dev->pci->dev, &dev->slock,
V4L2_BUF_TYPE_VIDEO_CAPTURE,
V4L2_FIELD_INTERLACED,
sizeof(struct cx25821_buffer),
fh);
dprintk(1, "post videobuf_queue_init()\n");
unlock_kernel();
return 0;
}
static ssize_t video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
{
struct cx25821_fh *fh = file->private_data;
switch (fh->type)
{
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
if (res_locked(fh->dev, RESOURCE_VIDEO10))
return -EBUSY;
return videobuf_read_one(&fh->vidq, data, count, ppos, file->f_flags & O_NONBLOCK);
default:
BUG();
return 0;
}
}
static unsigned int video_poll(struct file *file, struct poll_table_struct *wait)
{
struct cx25821_fh *fh = file->private_data;
struct cx25821_buffer *buf;
if (res_check(fh, RESOURCE_VIDEO10)) {
/* streaming capture */
if (list_empty(&fh->vidq.stream))
return POLLERR;
buf = list_entry(fh->vidq.stream.next,
struct cx25821_buffer, vb.stream);
} else {
/* read() capture */
buf = (struct cx25821_buffer *)fh->vidq.read_buf;
if (NULL == buf)
return POLLERR;
}
poll_wait(file, &buf->vb.done, wait);
if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR)
return POLLIN|POLLRDNORM;
return 0;
}
static int video_release(struct file *file)
{
struct cx25821_fh *fh = file->private_data;
struct cx25821_dev *dev = fh->dev;
//stop the risc engine and fifo
//cx_write(channel10->dma_ctl, 0);
/* stop video capture */
if (res_check(fh, RESOURCE_VIDEO10)) {
videobuf_queue_cancel(&fh->vidq);
res_free(dev, fh, RESOURCE_VIDEO10);
}
if (fh->vidq.read_buf) {
buffer_release(&fh->vidq, fh->vidq.read_buf);
kfree(fh->vidq.read_buf);
}
videobuf_mmap_free(&fh->vidq);
v4l2_prio_close(&dev->prio,&fh->prio);
file->private_data = NULL;
kfree(fh);
return 0;
}
static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = fh->dev;
if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE))
{
return -EINVAL;
}
if (unlikely(i != fh->type))
{
return -EINVAL;
}
if (unlikely(!res_get(dev, fh, get_resource(fh, RESOURCE_VIDEO10))))
{
return -EBUSY;
}
return videobuf_streamon(get_queue(fh));
}
static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = fh->dev;
int err, res;
if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
if (i != fh->type)
return -EINVAL;
res = get_resource(fh, RESOURCE_VIDEO10);
err = videobuf_streamoff(get_queue(fh));
if (err < 0)
return err;
res_free(dev, fh, res);
return 0;
}
static long video_ioctl_upstream10(struct file *file, unsigned int cmd, unsigned long arg)
{
struct cx25821_fh *fh = file->private_data;
struct cx25821_dev *dev = fh->dev;
int command = 0;
struct upstream_user_struct *data_from_user;
data_from_user = (struct upstream_user_struct *)arg;
if( !data_from_user )
{
printk("cx25821 in %s(): Upstream data is INVALID. Returning.\n", __func__);
return 0;
}
command = data_from_user->command;
if( command != UPSTREAM_START_VIDEO && command != UPSTREAM_STOP_VIDEO )
{
return 0;
}
dev->input_filename_ch2 = data_from_user->input_filename;
dev->input_audiofilename = data_from_user->input_filename;
dev->vid_stdname_ch2 = data_from_user->vid_stdname;
dev->pixel_format_ch2 = data_from_user->pixel_format;
dev->channel_select_ch2 = data_from_user->channel_select;
dev->command_ch2 = data_from_user->command;
switch(command)
{
case UPSTREAM_START_VIDEO:
cx25821_start_upstream_video_ch2(dev, data_from_user);
break;
case UPSTREAM_STOP_VIDEO:
cx25821_stop_upstream_video_ch2(dev);
break;
}
return 0;
}
static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
int err;
if (fh)
{
err = v4l2_prio_check(&dev->prio, &fh->prio);
if (0 != err)
return err;
}
dprintk(2, "%s()\n", __func__);
err = vidioc_try_fmt_vid_cap(file, priv, f);
if (0 != err)
return err;
fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
fh->width = f->fmt.pix.width;
fh->height = f->fmt.pix.height;
fh->vidq.field = f->fmt.pix.field;
dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, fh->height, fh->vidq.field);
cx25821_call_all(dev, video, s_fmt, f);
return 0;
}
static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
struct cx25821_fh *fh = priv;
return videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK);
}
static int vidioc_log_status (struct file *file, void *priv)
{
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
char name[32 + 2];
snprintf(name, sizeof(name), "%s/2", dev->name);
printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n",
dev->name);
cx25821_call_all(dev, core, log_status);
printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n",
dev->name);
return 0;
}
static int vidioc_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctl)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
int err;
if (fh)
{
err = v4l2_prio_check(&dev->prio, &fh->prio);
if (0 != err)
return err;
}
return 0;
}
//exported stuff
static const struct v4l2_file_operations video_fops = {
.owner = THIS_MODULE,
.open = video_open,
.release = video_release,
.read = video_read,
.poll = video_poll,
.mmap = video_mmap,
.ioctl = video_ioctl_upstream10,
};
static const struct v4l2_ioctl_ops video_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
.vidioc_reqbufs = vidioc_reqbufs,
.vidioc_querybuf = vidioc_querybuf,
.vidioc_qbuf = vidioc_qbuf,
.vidioc_dqbuf = vidioc_dqbuf,
#ifdef TUNER_FLAG
.vidioc_s_std = vidioc_s_std,
.vidioc_querystd = vidioc_querystd,
#endif
.vidioc_cropcap = vidioc_cropcap,
.vidioc_s_crop = vidioc_s_crop,
.vidioc_g_crop = vidioc_g_crop,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
.vidioc_g_ctrl = vidioc_g_ctrl,
.vidioc_s_ctrl = vidioc_s_ctrl,
.vidioc_queryctrl = vidioc_queryctrl,
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
.vidioc_log_status = vidioc_log_status,
.vidioc_g_priority = vidioc_g_priority,
.vidioc_s_priority = vidioc_s_priority,
#ifdef CONFIG_VIDEO_V4L1_COMPAT
.vidiocgmbuf = vidiocgmbuf,
#endif
#ifdef TUNER_FLAG
.vidioc_g_tuner = vidioc_g_tuner,
.vidioc_s_tuner = vidioc_s_tuner,
.vidioc_g_frequency = vidioc_g_frequency,
.vidioc_s_frequency = vidioc_s_frequency,
#endif
#ifdef CONFIG_VIDEO_ADV_DEBUG
.vidioc_g_register = vidioc_g_register,
.vidioc_s_register = vidioc_s_register,
#endif
};
struct video_device cx25821_video_template10 = {
.name = "cx25821-upstream10",
.fops = &video_fops,
.minor = -1,
.ioctl_ops = &video_ioctl_ops,
.tvnorms = CX25821_NORMS,
.current_norm = V4L2_STD_NTSC_M,
};

View File

@ -0,0 +1,441 @@
/*
* Driver for the Conexant CX25821 PCIe bridge
*
* Copyright (C) 2009 Conexant Systems Inc.
* Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
* Based on Steven Toth <stoth@linuxtv.org> cx23885 driver
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "cx25821-video.h"
static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
{
struct cx25821_buffer *buf = container_of(vb, struct cx25821_buffer, vb);
struct cx25821_buffer *prev;
struct cx25821_fh *fh = vq->priv_data;
struct cx25821_dev *dev = fh->dev;
struct cx25821_dmaqueue *q = &dev->vidq[SRAM_CH09];
/* 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);
buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]);
if (!list_empty(&q->queued)) {
list_add_tail(&buf->vb.queue, &q->queued);
buf->vb.state = VIDEOBUF_QUEUED;
dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, buf->vb.i);
} else if (list_empty(&q->active)) {
list_add_tail(&buf->vb.queue, &q->active);
cx25821_start_video_dma(dev, q, buf, &dev->sram_channels[SRAM_CH09]);
buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
dprintk(2, "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n",
buf, buf->vb. i, buf->count, q->count);
} else {
prev = list_entry(q->active.prev, struct cx25821_buffer, vb.queue);
if (prev->vb.width == buf->vb.width &&
prev->vb.height == buf->vb.height &&
prev->fmt == buf->fmt) {
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);
/* 64 bit bits 63-32 */
prev->risc.jmp[2] = cpu_to_le32(0);
dprintk(2, "[%p/%d] buffer_queue - append to active, buf->count=%d\n", buf, buf->vb.i, buf->count);
} else {
list_add_tail(&buf->vb.queue, &q->queued);
buf->vb.state = VIDEOBUF_QUEUED;
dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, buf->vb.i);
}
}
if (list_empty(&q->active))
{
dprintk(2, "active queue empty!\n");
}
}
static struct videobuf_queue_ops cx25821_video_qops = {
.buf_setup = buffer_setup,
.buf_prepare = buffer_prepare,
.buf_queue = buffer_queue,
.buf_release = buffer_release,
};
static int video_open(struct file *file)
{
int minor = video_devdata(file)->minor;
struct cx25821_dev *h, *dev = NULL;
struct cx25821_fh *fh;
struct list_head *list;
enum v4l2_buf_type type = 0;
lock_kernel();
list_for_each(list, &cx25821_devlist)
{
h = list_entry(list, struct cx25821_dev, devlist);
if (h->video_dev[SRAM_CH09] && h->video_dev[SRAM_CH09]->minor == minor)
{
dev = h;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
}
}
if (NULL == dev) {
unlock_kernel();
return -ENODEV;
}
printk("open minor=%d type=%s\n", minor, v4l2_type_names[type]);
/* allocate + initialize per filehandle data */
fh = kzalloc(sizeof(*fh), GFP_KERNEL);
if (NULL == fh) {
unlock_kernel();
return -ENOMEM;
}
file->private_data = fh;
fh->dev = dev;
fh->type = type;
fh->width = 720;
if(dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK)
fh->height = 576;
else
fh->height = 480;
dev->channel_opened = 8;
fh->fmt = format_by_fourcc(V4L2_PIX_FMT_YUYV);
v4l2_prio_open(&dev->prio,&fh->prio);
videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops,
&dev->pci->dev, &dev->slock,
V4L2_BUF_TYPE_VIDEO_CAPTURE,
V4L2_FIELD_INTERLACED,
sizeof(struct cx25821_buffer),
fh);
dprintk(1, "post videobuf_queue_init()\n");
unlock_kernel();
return 0;
}
static ssize_t video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
{
struct cx25821_fh *fh = file->private_data;
switch (fh->type)
{
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
if (res_locked(fh->dev, RESOURCE_VIDEO9))
return -EBUSY;
return videobuf_read_one(&fh->vidq, data, count, ppos, file->f_flags & O_NONBLOCK);
default:
BUG();
return 0;
}
}
static unsigned int video_poll(struct file *file, struct poll_table_struct *wait)
{
struct cx25821_fh *fh = file->private_data;
struct cx25821_buffer *buf;
if (res_check(fh, RESOURCE_VIDEO9)) {
/* streaming capture */
if (list_empty(&fh->vidq.stream))
return POLLERR;
buf = list_entry(fh->vidq.stream.next,
struct cx25821_buffer, vb.stream);
} else {
/* read() capture */
buf = (struct cx25821_buffer *)fh->vidq.read_buf;
if (NULL == buf)
return POLLERR;
}
poll_wait(file, &buf->vb.done, wait);
if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR)
return POLLIN|POLLRDNORM;
return 0;
}
static int video_release(struct file *file)
{
struct cx25821_fh *fh = file->private_data;
struct cx25821_dev *dev = fh->dev;
//stop the risc engine and fifo
//cx_write(channel9->dma_ctl, 0);
/* stop video capture */
if (res_check(fh, RESOURCE_VIDEO9)) {
videobuf_queue_cancel(&fh->vidq);
res_free(dev, fh, RESOURCE_VIDEO9);
}
if (fh->vidq.read_buf) {
buffer_release(&fh->vidq, fh->vidq.read_buf);
kfree(fh->vidq.read_buf);
}
videobuf_mmap_free(&fh->vidq);
v4l2_prio_close(&dev->prio,&fh->prio);
file->private_data = NULL;
kfree(fh);
return 0;
}
static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = fh->dev;
if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE))
{
return -EINVAL;
}
if (unlikely(i != fh->type))
{
return -EINVAL;
}
if (unlikely(!res_get(dev, fh, get_resource(fh, RESOURCE_VIDEO9))))
{
return -EBUSY;
}
return videobuf_streamon(get_queue(fh));
}
static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = fh->dev;
int err, res;
if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
if (i != fh->type)
return -EINVAL;
res = get_resource(fh, RESOURCE_VIDEO9);
err = videobuf_streamoff(get_queue(fh));
if (err < 0)
return err;
res_free(dev, fh, res);
return 0;
}
static long video_ioctl_upstream9(struct file *file, unsigned int cmd, unsigned long arg)
{
struct cx25821_fh *fh = file->private_data;
struct cx25821_dev *dev = fh->dev;
int command = 0;
struct upstream_user_struct *data_from_user;
data_from_user = (struct upstream_user_struct *)arg;
if( !data_from_user )
{
printk("cx25821 in %s(): Upstream data is INVALID. Returning.\n", __func__);
return 0;
}
command = data_from_user->command;
if( command != UPSTREAM_START_VIDEO && command != UPSTREAM_STOP_VIDEO )
{
return 0;
}
dev->input_filename = data_from_user->input_filename;
dev->input_audiofilename = data_from_user->input_filename;
dev->vid_stdname = data_from_user->vid_stdname;
dev->pixel_format = data_from_user->pixel_format;
dev->channel_select = data_from_user->channel_select;
dev->command = data_from_user->command;
switch(command)
{
case UPSTREAM_START_VIDEO:
cx25821_start_upstream_video_ch1(dev, data_from_user);
break;
case UPSTREAM_STOP_VIDEO:
cx25821_stop_upstream_video_ch1(dev);
break;
}
return 0;
}
static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f)
{
struct cx25821_fh *fh = priv;
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
int err;
if (fh)
{
err = v4l2_prio_check(&dev->prio, &fh->prio);
if (0 != err)
return err;
}
dprintk(2, "%s()\n", __func__);
err = vidioc_try_fmt_vid_cap(file, priv, f);
if (0 != err)
return err;
fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
fh->width = f->fmt.pix.width;
fh->height = f->fmt.pix.height;
fh->vidq.field = f->fmt.pix.field;
dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, fh->height, fh->vidq.field);
cx25821_call_all(dev, video, s_fmt, f);
return 0;
}
static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
struct cx25821_fh *fh = priv;
return videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK);
}
static int vidioc_log_status (struct file *file, void *priv)
{
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
char name[32 + 2];
snprintf(name, sizeof(name), "%s/2", dev->name);
printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n",
dev->name);
cx25821_call_all(dev, core, log_status);
printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n",
dev->name);
return 0;
}
static int vidioc_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctl)
{
struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
struct cx25821_fh *fh = priv;
int err;
if (fh)
{
err = v4l2_prio_check(&dev->prio, &fh->prio);
if (0 != err)
return err;
}
return 0;
}
// exported stuff
static const struct v4l2_file_operations video_fops = {
.owner = THIS_MODULE,
.open = video_open,
.release = video_release,
.read = video_read,
.poll = video_poll,
.mmap = video_mmap,
.ioctl = video_ioctl_upstream9,
};
static const struct v4l2_ioctl_ops video_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
.vidioc_reqbufs = vidioc_reqbufs,
.vidioc_querybuf = vidioc_querybuf,
.vidioc_qbuf = vidioc_qbuf,
.vidioc_dqbuf = vidioc_dqbuf,
#ifdef TUNER_FLAG
.vidioc_s_std = vidioc_s_std,
.vidioc_querystd = vidioc_querystd,
#endif
.vidioc_cropcap = vidioc_cropcap,
.vidioc_s_crop = vidioc_s_crop,
.vidioc_g_crop = vidioc_g_crop,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
.vidioc_g_ctrl = vidioc_g_ctrl,
.vidioc_s_ctrl = vidioc_s_ctrl,
.vidioc_queryctrl = vidioc_queryctrl,
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
.vidioc_log_status = vidioc_log_status,
.vidioc_g_priority = vidioc_g_priority,
.vidioc_s_priority = vidioc_s_priority,
#ifdef CONFIG_VIDEO_V4L1_COMPAT
.vidiocgmbuf = vidiocgmbuf,
#endif
#ifdef TUNER_FLAG
.vidioc_g_tuner = vidioc_g_tuner,
.vidioc_s_tuner = vidioc_s_tuner,
.vidioc_g_frequency = vidioc_g_frequency,
.vidioc_s_frequency = vidioc_s_frequency,
#endif
#ifdef CONFIG_VIDEO_ADV_DEBUG
.vidioc_g_register = vidioc_g_register,
.vidioc_s_register = vidioc_s_register,
#endif
};
struct video_device cx25821_video_template9 = {
.name = "cx25821-upstream9",
.fops = &video_fops,
.minor = -1,
.ioctl_ops = &video_ioctl_ops,
.tvnorms = CX25821_NORMS,
.current_norm = V4L2_STD_NTSC_M,
};

View File

@ -0,0 +1,589 @@
/*
* Driver for the Conexant CX25821 PCIe bridge
*
* Copyright (C) 2009 Conexant Systems Inc.
* Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
* Based on Steven Toth <stoth@linuxtv.org> cx23885 driver
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef CX25821_H_
#define CX25821_H_
#include <linux/pci.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/kdev_t.h>
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
#include <media/tuner.h>
#include <media/tveeprom.h>
#include <media/videobuf-dma-sg.h>
#include <media/videobuf-dvb.h>
#include "btcx-risc.h"
#include "cx25821-reg.h"
#include "cx25821-medusa-reg.h"
#include "cx25821-sram.h"
#include "cx25821-audio.h"
#include "media/cx2341x.h"
#include <linux/version.h>
#include <linux/mutex.h>
#define CX25821_VERSION_CODE KERNEL_VERSION(0, 0, 106)
#define UNSET (-1U)
#define NO_SYNC_LINE (-1U)
#define CX25821_MAXBOARDS 2
#define TRUE 1
#define FALSE 0
#define LINE_SIZE_D1 1440
// Number of decoders and encoders
#define MAX_DECODERS 8
#define MAX_ENCODERS 2
#define QUAD_DECODERS 4
#define MAX_CAMERAS 16
/* Max number of inputs by card */
#define MAX_CX25821_INPUT 8
#define INPUT(nr) (&cx25821_boards[dev->board].input[nr])
#define RESOURCE_VIDEO0 1
#define RESOURCE_VIDEO1 2
#define RESOURCE_VIDEO2 4
#define RESOURCE_VIDEO3 8
#define RESOURCE_VIDEO4 16
#define RESOURCE_VIDEO5 32
#define RESOURCE_VIDEO6 64
#define RESOURCE_VIDEO7 128
#define RESOURCE_VIDEO8 256
#define RESOURCE_VIDEO9 512
#define RESOURCE_VIDEO10 1024
#define RESOURCE_VIDEO11 2048
#define RESOURCE_VIDEO_IOCTL 4096
#define BUFFER_TIMEOUT (HZ) /* 0.5 seconds */
#define UNKNOWN_BOARD 0
#define CX25821_BOARD 1
/* Currently supported by the driver */
#define CX25821_NORMS (\
V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_JP | V4L2_STD_NTSC_M_KR | \
V4L2_STD_PAL_BG | V4L2_STD_PAL_DK | V4L2_STD_PAL_I | \
V4L2_STD_PAL_M | V4L2_STD_PAL_N | V4L2_STD_PAL_H | \
V4L2_STD_PAL_Nc )
#define CX25821_BOARD_CONEXANT_ATHENA10 1
#define MAX_VID_CHANNEL_NUM 12
#define VID_CHANNEL_NUM 8
struct cx25821_fmt {
char *name;
u32 fourcc; /* v4l2 format id */
int depth;
int flags;
u32 cxformat;
};
struct cx25821_ctrl {
struct v4l2_queryctrl v;
u32 off;
u32 reg;
u32 mask;
u32 shift;
};
struct cx25821_tvnorm {
char *name;
v4l2_std_id id;
u32 cxiformat;
u32 cxoformat;
};
struct cx25821_fh {
struct cx25821_dev *dev;
enum v4l2_buf_type type;
int radio;
u32 resources;
enum v4l2_priority prio;
/* video overlay */
struct v4l2_window win;
struct v4l2_clip *clips;
unsigned int nclips;
/* video capture */
struct cx25821_fmt *fmt;
unsigned int width, height;
/* vbi capture */
struct videobuf_queue vidq;
struct videobuf_queue vbiq;
/* H264 Encoder specifics ONLY */
struct videobuf_queue mpegq;
atomic_t v4l_reading;
};
enum cx25821_itype {
CX25821_VMUX_COMPOSITE = 1,
CX25821_VMUX_SVIDEO,
CX25821_VMUX_DEBUG,
CX25821_RADIO,
};
enum cx25821_src_sel_type {
CX25821_SRC_SEL_EXT_656_VIDEO = 0,
CX25821_SRC_SEL_PARALLEL_MPEG_VIDEO
};
/* buffer for one video frame */
struct cx25821_buffer {
/* common v4l buffer stuff -- must be first */
struct videobuf_buffer vb;
/* cx25821 specific */
unsigned int bpl;
struct btcx_riscmem risc;
struct cx25821_fmt *fmt;
u32 count;
};
struct cx25821_input {
enum cx25821_itype type;
unsigned int vmux;
u32 gpio0, gpio1, gpio2, gpio3;
};
typedef enum {
CX25821_UNDEFINED = 0,
CX25821_RAW,
CX25821_264
} port_t;
struct cx25821_board {
char *name;
port_t porta, portb, portc;
unsigned int tuner_type;
unsigned int radio_type;
unsigned char tuner_addr;
unsigned char radio_addr;
u32 clk_freq;
struct cx25821_input input[2];
};
struct cx25821_subid {
u16 subvendor;
u16 subdevice;
u32 card;
};
struct cx25821_i2c {
struct cx25821_dev *dev;
int nr;
/* i2c i/o */
struct i2c_adapter i2c_adap;
struct i2c_algo_bit_data i2c_algo;
struct i2c_client i2c_client;
u32 i2c_rc;
/* cx25821 registers used for raw addess */
u32 i2c_period;
u32 reg_ctrl;
u32 reg_stat;
u32 reg_addr;
u32 reg_rdata;
u32 reg_wdata;
};
struct cx25821_dmaqueue {
struct list_head active;
struct list_head queued;
struct timer_list timeout;
struct btcx_riscmem stopper;
u32 count;
};
struct cx25821_data {
struct cx25821_dev *dev;
struct sram_channel *channel;
};
struct cx25821_dev {
struct list_head devlist;
atomic_t refcount;
struct v4l2_device v4l2_dev;
struct v4l2_prio_state prio;
/* pci stuff */
struct pci_dev *pci;
unsigned char pci_rev, pci_lat;
int pci_bus, pci_slot;
u32 base_io_addr;
u32 __iomem *lmmio;
u8 __iomem *bmmio;
int pci_irqmask;
int hwrevision;
u32 clk_freq;
/* I2C adapters: Master 1 & 2 (External) & Master 3 (Internal only) */
struct cx25821_i2c i2c_bus[3];
int nr;
struct mutex lock;
/* board details */
unsigned int board;
char name[32];
/* sram configuration */
struct sram_channel *sram_channels;
/* Analog video */
u32 resources;
unsigned int input;
u32 tvaudio;
v4l2_std_id tvnorm;
unsigned int tuner_type;
unsigned char tuner_addr;
unsigned int radio_type;
unsigned char radio_addr;
unsigned int has_radio;
unsigned int videc_type;
unsigned char videc_addr;
unsigned short _max_num_decoders;
int ctl_bright;
int ctl_contrast;
int ctl_hue;
int ctl_saturation;
struct cx25821_data timeout_data[MAX_VID_CHANNEL_NUM];
/* Analog Audio Upstream */
int _audio_is_running;
int _audiopixel_format;
int _is_first_audio_frame;
int _audiofile_status;
int _audio_lines_count;
int _audioframe_count;
int _audio_upstream_channel_select;
int _last_index_irq; //The last interrupt index processed.
__le32 * _risc_audio_jmp_addr;
__le32 * _risc_virt_start_addr;
__le32 * _risc_virt_addr;
dma_addr_t _risc_phys_addr;
dma_addr_t _risc_phys_start_addr;
unsigned int _audiorisc_size;
unsigned int _audiodata_buf_size;
__le32 * _audiodata_buf_virt_addr;
dma_addr_t _audiodata_buf_phys_addr;
char *_audiofilename;
/* V4l */
u32 freq;
struct video_device *video_dev[MAX_VID_CHANNEL_NUM];
struct video_device *vbi_dev;
struct video_device *radio_dev;
struct video_device *ioctl_dev;
struct cx25821_dmaqueue vidq[MAX_VID_CHANNEL_NUM];
spinlock_t slock;
/* Video Upstream */
int _line_size;
int _prog_cnt;
int _pixel_format;
int _is_first_frame;
int _is_running;
int _file_status;
int _lines_count;
int _frame_count;
int _channel_upstream_select;
unsigned int _risc_size;
__le32 * _dma_virt_start_addr;
__le32 * _dma_virt_addr;
dma_addr_t _dma_phys_addr;
dma_addr_t _dma_phys_start_addr;
unsigned int _data_buf_size;
__le32 * _data_buf_virt_addr;
dma_addr_t _data_buf_phys_addr;
char * _filename;
char * _defaultname;
int _line_size_ch2;
int _prog_cnt_ch2;
int _pixel_format_ch2;
int _is_first_frame_ch2;
int _is_running_ch2;
int _file_status_ch2;
int _lines_count_ch2;
int _frame_count_ch2;
int _channel2_upstream_select;
unsigned int _risc_size_ch2;
__le32 * _dma_virt_start_addr_ch2;
__le32 * _dma_virt_addr_ch2;
dma_addr_t _dma_phys_addr_ch2;
dma_addr_t _dma_phys_start_addr_ch2;
unsigned int _data_buf_size_ch2;
__le32 * _data_buf_virt_addr_ch2;
dma_addr_t _data_buf_phys_addr_ch2;
char * _filename_ch2;
char * _defaultname_ch2;
/* MPEG Encoder ONLY settings */
u32 cx23417_mailbox;
struct cx2341x_mpeg_params mpeg_params;
struct video_device *v4l_device;
atomic_t v4l_reader_count;
struct cx25821_tvnorm encodernorm;
u32 upstream_riscbuf_size;
u32 upstream_databuf_size;
u32 upstream_riscbuf_size_ch2;
u32 upstream_databuf_size_ch2;
u32 audio_upstream_riscbuf_size;
u32 audio_upstream_databuf_size;
int _isNTSC;
int _frame_index;
int _audioframe_index;
struct workqueue_struct * _irq_queues;
struct work_struct _irq_work_entry;
struct workqueue_struct * _irq_queues_ch2;
struct work_struct _irq_work_entry_ch2;
struct workqueue_struct * _irq_audio_queues;
struct work_struct _audio_work_entry;
char *input_filename;
char *input_filename_ch2;
int _frame_index_ch2;
int _isNTSC_ch2;
char *vid_stdname_ch2;
int pixel_format_ch2;
int channel_select_ch2;
int command_ch2;
char *input_audiofilename;
char *vid_stdname;
int pixel_format;
int channel_select;
int command;
int pixel_formats[VID_CHANNEL_NUM];
int use_cif_resolution[VID_CHANNEL_NUM];
int cif_width[VID_CHANNEL_NUM];
int channel_opened;
};
struct upstream_user_struct {
char *input_filename;
char *vid_stdname;
int pixel_format;
int channel_select;
int command;
};
struct downstream_user_struct {
char *vid_stdname;
int pixel_format;
int cif_resolution_enable;
int cif_width;
int decoder_select;
int command;
int reg_address;
int reg_data;
};
extern struct upstream_user_struct *up_data;
static inline struct cx25821_dev *get_cx25821(struct v4l2_device *v4l2_dev)
{
return container_of(v4l2_dev, struct cx25821_dev, v4l2_dev);
}
#define cx25821_call_all(dev, o, f, args...) \
v4l2_device_call_all(&dev->v4l2_dev, 0, o, f, ##args)
extern struct list_head cx25821_devlist;
extern struct cx25821_board cx25821_boards[];
extern struct cx25821_subid cx25821_subids[];
#define SRAM_CH00 0 /* Video A */
#define SRAM_CH01 1 /* Video B */
#define SRAM_CH02 2 /* Video C */
#define SRAM_CH03 3 /* Video D */
#define SRAM_CH04 4 /* Video E */
#define SRAM_CH05 5 /* Video F */
#define SRAM_CH06 6 /* Video G */
#define SRAM_CH07 7 /* Video H */
#define SRAM_CH08 8 /* Audio A */
#define SRAM_CH09 9 /* Video Upstream I */
#define SRAM_CH10 10 /* Video Upstream J */
#define SRAM_CH11 11 /* Audio Upstream AUD_CHANNEL_B */
#define VID_UPSTREAM_SRAM_CHANNEL_I SRAM_CH09
#define VID_UPSTREAM_SRAM_CHANNEL_J SRAM_CH10
#define AUDIO_UPSTREAM_SRAM_CHANNEL_B SRAM_CH11
#define VIDEO_IOCTL_CH 11
struct sram_channel {
char *name;
u32 i;
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;
u32 int_msk;
u32 int_stat;
u32 int_mstat;
u32 dma_ctl;
u32 gpcnt_ctl;
u32 gpcnt;
u32 aud_length;
u32 aud_cfg;
u32 fld_aud_fifo_en;
u32 fld_aud_risc_en;
//For Upstream Video
u32 vid_fmt_ctl;
u32 vid_active_ctl1;
u32 vid_active_ctl2;
u32 vid_cdt_size;
u32 vip_ctl;
u32 pix_frmt;
u32 jumponly;
u32 irq_bit;
};
extern struct sram_channel cx25821_sram_channels[];
#define STATUS_SUCCESS 0
#define STATUS_UNSUCCESSFUL -1
#define cx_read(reg) readl(dev->lmmio + ((reg)>>2))
#define cx_write(reg, value) writel((value), dev->lmmio + ((reg)>>2))
#define cx_andor(reg, mask, value) \
writel((readl(dev->lmmio+((reg)>>2)) & ~(mask)) |\
((value) & (mask)), dev->lmmio+((reg)>>2))
#define cx_set(reg, bit) cx_andor((reg), (bit), (bit))
#define cx_clear(reg, bit) cx_andor((reg), (bit), 0)
#define Set_GPIO_Bit(Bit) (1 << Bit)
#define Clear_GPIO_Bit(Bit) (~(1 << Bit))
#define CX25821_ERR(fmt, args...) printk(KERN_ERR "cx25821(%d): " fmt, dev->board, ## args)
#define CX25821_WARN(fmt, args...) printk(KERN_WARNING "cx25821(%d): " fmt, dev->board , ## args)
#define CX25821_INFO(fmt, args...) printk(KERN_INFO "cx25821(%d): " fmt, dev->board , ## args)
extern int cx25821_i2c_register(struct cx25821_i2c *bus);
extern void cx25821_card_setup(struct cx25821_dev *dev);
extern int cx25821_ir_init(struct cx25821_dev *dev);
extern int cx25821_i2c_read(struct cx25821_i2c *bus, u16 reg_addr, int *value);
extern int cx25821_i2c_write(struct cx25821_i2c *bus, u16 reg_addr, int value);
extern int cx25821_i2c_unregister(struct cx25821_i2c *bus);
extern void cx25821_gpio_init(struct cx25821_dev *dev);
extern void cx25821_set_gpiopin_direction( struct cx25821_dev *dev,
int pin_number,
int pin_logic_value);
extern int medusa_video_init(struct cx25821_dev *dev);
extern int medusa_set_videostandard(struct cx25821_dev *dev);
extern void medusa_set_resolution(struct cx25821_dev *dev, int width, int decoder_select);
extern int medusa_set_brightness(struct cx25821_dev *dev, int brightness, int decoder);
extern int medusa_set_contrast(struct cx25821_dev *dev, int contrast, int decoder);
extern int medusa_set_hue(struct cx25821_dev *dev, int hue, int decoder);
extern int medusa_set_saturation(struct cx25821_dev *dev, int saturation, int decoder);
extern int cx25821_sram_channel_setup(struct cx25821_dev *dev, struct sram_channel *ch, unsigned int bpl, u32 risc);
extern int cx25821_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 cx25821_risc_databuffer_audio(struct pci_dev *pci,
struct btcx_riscmem *risc,
struct scatterlist *sglist,
unsigned int bpl,
unsigned int lines,
unsigned int lpi);
extern void cx25821_free_buffer(struct videobuf_queue *q, struct cx25821_buffer *buf);
extern int cx25821_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,u32 reg, u32 mask, u32 value);
extern void cx25821_sram_channel_dump(struct cx25821_dev *dev, struct sram_channel *ch);
extern void cx25821_sram_channel_dump_audio(struct cx25821_dev *dev, struct sram_channel *ch);
extern struct cx25821_dev* cx25821_dev_get(struct pci_dev *pci);
extern void cx25821_print_irqbits(char *name, char *tag, char **strings, int len, u32 bits, u32 mask);
extern void cx25821_dev_unregister(struct cx25821_dev *dev);
extern int cx25821_sram_channel_setup_audio(struct cx25821_dev *dev,
struct sram_channel *ch,
unsigned int bpl, u32 risc);
extern int cx25821_vidupstream_init_ch1(struct cx25821_dev *dev, int channel_select, int pixel_format);
extern int cx25821_vidupstream_init_ch2(struct cx25821_dev *dev, int channel_select, int pixel_format);
extern int cx25821_audio_upstream_init(struct cx25821_dev *dev, int channel_select);
extern void cx25821_free_mem_upstream_ch1(struct cx25821_dev *dev);
extern void cx25821_free_mem_upstream_ch2(struct cx25821_dev *dev);
extern void cx25821_free_mem_upstream_audio(struct cx25821_dev *dev);
extern void cx25821_start_upstream_video_ch1(struct cx25821_dev *dev, struct upstream_user_struct *up_data);
extern void cx25821_start_upstream_video_ch2(struct cx25821_dev *dev, struct upstream_user_struct *up_data);
extern void cx25821_start_upstream_audio(struct cx25821_dev *dev, struct upstream_user_struct *up_data);
extern void cx25821_stop_upstream_video_ch1(struct cx25821_dev *dev);
extern void cx25821_stop_upstream_video_ch2(struct cx25821_dev *dev);
extern void cx25821_stop_upstream_audio(struct cx25821_dev *dev);
extern int cx25821_sram_channel_setup_upstream( struct cx25821_dev *dev, struct sram_channel *ch, unsigned int bpl, u32 risc);
extern void cx25821_set_pixel_format(struct cx25821_dev *dev, int channel, u32 format);
extern void cx25821_videoioctl_unregister(struct cx25821_dev *dev);
extern struct video_device *cx25821_vdev_init(struct cx25821_dev *dev,
struct pci_dev *pci,
struct video_device *template,
char *type);
#endif