forked from Minki/linux
1899e97c52
Start moving the dvb specific code to ngene-dvb.c. Note that there is still stuff in ngene-core.c which could potentially be moved out, but it will require considerable reworking of the logic in order to continue to properly support DVB while also being able to support transport streams coming from analog MPEG encoders. Signed-off-by: Devin Heitmueller <dheitmueller@kernellabs.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
1548 lines
40 KiB
C
1548 lines
40 KiB
C
/*
|
|
* ngene.c: nGene PCIe bridge driver
|
|
*
|
|
* Copyright (C) 2005-2007 Micronas
|
|
*
|
|
* Copyright (C) 2008-2009 Ralph Metzler <rjkm@metzlerbros.de>
|
|
* Modifications for new nGene firmware,
|
|
* support for EEPROM-copying,
|
|
* support for new dual DVB-S2 card prototype
|
|
*
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* version 2 only, as published by the Free Software Foundation.
|
|
*
|
|
*
|
|
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
* 02110-1301, USA
|
|
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/poll.h>
|
|
#include <linux/io.h>
|
|
#include <asm/div64.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/smp_lock.h>
|
|
#include <linux/timer.h>
|
|
#include <linux/byteorder/generic.h>
|
|
#include <linux/firmware.h>
|
|
#include <linux/vmalloc.h>
|
|
|
|
#include "ngene.h"
|
|
|
|
static int one_adapter = 1;
|
|
module_param(one_adapter, int, 0444);
|
|
MODULE_PARM_DESC(one_adapter, "Use only one adapter.");
|
|
|
|
|
|
static int debug;
|
|
module_param(debug, int, 0444);
|
|
MODULE_PARM_DESC(debug, "Print debugging information.");
|
|
|
|
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
|
|
|
|
#define COMMAND_TIMEOUT_WORKAROUND
|
|
|
|
#define dprintk if (debug) printk
|
|
|
|
#define ngwriteb(dat, adr) writeb((dat), (char *)(dev->iomem + (adr)))
|
|
#define ngwritel(dat, adr) writel((dat), (char *)(dev->iomem + (adr)))
|
|
#define ngwriteb(dat, adr) writeb((dat), (char *)(dev->iomem + (adr)))
|
|
#define ngreadl(adr) readl(dev->iomem + (adr))
|
|
#define ngreadb(adr) readb(dev->iomem + (adr))
|
|
#define ngcpyto(adr, src, count) memcpy_toio((char *) \
|
|
(dev->iomem + (adr)), (src), (count))
|
|
#define ngcpyfrom(dst, adr, count) memcpy_fromio((dst), (char *) \
|
|
(dev->iomem + (adr)), (count))
|
|
|
|
/****************************************************************************/
|
|
/* nGene interrupt handler **************************************************/
|
|
/****************************************************************************/
|
|
|
|
static void event_tasklet(unsigned long data)
|
|
{
|
|
struct ngene *dev = (struct ngene *)data;
|
|
|
|
while (dev->EventQueueReadIndex != dev->EventQueueWriteIndex) {
|
|
struct EVENT_BUFFER Event =
|
|
dev->EventQueue[dev->EventQueueReadIndex];
|
|
dev->EventQueueReadIndex =
|
|
(dev->EventQueueReadIndex + 1) & (EVENT_QUEUE_SIZE - 1);
|
|
|
|
if ((Event.UARTStatus & 0x01) && (dev->TxEventNotify))
|
|
dev->TxEventNotify(dev, Event.TimeStamp);
|
|
if ((Event.UARTStatus & 0x02) && (dev->RxEventNotify))
|
|
dev->RxEventNotify(dev, Event.TimeStamp,
|
|
Event.RXCharacter);
|
|
}
|
|
}
|
|
|
|
static void demux_tasklet(unsigned long data)
|
|
{
|
|
struct ngene_channel *chan = (struct ngene_channel *)data;
|
|
struct SBufferHeader *Cur = chan->nextBuffer;
|
|
|
|
spin_lock_irq(&chan->state_lock);
|
|
|
|
while (Cur->ngeneBuffer.SR.Flags & 0x80) {
|
|
if (chan->mode & NGENE_IO_TSOUT) {
|
|
u32 Flags = chan->DataFormatFlags;
|
|
if (Cur->ngeneBuffer.SR.Flags & 0x20)
|
|
Flags |= BEF_OVERFLOW;
|
|
if (chan->pBufferExchange) {
|
|
if (!chan->pBufferExchange(chan,
|
|
Cur->Buffer1,
|
|
chan->Capture1Length,
|
|
Cur->ngeneBuffer.SR.
|
|
Clock, Flags)) {
|
|
/*
|
|
We didn't get data
|
|
Clear in service flag to make sure we
|
|
get called on next interrupt again.
|
|
leave fill/empty (0x80) flag alone
|
|
to avoid hardware running out of
|
|
buffers during startup, we hold only
|
|
in run state ( the source may be late
|
|
delivering data )
|
|
*/
|
|
|
|
if (chan->HWState == HWSTATE_RUN) {
|
|
Cur->ngeneBuffer.SR.Flags &=
|
|
~0x40;
|
|
break;
|
|
/* Stop proccessing stream */
|
|
}
|
|
} else {
|
|
/* We got a valid buffer,
|
|
so switch to run state */
|
|
chan->HWState = HWSTATE_RUN;
|
|
}
|
|
} else {
|
|
printk(KERN_ERR DEVICE_NAME ": OOPS\n");
|
|
if (chan->HWState == HWSTATE_RUN) {
|
|
Cur->ngeneBuffer.SR.Flags &= ~0x40;
|
|
break; /* Stop proccessing stream */
|
|
}
|
|
}
|
|
if (chan->AudioDTOUpdated) {
|
|
printk(KERN_INFO DEVICE_NAME
|
|
": Update AudioDTO = %d\n",
|
|
chan->AudioDTOValue);
|
|
Cur->ngeneBuffer.SR.DTOUpdate =
|
|
chan->AudioDTOValue;
|
|
chan->AudioDTOUpdated = 0;
|
|
}
|
|
} else {
|
|
if (chan->HWState == HWSTATE_RUN) {
|
|
u32 Flags = 0;
|
|
if (Cur->ngeneBuffer.SR.Flags & 0x01)
|
|
Flags |= BEF_EVEN_FIELD;
|
|
if (Cur->ngeneBuffer.SR.Flags & 0x20)
|
|
Flags |= BEF_OVERFLOW;
|
|
if (chan->pBufferExchange)
|
|
chan->pBufferExchange(chan,
|
|
Cur->Buffer1,
|
|
chan->
|
|
Capture1Length,
|
|
Cur->ngeneBuffer.
|
|
SR.Clock, Flags);
|
|
if (chan->pBufferExchange2)
|
|
chan->pBufferExchange2(chan,
|
|
Cur->Buffer2,
|
|
chan->
|
|
Capture2Length,
|
|
Cur->ngeneBuffer.
|
|
SR.Clock, Flags);
|
|
} else if (chan->HWState != HWSTATE_STOP)
|
|
chan->HWState = HWSTATE_RUN;
|
|
}
|
|
Cur->ngeneBuffer.SR.Flags = 0x00;
|
|
Cur = Cur->Next;
|
|
}
|
|
chan->nextBuffer = Cur;
|
|
|
|
spin_unlock_irq(&chan->state_lock);
|
|
}
|
|
|
|
static irqreturn_t irq_handler(int irq, void *dev_id)
|
|
{
|
|
struct ngene *dev = (struct ngene *)dev_id;
|
|
u32 icounts = 0;
|
|
irqreturn_t rc = IRQ_NONE;
|
|
u32 i = MAX_STREAM;
|
|
u8 *tmpCmdDoneByte;
|
|
|
|
if (dev->BootFirmware) {
|
|
icounts = ngreadl(NGENE_INT_COUNTS);
|
|
if (icounts != dev->icounts) {
|
|
ngwritel(0, FORCE_NMI);
|
|
dev->cmd_done = 1;
|
|
wake_up(&dev->cmd_wq);
|
|
dev->icounts = icounts;
|
|
rc = IRQ_HANDLED;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
ngwritel(0, FORCE_NMI);
|
|
|
|
spin_lock(&dev->cmd_lock);
|
|
tmpCmdDoneByte = dev->CmdDoneByte;
|
|
if (tmpCmdDoneByte &&
|
|
(*tmpCmdDoneByte ||
|
|
(dev->ngenetohost[0] == 1 && dev->ngenetohost[1] != 0))) {
|
|
dev->CmdDoneByte = NULL;
|
|
dev->cmd_done = 1;
|
|
wake_up(&dev->cmd_wq);
|
|
rc = IRQ_HANDLED;
|
|
}
|
|
spin_unlock(&dev->cmd_lock);
|
|
|
|
if (dev->EventBuffer->EventStatus & 0x80) {
|
|
u8 nextWriteIndex =
|
|
(dev->EventQueueWriteIndex + 1) &
|
|
(EVENT_QUEUE_SIZE - 1);
|
|
if (nextWriteIndex != dev->EventQueueReadIndex) {
|
|
dev->EventQueue[dev->EventQueueWriteIndex] =
|
|
*(dev->EventBuffer);
|
|
dev->EventQueueWriteIndex = nextWriteIndex;
|
|
} else {
|
|
printk(KERN_ERR DEVICE_NAME ": event overflow\n");
|
|
dev->EventQueueOverflowCount += 1;
|
|
dev->EventQueueOverflowFlag = 1;
|
|
}
|
|
dev->EventBuffer->EventStatus &= ~0x80;
|
|
tasklet_schedule(&dev->event_tasklet);
|
|
rc = IRQ_HANDLED;
|
|
}
|
|
|
|
while (i > 0) {
|
|
i--;
|
|
spin_lock(&dev->channel[i].state_lock);
|
|
/* if (dev->channel[i].State>=KSSTATE_RUN) { */
|
|
if (dev->channel[i].nextBuffer) {
|
|
if ((dev->channel[i].nextBuffer->
|
|
ngeneBuffer.SR.Flags & 0xC0) == 0x80) {
|
|
dev->channel[i].nextBuffer->
|
|
ngeneBuffer.SR.Flags |= 0x40;
|
|
tasklet_schedule(
|
|
&dev->channel[i].demux_tasklet);
|
|
rc = IRQ_HANDLED;
|
|
}
|
|
}
|
|
spin_unlock(&dev->channel[i].state_lock);
|
|
}
|
|
|
|
/* Request might have been processed by a previous call. */
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* nGene command interface **************************************************/
|
|
/****************************************************************************/
|
|
|
|
static void dump_command_io(struct ngene *dev)
|
|
{
|
|
u8 buf[8], *b;
|
|
|
|
ngcpyfrom(buf, HOST_TO_NGENE, 8);
|
|
printk(KERN_ERR "host_to_ngene (%04x): %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
|
HOST_TO_NGENE, buf[0], buf[1], buf[2], buf[3],
|
|
buf[4], buf[5], buf[6], buf[7]);
|
|
|
|
ngcpyfrom(buf, NGENE_TO_HOST, 8);
|
|
printk(KERN_ERR "ngene_to_host (%04x): %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
|
NGENE_TO_HOST, buf[0], buf[1], buf[2], buf[3],
|
|
buf[4], buf[5], buf[6], buf[7]);
|
|
|
|
b = dev->hosttongene;
|
|
printk(KERN_ERR "dev->hosttongene (%p): %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
|
b, b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
|
|
|
|
b = dev->ngenetohost;
|
|
printk(KERN_ERR "dev->ngenetohost (%p): %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
|
b, b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
|
|
}
|
|
|
|
static int ngene_command_mutex(struct ngene *dev, struct ngene_command *com)
|
|
{
|
|
int ret;
|
|
u8 *tmpCmdDoneByte;
|
|
|
|
dev->cmd_done = 0;
|
|
|
|
if (com->cmd.hdr.Opcode == CMD_FWLOAD_PREPARE) {
|
|
dev->BootFirmware = 1;
|
|
dev->icounts = ngreadl(NGENE_INT_COUNTS);
|
|
ngwritel(0, NGENE_COMMAND);
|
|
ngwritel(0, NGENE_COMMAND_HI);
|
|
ngwritel(0, NGENE_STATUS);
|
|
ngwritel(0, NGENE_STATUS_HI);
|
|
ngwritel(0, NGENE_EVENT);
|
|
ngwritel(0, NGENE_EVENT_HI);
|
|
} else if (com->cmd.hdr.Opcode == CMD_FWLOAD_FINISH) {
|
|
u64 fwio = dev->PAFWInterfaceBuffer;
|
|
|
|
ngwritel(fwio & 0xffffffff, NGENE_COMMAND);
|
|
ngwritel(fwio >> 32, NGENE_COMMAND_HI);
|
|
ngwritel((fwio + 256) & 0xffffffff, NGENE_STATUS);
|
|
ngwritel((fwio + 256) >> 32, NGENE_STATUS_HI);
|
|
ngwritel((fwio + 512) & 0xffffffff, NGENE_EVENT);
|
|
ngwritel((fwio + 512) >> 32, NGENE_EVENT_HI);
|
|
}
|
|
|
|
memcpy(dev->FWInterfaceBuffer, com->cmd.raw8, com->in_len + 2);
|
|
|
|
if (dev->BootFirmware)
|
|
ngcpyto(HOST_TO_NGENE, com->cmd.raw8, com->in_len + 2);
|
|
|
|
spin_lock_irq(&dev->cmd_lock);
|
|
tmpCmdDoneByte = dev->ngenetohost + com->out_len;
|
|
if (!com->out_len)
|
|
tmpCmdDoneByte++;
|
|
*tmpCmdDoneByte = 0;
|
|
dev->ngenetohost[0] = 0;
|
|
dev->ngenetohost[1] = 0;
|
|
dev->CmdDoneByte = tmpCmdDoneByte;
|
|
spin_unlock_irq(&dev->cmd_lock);
|
|
|
|
/* Notify 8051. */
|
|
ngwritel(1, FORCE_INT);
|
|
|
|
ret = wait_event_timeout(dev->cmd_wq, dev->cmd_done == 1, 2 * HZ);
|
|
if (!ret) {
|
|
/*ngwritel(0, FORCE_NMI);*/
|
|
|
|
printk(KERN_ERR DEVICE_NAME
|
|
": Command timeout cmd=%02x prev=%02x\n",
|
|
com->cmd.hdr.Opcode, dev->prev_cmd);
|
|
dump_command_io(dev);
|
|
return -1;
|
|
}
|
|
if (com->cmd.hdr.Opcode == CMD_FWLOAD_FINISH)
|
|
dev->BootFirmware = 0;
|
|
|
|
dev->prev_cmd = com->cmd.hdr.Opcode;
|
|
|
|
if (!com->out_len)
|
|
return 0;
|
|
|
|
memcpy(com->cmd.raw8, dev->ngenetohost, com->out_len);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ngene_command(struct ngene *dev, struct ngene_command *com)
|
|
{
|
|
int result;
|
|
|
|
down(&dev->cmd_mutex);
|
|
result = ngene_command_mutex(dev, com);
|
|
up(&dev->cmd_mutex);
|
|
return result;
|
|
}
|
|
|
|
|
|
static int ngene_command_load_firmware(struct ngene *dev,
|
|
u8 *ngene_fw, u32 size)
|
|
{
|
|
#define FIRSTCHUNK (1024)
|
|
u32 cleft;
|
|
struct ngene_command com;
|
|
|
|
com.cmd.hdr.Opcode = CMD_FWLOAD_PREPARE;
|
|
com.cmd.hdr.Length = 0;
|
|
com.in_len = 0;
|
|
com.out_len = 0;
|
|
|
|
ngene_command(dev, &com);
|
|
|
|
cleft = (size + 3) & ~3;
|
|
if (cleft > FIRSTCHUNK) {
|
|
ngcpyto(PROGRAM_SRAM + FIRSTCHUNK, ngene_fw + FIRSTCHUNK,
|
|
cleft - FIRSTCHUNK);
|
|
cleft = FIRSTCHUNK;
|
|
}
|
|
ngcpyto(DATA_FIFO_AREA, ngene_fw, cleft);
|
|
|
|
memset(&com, 0, sizeof(struct ngene_command));
|
|
com.cmd.hdr.Opcode = CMD_FWLOAD_FINISH;
|
|
com.cmd.hdr.Length = 4;
|
|
com.cmd.FWLoadFinish.Address = DATA_FIFO_AREA;
|
|
com.cmd.FWLoadFinish.Length = (unsigned short)cleft;
|
|
com.in_len = 4;
|
|
com.out_len = 0;
|
|
|
|
return ngene_command(dev, &com);
|
|
}
|
|
|
|
|
|
static int ngene_command_config_buf(struct ngene *dev, u8 config)
|
|
{
|
|
struct ngene_command com;
|
|
|
|
com.cmd.hdr.Opcode = CMD_CONFIGURE_BUFFER;
|
|
com.cmd.hdr.Length = 1;
|
|
com.cmd.ConfigureBuffers.config = config;
|
|
com.in_len = 1;
|
|
com.out_len = 0;
|
|
|
|
if (ngene_command(dev, &com) < 0)
|
|
return -EIO;
|
|
return 0;
|
|
}
|
|
|
|
static int ngene_command_config_free_buf(struct ngene *dev, u8 *config)
|
|
{
|
|
struct ngene_command com;
|
|
|
|
com.cmd.hdr.Opcode = CMD_CONFIGURE_FREE_BUFFER;
|
|
com.cmd.hdr.Length = 6;
|
|
memcpy(&com.cmd.ConfigureBuffers.config, config, 6);
|
|
com.in_len = 6;
|
|
com.out_len = 0;
|
|
|
|
if (ngene_command(dev, &com) < 0)
|
|
return -EIO;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ngene_command_gpio_set(struct ngene *dev, u8 select, u8 level)
|
|
{
|
|
struct ngene_command com;
|
|
|
|
com.cmd.hdr.Opcode = CMD_SET_GPIO_PIN;
|
|
com.cmd.hdr.Length = 1;
|
|
com.cmd.SetGpioPin.select = select | (level << 7);
|
|
com.in_len = 1;
|
|
com.out_len = 0;
|
|
|
|
return ngene_command(dev, &com);
|
|
}
|
|
|
|
|
|
/*
|
|
02000640 is sample on rising edge.
|
|
02000740 is sample on falling edge.
|
|
02000040 is ignore "valid" signal
|
|
|
|
0: FD_CTL1 Bit 7,6 must be 0,1
|
|
7 disable(fw controlled)
|
|
6 0-AUX,1-TS
|
|
5 0-par,1-ser
|
|
4 0-lsb/1-msb
|
|
3,2 reserved
|
|
1,0 0-no sync, 1-use ext. start, 2-use 0x47, 3-both
|
|
1: FD_CTL2 has 3-valid must be hi, 2-use valid, 1-edge
|
|
2: FD_STA is read-only. 0-sync
|
|
3: FD_INSYNC is number of 47s to trigger "in sync".
|
|
4: FD_OUTSYNC is number of 47s to trigger "out of sync".
|
|
5: FD_MAXBYTE1 is low-order of bytes per packet.
|
|
6: FD_MAXBYTE2 is high-order of bytes per packet.
|
|
7: Top byte is unused.
|
|
*/
|
|
|
|
/****************************************************************************/
|
|
|
|
static u8 TSFeatureDecoderSetup[8 * 5] = {
|
|
0x42, 0x00, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00,
|
|
0x40, 0x06, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* DRXH */
|
|
0x71, 0x07, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* DRXHser */
|
|
0x72, 0x06, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* S2ser */
|
|
0x40, 0x07, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* LGDT3303 */
|
|
};
|
|
|
|
/* Set NGENE I2S Config to 16 bit packed */
|
|
static u8 I2SConfiguration[] = {
|
|
0x00, 0x10, 0x00, 0x00,
|
|
0x80, 0x10, 0x00, 0x00,
|
|
};
|
|
|
|
static u8 SPDIFConfiguration[10] = {
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
};
|
|
|
|
/* Set NGENE I2S Config to transport stream compatible mode */
|
|
|
|
static u8 TS_I2SConfiguration[4] = { 0x3E, 0x1A, 0x00, 0x00 }; /*3e 18 00 00 ?*/
|
|
|
|
static u8 TS_I2SOutConfiguration[4] = { 0x80, 0x20, 0x00, 0x00 };
|
|
|
|
static u8 ITUDecoderSetup[4][16] = {
|
|
{0x1c, 0x13, 0x01, 0x68, 0x3d, 0x90, 0x14, 0x20, /* SDTV */
|
|
0x00, 0x00, 0x01, 0xb0, 0x9c, 0x00, 0x00, 0x00},
|
|
{0x9c, 0x03, 0x23, 0xC0, 0x60, 0x0E, 0x13, 0x00,
|
|
0x00, 0x00, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x00},
|
|
{0x9f, 0x00, 0x23, 0xC0, 0x60, 0x0F, 0x13, 0x00, /* HDTV 1080i50 */
|
|
0x00, 0x00, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x00},
|
|
{0x9c, 0x01, 0x23, 0xC0, 0x60, 0x0E, 0x13, 0x00, /* HDTV 1080i60 */
|
|
0x00, 0x00, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x00},
|
|
};
|
|
|
|
/*
|
|
* 50 48 60 gleich
|
|
* 27p50 9f 00 22 80 42 69 18 ...
|
|
* 27p60 93 00 22 80 82 69 1c ...
|
|
*/
|
|
|
|
/* Maxbyte to 1144 (for raw data) */
|
|
static u8 ITUFeatureDecoderSetup[8] = {
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x04, 0x00
|
|
};
|
|
|
|
void FillTSBuffer(void *Buffer, int Length, u32 Flags)
|
|
{
|
|
u32 *ptr = Buffer;
|
|
|
|
memset(Buffer, 0xff, Length);
|
|
while (Length > 0) {
|
|
if (Flags & DF_SWAP32)
|
|
*ptr = 0x471FFF10;
|
|
else
|
|
*ptr = 0x10FF1F47;
|
|
ptr += (188 / 4);
|
|
Length -= 188;
|
|
}
|
|
}
|
|
|
|
|
|
static void flush_buffers(struct ngene_channel *chan)
|
|
{
|
|
u8 val;
|
|
|
|
do {
|
|
msleep(1);
|
|
spin_lock_irq(&chan->state_lock);
|
|
val = chan->nextBuffer->ngeneBuffer.SR.Flags & 0x80;
|
|
spin_unlock_irq(&chan->state_lock);
|
|
} while (val);
|
|
}
|
|
|
|
static void clear_buffers(struct ngene_channel *chan)
|
|
{
|
|
struct SBufferHeader *Cur = chan->nextBuffer;
|
|
|
|
do {
|
|
memset(&Cur->ngeneBuffer.SR, 0, sizeof(Cur->ngeneBuffer.SR));
|
|
if (chan->mode & NGENE_IO_TSOUT)
|
|
FillTSBuffer(Cur->Buffer1,
|
|
chan->Capture1Length,
|
|
chan->DataFormatFlags);
|
|
Cur = Cur->Next;
|
|
} while (Cur != chan->nextBuffer);
|
|
|
|
if (chan->mode & NGENE_IO_TSOUT) {
|
|
chan->nextBuffer->ngeneBuffer.SR.DTOUpdate =
|
|
chan->AudioDTOValue;
|
|
chan->AudioDTOUpdated = 0;
|
|
|
|
Cur = chan->TSIdleBuffer.Head;
|
|
|
|
do {
|
|
memset(&Cur->ngeneBuffer.SR, 0,
|
|
sizeof(Cur->ngeneBuffer.SR));
|
|
FillTSBuffer(Cur->Buffer1,
|
|
chan->Capture1Length,
|
|
chan->DataFormatFlags);
|
|
Cur = Cur->Next;
|
|
} while (Cur != chan->TSIdleBuffer.Head);
|
|
}
|
|
}
|
|
|
|
static int ngene_command_stream_control(struct ngene *dev, u8 stream,
|
|
u8 control, u8 mode, u8 flags)
|
|
{
|
|
struct ngene_channel *chan = &dev->channel[stream];
|
|
struct ngene_command com;
|
|
u16 BsUVI = ((stream & 1) ? 0x9400 : 0x9300);
|
|
u16 BsSDI = ((stream & 1) ? 0x9600 : 0x9500);
|
|
u16 BsSPI = ((stream & 1) ? 0x9800 : 0x9700);
|
|
u16 BsSDO = 0x9B00;
|
|
|
|
/* down(&dev->stream_mutex); */
|
|
while (down_trylock(&dev->stream_mutex)) {
|
|
printk(KERN_INFO DEVICE_NAME ": SC locked\n");
|
|
msleep(1);
|
|
}
|
|
memset(&com, 0, sizeof(com));
|
|
com.cmd.hdr.Opcode = CMD_CONTROL;
|
|
com.cmd.hdr.Length = sizeof(struct FW_STREAM_CONTROL) - 2;
|
|
com.cmd.StreamControl.Stream = stream | (control ? 8 : 0);
|
|
if (chan->mode & NGENE_IO_TSOUT)
|
|
com.cmd.StreamControl.Stream |= 0x07;
|
|
com.cmd.StreamControl.Control = control |
|
|
(flags & SFLAG_ORDER_LUMA_CHROMA);
|
|
com.cmd.StreamControl.Mode = mode;
|
|
com.in_len = sizeof(struct FW_STREAM_CONTROL);
|
|
com.out_len = 0;
|
|
|
|
dprintk(KERN_INFO DEVICE_NAME
|
|
": Stream=%02x, Control=%02x, Mode=%02x\n",
|
|
com.cmd.StreamControl.Stream, com.cmd.StreamControl.Control,
|
|
com.cmd.StreamControl.Mode);
|
|
|
|
chan->Mode = mode;
|
|
|
|
if (!(control & 0x80)) {
|
|
spin_lock_irq(&chan->state_lock);
|
|
if (chan->State == KSSTATE_RUN) {
|
|
chan->State = KSSTATE_ACQUIRE;
|
|
chan->HWState = HWSTATE_STOP;
|
|
spin_unlock_irq(&chan->state_lock);
|
|
if (ngene_command(dev, &com) < 0) {
|
|
up(&dev->stream_mutex);
|
|
return -1;
|
|
}
|
|
/* clear_buffers(chan); */
|
|
flush_buffers(chan);
|
|
up(&dev->stream_mutex);
|
|
return 0;
|
|
}
|
|
spin_unlock_irq(&chan->state_lock);
|
|
up(&dev->stream_mutex);
|
|
return 0;
|
|
}
|
|
|
|
if (mode & SMODE_AUDIO_CAPTURE) {
|
|
com.cmd.StreamControl.CaptureBlockCount =
|
|
chan->Capture1Length / AUDIO_BLOCK_SIZE;
|
|
com.cmd.StreamControl.Buffer_Address = chan->RingBuffer.PAHead;
|
|
} else if (mode & SMODE_TRANSPORT_STREAM) {
|
|
com.cmd.StreamControl.CaptureBlockCount =
|
|
chan->Capture1Length / TS_BLOCK_SIZE;
|
|
com.cmd.StreamControl.MaxLinesPerField =
|
|
chan->Capture1Length / TS_BLOCK_SIZE;
|
|
com.cmd.StreamControl.Buffer_Address =
|
|
chan->TSRingBuffer.PAHead;
|
|
if (chan->mode & NGENE_IO_TSOUT) {
|
|
com.cmd.StreamControl.BytesPerVBILine =
|
|
chan->Capture1Length / TS_BLOCK_SIZE;
|
|
com.cmd.StreamControl.Stream |= 0x07;
|
|
}
|
|
} else {
|
|
com.cmd.StreamControl.BytesPerVideoLine = chan->nBytesPerLine;
|
|
com.cmd.StreamControl.MaxLinesPerField = chan->nLines;
|
|
com.cmd.StreamControl.MinLinesPerField = 100;
|
|
com.cmd.StreamControl.Buffer_Address = chan->RingBuffer.PAHead;
|
|
|
|
if (mode & SMODE_VBI_CAPTURE) {
|
|
com.cmd.StreamControl.MaxVBILinesPerField =
|
|
chan->nVBILines;
|
|
com.cmd.StreamControl.MinVBILinesPerField = 0;
|
|
com.cmd.StreamControl.BytesPerVBILine =
|
|
chan->nBytesPerVBILine;
|
|
}
|
|
if (flags & SFLAG_COLORBAR)
|
|
com.cmd.StreamControl.Stream |= 0x04;
|
|
}
|
|
|
|
spin_lock_irq(&chan->state_lock);
|
|
if (mode & SMODE_AUDIO_CAPTURE) {
|
|
chan->nextBuffer = chan->RingBuffer.Head;
|
|
if (mode & SMODE_AUDIO_SPDIF) {
|
|
com.cmd.StreamControl.SetupDataLen =
|
|
sizeof(SPDIFConfiguration);
|
|
com.cmd.StreamControl.SetupDataAddr = BsSPI;
|
|
memcpy(com.cmd.StreamControl.SetupData,
|
|
SPDIFConfiguration, sizeof(SPDIFConfiguration));
|
|
} else {
|
|
com.cmd.StreamControl.SetupDataLen = 4;
|
|
com.cmd.StreamControl.SetupDataAddr = BsSDI;
|
|
memcpy(com.cmd.StreamControl.SetupData,
|
|
I2SConfiguration +
|
|
4 * dev->card_info->i2s[stream], 4);
|
|
}
|
|
} else if (mode & SMODE_TRANSPORT_STREAM) {
|
|
chan->nextBuffer = chan->TSRingBuffer.Head;
|
|
if (stream >= STREAM_AUDIOIN1) {
|
|
if (chan->mode & NGENE_IO_TSOUT) {
|
|
com.cmd.StreamControl.SetupDataLen =
|
|
sizeof(TS_I2SOutConfiguration);
|
|
com.cmd.StreamControl.SetupDataAddr = BsSDO;
|
|
memcpy(com.cmd.StreamControl.SetupData,
|
|
TS_I2SOutConfiguration,
|
|
sizeof(TS_I2SOutConfiguration));
|
|
} else {
|
|
com.cmd.StreamControl.SetupDataLen =
|
|
sizeof(TS_I2SConfiguration);
|
|
com.cmd.StreamControl.SetupDataAddr = BsSDI;
|
|
memcpy(com.cmd.StreamControl.SetupData,
|
|
TS_I2SConfiguration,
|
|
sizeof(TS_I2SConfiguration));
|
|
}
|
|
} else {
|
|
com.cmd.StreamControl.SetupDataLen = 8;
|
|
com.cmd.StreamControl.SetupDataAddr = BsUVI + 0x10;
|
|
memcpy(com.cmd.StreamControl.SetupData,
|
|
TSFeatureDecoderSetup +
|
|
8 * dev->card_info->tsf[stream], 8);
|
|
}
|
|
} else {
|
|
chan->nextBuffer = chan->RingBuffer.Head;
|
|
com.cmd.StreamControl.SetupDataLen =
|
|
16 + sizeof(ITUFeatureDecoderSetup);
|
|
com.cmd.StreamControl.SetupDataAddr = BsUVI;
|
|
memcpy(com.cmd.StreamControl.SetupData,
|
|
ITUDecoderSetup[chan->itumode], 16);
|
|
memcpy(com.cmd.StreamControl.SetupData + 16,
|
|
ITUFeatureDecoderSetup, sizeof(ITUFeatureDecoderSetup));
|
|
}
|
|
clear_buffers(chan);
|
|
chan->State = KSSTATE_RUN;
|
|
if (mode & SMODE_TRANSPORT_STREAM)
|
|
chan->HWState = HWSTATE_RUN;
|
|
else
|
|
chan->HWState = HWSTATE_STARTUP;
|
|
spin_unlock_irq(&chan->state_lock);
|
|
|
|
if (ngene_command(dev, &com) < 0) {
|
|
up(&dev->stream_mutex);
|
|
return -1;
|
|
}
|
|
up(&dev->stream_mutex);
|
|
return 0;
|
|
}
|
|
|
|
void set_transfer(struct ngene_channel *chan, int state)
|
|
{
|
|
u8 control = 0, mode = 0, flags = 0;
|
|
struct ngene *dev = chan->dev;
|
|
int ret;
|
|
|
|
/*
|
|
printk(KERN_INFO DEVICE_NAME ": st %d\n", state);
|
|
msleep(100);
|
|
*/
|
|
|
|
if (state) {
|
|
if (chan->running) {
|
|
printk(KERN_INFO DEVICE_NAME ": already running\n");
|
|
return;
|
|
}
|
|
} else {
|
|
if (!chan->running) {
|
|
printk(KERN_INFO DEVICE_NAME ": already stopped\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (dev->card_info->switch_ctrl)
|
|
dev->card_info->switch_ctrl(chan, 1, state ^ 1);
|
|
|
|
if (state) {
|
|
spin_lock_irq(&chan->state_lock);
|
|
|
|
/* printk(KERN_INFO DEVICE_NAME ": lock=%08x\n",
|
|
ngreadl(0x9310)); */
|
|
dvb_ringbuffer_flush(&dev->tsout_rbuf);
|
|
control = 0x80;
|
|
if (chan->mode & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) {
|
|
chan->Capture1Length = 512 * 188;
|
|
mode = SMODE_TRANSPORT_STREAM;
|
|
}
|
|
if (chan->mode & NGENE_IO_TSOUT) {
|
|
chan->pBufferExchange = tsout_exchange;
|
|
/* 0x66666666 = 50MHz *2^33 /250MHz */
|
|
chan->AudioDTOValue = 0x66666666;
|
|
/* set_dto(chan, 38810700+1000); */
|
|
/* set_dto(chan, 19392658); */
|
|
}
|
|
if (chan->mode & NGENE_IO_TSIN)
|
|
chan->pBufferExchange = tsin_exchange;
|
|
/* ngwritel(0, 0x9310); */
|
|
spin_unlock_irq(&chan->state_lock);
|
|
} else
|
|
;/* printk(KERN_INFO DEVICE_NAME ": lock=%08x\n",
|
|
ngreadl(0x9310)); */
|
|
|
|
ret = ngene_command_stream_control(dev, chan->number,
|
|
control, mode, flags);
|
|
if (!ret)
|
|
chan->running = state;
|
|
else
|
|
printk(KERN_ERR DEVICE_NAME ": set_transfer %d failed\n",
|
|
state);
|
|
if (!state) {
|
|
spin_lock_irq(&chan->state_lock);
|
|
chan->pBufferExchange = NULL;
|
|
dvb_ringbuffer_flush(&dev->tsout_rbuf);
|
|
spin_unlock_irq(&chan->state_lock);
|
|
}
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* nGene hardware init and release functions ********************************/
|
|
/****************************************************************************/
|
|
|
|
static void free_ringbuffer(struct ngene *dev, struct SRingBufferDescriptor *rb)
|
|
{
|
|
struct SBufferHeader *Cur = rb->Head;
|
|
u32 j;
|
|
|
|
if (!Cur)
|
|
return;
|
|
|
|
for (j = 0; j < rb->NumBuffers; j++, Cur = Cur->Next) {
|
|
if (Cur->Buffer1)
|
|
pci_free_consistent(dev->pci_dev,
|
|
rb->Buffer1Length,
|
|
Cur->Buffer1,
|
|
Cur->scList1->Address);
|
|
|
|
if (Cur->Buffer2)
|
|
pci_free_consistent(dev->pci_dev,
|
|
rb->Buffer2Length,
|
|
Cur->Buffer2,
|
|
Cur->scList2->Address);
|
|
}
|
|
|
|
if (rb->SCListMem)
|
|
pci_free_consistent(dev->pci_dev, rb->SCListMemSize,
|
|
rb->SCListMem, rb->PASCListMem);
|
|
|
|
pci_free_consistent(dev->pci_dev, rb->MemSize, rb->Head, rb->PAHead);
|
|
}
|
|
|
|
static void free_idlebuffer(struct ngene *dev,
|
|
struct SRingBufferDescriptor *rb,
|
|
struct SRingBufferDescriptor *tb)
|
|
{
|
|
int j;
|
|
struct SBufferHeader *Cur = tb->Head;
|
|
|
|
if (!rb->Head)
|
|
return;
|
|
free_ringbuffer(dev, rb);
|
|
for (j = 0; j < tb->NumBuffers; j++, Cur = Cur->Next) {
|
|
Cur->Buffer2 = NULL;
|
|
Cur->scList2 = NULL;
|
|
Cur->ngeneBuffer.Address_of_first_entry_2 = 0;
|
|
Cur->ngeneBuffer.Number_of_entries_2 = 0;
|
|
}
|
|
}
|
|
|
|
static void free_common_buffers(struct ngene *dev)
|
|
{
|
|
u32 i;
|
|
struct ngene_channel *chan;
|
|
|
|
for (i = STREAM_VIDEOIN1; i < MAX_STREAM; i++) {
|
|
chan = &dev->channel[i];
|
|
free_idlebuffer(dev, &chan->TSIdleBuffer, &chan->TSRingBuffer);
|
|
free_ringbuffer(dev, &chan->RingBuffer);
|
|
free_ringbuffer(dev, &chan->TSRingBuffer);
|
|
}
|
|
|
|
if (dev->OverflowBuffer)
|
|
pci_free_consistent(dev->pci_dev,
|
|
OVERFLOW_BUFFER_SIZE,
|
|
dev->OverflowBuffer, dev->PAOverflowBuffer);
|
|
|
|
if (dev->FWInterfaceBuffer)
|
|
pci_free_consistent(dev->pci_dev,
|
|
4096,
|
|
dev->FWInterfaceBuffer,
|
|
dev->PAFWInterfaceBuffer);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* Ring buffer handling *****************************************************/
|
|
/****************************************************************************/
|
|
|
|
static int create_ring_buffer(struct pci_dev *pci_dev,
|
|
struct SRingBufferDescriptor *descr, u32 NumBuffers)
|
|
{
|
|
dma_addr_t tmp;
|
|
struct SBufferHeader *Head;
|
|
u32 i;
|
|
u32 MemSize = SIZEOF_SBufferHeader * NumBuffers;
|
|
u64 PARingBufferHead;
|
|
u64 PARingBufferCur;
|
|
u64 PARingBufferNext;
|
|
struct SBufferHeader *Cur, *Next;
|
|
|
|
descr->Head = NULL;
|
|
descr->MemSize = 0;
|
|
descr->PAHead = 0;
|
|
descr->NumBuffers = 0;
|
|
|
|
if (MemSize < 4096)
|
|
MemSize = 4096;
|
|
|
|
Head = pci_alloc_consistent(pci_dev, MemSize, &tmp);
|
|
PARingBufferHead = tmp;
|
|
|
|
if (!Head)
|
|
return -ENOMEM;
|
|
|
|
memset(Head, 0, MemSize);
|
|
|
|
PARingBufferCur = PARingBufferHead;
|
|
Cur = Head;
|
|
|
|
for (i = 0; i < NumBuffers - 1; i++) {
|
|
Next = (struct SBufferHeader *)
|
|
(((u8 *) Cur) + SIZEOF_SBufferHeader);
|
|
PARingBufferNext = PARingBufferCur + SIZEOF_SBufferHeader;
|
|
Cur->Next = Next;
|
|
Cur->ngeneBuffer.Next = PARingBufferNext;
|
|
Cur = Next;
|
|
PARingBufferCur = PARingBufferNext;
|
|
}
|
|
/* Last Buffer points back to first one */
|
|
Cur->Next = Head;
|
|
Cur->ngeneBuffer.Next = PARingBufferHead;
|
|
|
|
descr->Head = Head;
|
|
descr->MemSize = MemSize;
|
|
descr->PAHead = PARingBufferHead;
|
|
descr->NumBuffers = NumBuffers;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int AllocateRingBuffers(struct pci_dev *pci_dev,
|
|
dma_addr_t of,
|
|
struct SRingBufferDescriptor *pRingBuffer,
|
|
u32 Buffer1Length, u32 Buffer2Length)
|
|
{
|
|
dma_addr_t tmp;
|
|
u32 i, j;
|
|
int status = 0;
|
|
u32 SCListMemSize = pRingBuffer->NumBuffers
|
|
* ((Buffer2Length != 0) ? (NUM_SCATTER_GATHER_ENTRIES * 2) :
|
|
NUM_SCATTER_GATHER_ENTRIES)
|
|
* sizeof(struct HW_SCATTER_GATHER_ELEMENT);
|
|
|
|
u64 PASCListMem;
|
|
struct HW_SCATTER_GATHER_ELEMENT *SCListEntry;
|
|
u64 PASCListEntry;
|
|
struct SBufferHeader *Cur;
|
|
void *SCListMem;
|
|
|
|
if (SCListMemSize < 4096)
|
|
SCListMemSize = 4096;
|
|
|
|
SCListMem = pci_alloc_consistent(pci_dev, SCListMemSize, &tmp);
|
|
|
|
PASCListMem = tmp;
|
|
if (SCListMem == NULL)
|
|
return -ENOMEM;
|
|
|
|
memset(SCListMem, 0, SCListMemSize);
|
|
|
|
pRingBuffer->SCListMem = SCListMem;
|
|
pRingBuffer->PASCListMem = PASCListMem;
|
|
pRingBuffer->SCListMemSize = SCListMemSize;
|
|
pRingBuffer->Buffer1Length = Buffer1Length;
|
|
pRingBuffer->Buffer2Length = Buffer2Length;
|
|
|
|
SCListEntry = SCListMem;
|
|
PASCListEntry = PASCListMem;
|
|
Cur = pRingBuffer->Head;
|
|
|
|
for (i = 0; i < pRingBuffer->NumBuffers; i += 1, Cur = Cur->Next) {
|
|
u64 PABuffer;
|
|
|
|
void *Buffer = pci_alloc_consistent(pci_dev, Buffer1Length,
|
|
&tmp);
|
|
PABuffer = tmp;
|
|
|
|
if (Buffer == NULL)
|
|
return -ENOMEM;
|
|
|
|
Cur->Buffer1 = Buffer;
|
|
|
|
SCListEntry->Address = PABuffer;
|
|
SCListEntry->Length = Buffer1Length;
|
|
|
|
Cur->scList1 = SCListEntry;
|
|
Cur->ngeneBuffer.Address_of_first_entry_1 = PASCListEntry;
|
|
Cur->ngeneBuffer.Number_of_entries_1 =
|
|
NUM_SCATTER_GATHER_ENTRIES;
|
|
|
|
SCListEntry += 1;
|
|
PASCListEntry += sizeof(struct HW_SCATTER_GATHER_ELEMENT);
|
|
|
|
#if NUM_SCATTER_GATHER_ENTRIES > 1
|
|
for (j = 0; j < NUM_SCATTER_GATHER_ENTRIES - 1; j += 1) {
|
|
SCListEntry->Address = of;
|
|
SCListEntry->Length = OVERFLOW_BUFFER_SIZE;
|
|
SCListEntry += 1;
|
|
PASCListEntry +=
|
|
sizeof(struct HW_SCATTER_GATHER_ELEMENT);
|
|
}
|
|
#endif
|
|
|
|
if (!Buffer2Length)
|
|
continue;
|
|
|
|
Buffer = pci_alloc_consistent(pci_dev, Buffer2Length, &tmp);
|
|
PABuffer = tmp;
|
|
|
|
if (Buffer == NULL)
|
|
return -ENOMEM;
|
|
|
|
Cur->Buffer2 = Buffer;
|
|
|
|
SCListEntry->Address = PABuffer;
|
|
SCListEntry->Length = Buffer2Length;
|
|
|
|
Cur->scList2 = SCListEntry;
|
|
Cur->ngeneBuffer.Address_of_first_entry_2 = PASCListEntry;
|
|
Cur->ngeneBuffer.Number_of_entries_2 =
|
|
NUM_SCATTER_GATHER_ENTRIES;
|
|
|
|
SCListEntry += 1;
|
|
PASCListEntry += sizeof(struct HW_SCATTER_GATHER_ELEMENT);
|
|
|
|
#if NUM_SCATTER_GATHER_ENTRIES > 1
|
|
for (j = 0; j < NUM_SCATTER_GATHER_ENTRIES - 1; j++) {
|
|
SCListEntry->Address = of;
|
|
SCListEntry->Length = OVERFLOW_BUFFER_SIZE;
|
|
SCListEntry += 1;
|
|
PASCListEntry +=
|
|
sizeof(struct HW_SCATTER_GATHER_ELEMENT);
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
static int FillTSIdleBuffer(struct SRingBufferDescriptor *pIdleBuffer,
|
|
struct SRingBufferDescriptor *pRingBuffer)
|
|
{
|
|
int status = 0;
|
|
|
|
/* Copy pointer to scatter gather list in TSRingbuffer
|
|
structure for buffer 2
|
|
Load number of buffer
|
|
*/
|
|
u32 n = pRingBuffer->NumBuffers;
|
|
|
|
/* Point to first buffer entry */
|
|
struct SBufferHeader *Cur = pRingBuffer->Head;
|
|
int i;
|
|
/* Loop thru all buffer and set Buffer 2 pointers to TSIdlebuffer */
|
|
for (i = 0; i < n; i++) {
|
|
Cur->Buffer2 = pIdleBuffer->Head->Buffer1;
|
|
Cur->scList2 = pIdleBuffer->Head->scList1;
|
|
Cur->ngeneBuffer.Address_of_first_entry_2 =
|
|
pIdleBuffer->Head->ngeneBuffer.
|
|
Address_of_first_entry_1;
|
|
Cur->ngeneBuffer.Number_of_entries_2 =
|
|
pIdleBuffer->Head->ngeneBuffer.Number_of_entries_1;
|
|
Cur = Cur->Next;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
static u32 RingBufferSizes[MAX_STREAM] = {
|
|
RING_SIZE_VIDEO,
|
|
RING_SIZE_VIDEO,
|
|
RING_SIZE_AUDIO,
|
|
RING_SIZE_AUDIO,
|
|
RING_SIZE_AUDIO,
|
|
};
|
|
|
|
static u32 Buffer1Sizes[MAX_STREAM] = {
|
|
MAX_VIDEO_BUFFER_SIZE,
|
|
MAX_VIDEO_BUFFER_SIZE,
|
|
MAX_AUDIO_BUFFER_SIZE,
|
|
MAX_AUDIO_BUFFER_SIZE,
|
|
MAX_AUDIO_BUFFER_SIZE
|
|
};
|
|
|
|
static u32 Buffer2Sizes[MAX_STREAM] = {
|
|
MAX_VBI_BUFFER_SIZE,
|
|
MAX_VBI_BUFFER_SIZE,
|
|
0,
|
|
0,
|
|
0
|
|
};
|
|
|
|
|
|
static int AllocCommonBuffers(struct ngene *dev)
|
|
{
|
|
int status = 0, i;
|
|
|
|
dev->FWInterfaceBuffer = pci_alloc_consistent(dev->pci_dev, 4096,
|
|
&dev->PAFWInterfaceBuffer);
|
|
if (!dev->FWInterfaceBuffer)
|
|
return -ENOMEM;
|
|
dev->hosttongene = dev->FWInterfaceBuffer;
|
|
dev->ngenetohost = dev->FWInterfaceBuffer + 256;
|
|
dev->EventBuffer = dev->FWInterfaceBuffer + 512;
|
|
|
|
dev->OverflowBuffer = pci_alloc_consistent(dev->pci_dev,
|
|
OVERFLOW_BUFFER_SIZE,
|
|
&dev->PAOverflowBuffer);
|
|
if (!dev->OverflowBuffer)
|
|
return -ENOMEM;
|
|
memset(dev->OverflowBuffer, 0, OVERFLOW_BUFFER_SIZE);
|
|
|
|
for (i = STREAM_VIDEOIN1; i < MAX_STREAM; i++) {
|
|
int type = dev->card_info->io_type[i];
|
|
|
|
dev->channel[i].State = KSSTATE_STOP;
|
|
|
|
if (type & (NGENE_IO_TV | NGENE_IO_HDTV | NGENE_IO_AIN)) {
|
|
status = create_ring_buffer(dev->pci_dev,
|
|
&dev->channel[i].RingBuffer,
|
|
RingBufferSizes[i]);
|
|
if (status < 0)
|
|
break;
|
|
|
|
if (type & (NGENE_IO_TV | NGENE_IO_AIN)) {
|
|
status = AllocateRingBuffers(dev->pci_dev,
|
|
dev->
|
|
PAOverflowBuffer,
|
|
&dev->channel[i].
|
|
RingBuffer,
|
|
Buffer1Sizes[i],
|
|
Buffer2Sizes[i]);
|
|
if (status < 0)
|
|
break;
|
|
} else if (type & NGENE_IO_HDTV) {
|
|
status = AllocateRingBuffers(dev->pci_dev,
|
|
dev->
|
|
PAOverflowBuffer,
|
|
&dev->channel[i].
|
|
RingBuffer,
|
|
MAX_HDTV_BUFFER_SIZE,
|
|
0);
|
|
if (status < 0)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (type & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) {
|
|
|
|
status = create_ring_buffer(dev->pci_dev,
|
|
&dev->channel[i].
|
|
TSRingBuffer, RING_SIZE_TS);
|
|
if (status < 0)
|
|
break;
|
|
|
|
status = AllocateRingBuffers(dev->pci_dev,
|
|
dev->PAOverflowBuffer,
|
|
&dev->channel[i].
|
|
TSRingBuffer,
|
|
MAX_TS_BUFFER_SIZE, 0);
|
|
if (status)
|
|
break;
|
|
}
|
|
|
|
if (type & NGENE_IO_TSOUT) {
|
|
status = create_ring_buffer(dev->pci_dev,
|
|
&dev->channel[i].
|
|
TSIdleBuffer, 1);
|
|
if (status < 0)
|
|
break;
|
|
status = AllocateRingBuffers(dev->pci_dev,
|
|
dev->PAOverflowBuffer,
|
|
&dev->channel[i].
|
|
TSIdleBuffer,
|
|
MAX_TS_BUFFER_SIZE, 0);
|
|
if (status)
|
|
break;
|
|
FillTSIdleBuffer(&dev->channel[i].TSIdleBuffer,
|
|
&dev->channel[i].TSRingBuffer);
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
static void ngene_release_buffers(struct ngene *dev)
|
|
{
|
|
if (dev->iomem)
|
|
iounmap(dev->iomem);
|
|
free_common_buffers(dev);
|
|
vfree(dev->tsout_buf);
|
|
vfree(dev->ain_buf);
|
|
vfree(dev->vin_buf);
|
|
vfree(dev);
|
|
}
|
|
|
|
static int ngene_get_buffers(struct ngene *dev)
|
|
{
|
|
if (AllocCommonBuffers(dev))
|
|
return -ENOMEM;
|
|
if (dev->card_info->io_type[4] & NGENE_IO_TSOUT) {
|
|
dev->tsout_buf = vmalloc(TSOUT_BUF_SIZE);
|
|
if (!dev->tsout_buf)
|
|
return -ENOMEM;
|
|
dvb_ringbuffer_init(&dev->tsout_rbuf,
|
|
dev->tsout_buf, TSOUT_BUF_SIZE);
|
|
}
|
|
if (dev->card_info->io_type[2] & NGENE_IO_AIN) {
|
|
dev->ain_buf = vmalloc(AIN_BUF_SIZE);
|
|
if (!dev->ain_buf)
|
|
return -ENOMEM;
|
|
dvb_ringbuffer_init(&dev->ain_rbuf, dev->ain_buf, AIN_BUF_SIZE);
|
|
}
|
|
if (dev->card_info->io_type[0] & NGENE_IO_HDTV) {
|
|
dev->vin_buf = vmalloc(VIN_BUF_SIZE);
|
|
if (!dev->vin_buf)
|
|
return -ENOMEM;
|
|
dvb_ringbuffer_init(&dev->vin_rbuf, dev->vin_buf, VIN_BUF_SIZE);
|
|
}
|
|
dev->iomem = ioremap(pci_resource_start(dev->pci_dev, 0),
|
|
pci_resource_len(dev->pci_dev, 0));
|
|
if (!dev->iomem)
|
|
return -ENOMEM;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void ngene_init(struct ngene *dev)
|
|
{
|
|
int i;
|
|
|
|
tasklet_init(&dev->event_tasklet, event_tasklet, (unsigned long)dev);
|
|
|
|
memset_io(dev->iomem + 0xc000, 0x00, 0x220);
|
|
memset_io(dev->iomem + 0xc400, 0x00, 0x100);
|
|
|
|
for (i = 0; i < MAX_STREAM; i++) {
|
|
dev->channel[i].dev = dev;
|
|
dev->channel[i].number = i;
|
|
}
|
|
|
|
dev->fw_interface_version = 0;
|
|
|
|
ngwritel(0, NGENE_INT_ENABLE);
|
|
|
|
dev->icounts = ngreadl(NGENE_INT_COUNTS);
|
|
|
|
dev->device_version = ngreadl(DEV_VER) & 0x0f;
|
|
printk(KERN_INFO DEVICE_NAME ": Device version %d\n",
|
|
dev->device_version);
|
|
}
|
|
|
|
static int ngene_load_firm(struct ngene *dev)
|
|
{
|
|
u32 size;
|
|
const struct firmware *fw = NULL;
|
|
u8 *ngene_fw;
|
|
char *fw_name;
|
|
int err, version;
|
|
|
|
version = dev->card_info->fw_version;
|
|
|
|
switch (version) {
|
|
default:
|
|
case 15:
|
|
version = 15;
|
|
size = 23466;
|
|
fw_name = "ngene_15.fw";
|
|
break;
|
|
case 16:
|
|
size = 23498;
|
|
fw_name = "ngene_16.fw";
|
|
break;
|
|
case 17:
|
|
size = 24446;
|
|
fw_name = "ngene_17.fw";
|
|
break;
|
|
}
|
|
|
|
if (request_firmware(&fw, fw_name, &dev->pci_dev->dev) < 0) {
|
|
printk(KERN_ERR DEVICE_NAME
|
|
": Could not load firmware file %s.\n", fw_name);
|
|
printk(KERN_INFO DEVICE_NAME
|
|
": Copy %s to your hotplug directory!\n", fw_name);
|
|
return -1;
|
|
}
|
|
if (size != fw->size) {
|
|
printk(KERN_ERR DEVICE_NAME
|
|
": Firmware %s has invalid size!", fw_name);
|
|
err = -1;
|
|
} else {
|
|
printk(KERN_INFO DEVICE_NAME
|
|
": Loading firmware file %s.\n", fw_name);
|
|
ngene_fw = (u8 *) fw->data;
|
|
err = ngene_command_load_firmware(dev, ngene_fw, size);
|
|
}
|
|
|
|
release_firmware(fw);
|
|
|
|
return err;
|
|
}
|
|
|
|
static void ngene_stop(struct ngene *dev)
|
|
{
|
|
down(&dev->cmd_mutex);
|
|
i2c_del_adapter(&(dev->channel[0].i2c_adapter));
|
|
i2c_del_adapter(&(dev->channel[1].i2c_adapter));
|
|
ngwritel(0, NGENE_INT_ENABLE);
|
|
ngwritel(0, NGENE_COMMAND);
|
|
ngwritel(0, NGENE_COMMAND_HI);
|
|
ngwritel(0, NGENE_STATUS);
|
|
ngwritel(0, NGENE_STATUS_HI);
|
|
ngwritel(0, NGENE_EVENT);
|
|
ngwritel(0, NGENE_EVENT_HI);
|
|
free_irq(dev->pci_dev->irq, dev);
|
|
}
|
|
|
|
static int ngene_start(struct ngene *dev)
|
|
{
|
|
int stat;
|
|
int i;
|
|
|
|
pci_set_master(dev->pci_dev);
|
|
ngene_init(dev);
|
|
|
|
stat = request_irq(dev->pci_dev->irq, irq_handler,
|
|
IRQF_SHARED, "nGene",
|
|
(void *)dev);
|
|
if (stat < 0)
|
|
return stat;
|
|
|
|
init_waitqueue_head(&dev->cmd_wq);
|
|
init_waitqueue_head(&dev->tx_wq);
|
|
init_waitqueue_head(&dev->rx_wq);
|
|
sema_init(&dev->cmd_mutex, 1);
|
|
sema_init(&dev->stream_mutex, 1);
|
|
sema_init(&dev->pll_mutex, 1);
|
|
sema_init(&dev->i2c_switch_mutex, 1);
|
|
spin_lock_init(&dev->cmd_lock);
|
|
for (i = 0; i < MAX_STREAM; i++)
|
|
spin_lock_init(&dev->channel[i].state_lock);
|
|
ngwritel(1, TIMESTAMPS);
|
|
|
|
ngwritel(1, NGENE_INT_ENABLE);
|
|
|
|
stat = ngene_load_firm(dev);
|
|
if (stat < 0)
|
|
goto fail;
|
|
|
|
stat = ngene_i2c_init(dev, 0);
|
|
if (stat < 0)
|
|
goto fail;
|
|
|
|
stat = ngene_i2c_init(dev, 1);
|
|
if (stat < 0)
|
|
goto fail;
|
|
|
|
if (dev->card_info->fw_version == 17) {
|
|
u8 tsin4_config[6] = {
|
|
3072 / 64, 3072 / 64, 0, 3072 / 64, 3072 / 64, 0};
|
|
u8 default_config[6] = {
|
|
4096 / 64, 4096 / 64, 0, 2048 / 64, 2048 / 64, 0};
|
|
u8 *bconf = default_config;
|
|
|
|
if (dev->card_info->io_type[3] == NGENE_IO_TSIN)
|
|
bconf = tsin4_config;
|
|
dprintk(KERN_DEBUG DEVICE_NAME ": FW 17 buffer config\n");
|
|
stat = ngene_command_config_free_buf(dev, bconf);
|
|
} else {
|
|
int bconf = BUFFER_CONFIG_4422;
|
|
if (dev->card_info->io_type[3] == NGENE_IO_TSIN)
|
|
bconf = BUFFER_CONFIG_3333;
|
|
stat = ngene_command_config_buf(dev, bconf);
|
|
}
|
|
return stat;
|
|
fail:
|
|
ngwritel(0, NGENE_INT_ENABLE);
|
|
free_irq(dev->pci_dev->irq, dev);
|
|
return stat;
|
|
}
|
|
|
|
|
|
|
|
|
|
/****************************************************************************/
|
|
/****************************************************************************/
|
|
/****************************************************************************/
|
|
|
|
static void release_channel(struct ngene_channel *chan)
|
|
{
|
|
struct dvb_demux *dvbdemux = &chan->demux;
|
|
struct ngene *dev = chan->dev;
|
|
struct ngene_info *ni = dev->card_info;
|
|
int io = ni->io_type[chan->number];
|
|
|
|
#ifdef COMMAND_TIMEOUT_WORKAROUND
|
|
if (chan->running)
|
|
set_transfer(chan, 0);
|
|
#endif
|
|
|
|
tasklet_kill(&chan->demux_tasklet);
|
|
|
|
if (io & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) {
|
|
if (chan->fe) {
|
|
dvb_unregister_frontend(chan->fe);
|
|
dvb_frontend_detach(chan->fe);
|
|
chan->fe = NULL;
|
|
}
|
|
dvbdemux->dmx.close(&dvbdemux->dmx);
|
|
dvbdemux->dmx.remove_frontend(&dvbdemux->dmx,
|
|
&chan->hw_frontend);
|
|
dvbdemux->dmx.remove_frontend(&dvbdemux->dmx,
|
|
&chan->mem_frontend);
|
|
dvb_dmxdev_release(&chan->dmxdev);
|
|
dvb_dmx_release(&chan->demux);
|
|
|
|
if (chan->number == 0 || !one_adapter)
|
|
dvb_unregister_adapter(&dev->adapter[chan->number]);
|
|
}
|
|
}
|
|
|
|
static int init_channel(struct ngene_channel *chan)
|
|
{
|
|
int ret = 0, nr = chan->number;
|
|
struct dvb_adapter *adapter = NULL;
|
|
struct dvb_demux *dvbdemux = &chan->demux;
|
|
struct ngene *dev = chan->dev;
|
|
struct ngene_info *ni = dev->card_info;
|
|
int io = ni->io_type[nr];
|
|
|
|
tasklet_init(&chan->demux_tasklet, demux_tasklet, (unsigned long)chan);
|
|
chan->users = 0;
|
|
chan->type = io;
|
|
chan->mode = chan->type; /* for now only one mode */
|
|
|
|
if (io & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) {
|
|
if (nr >= STREAM_AUDIOIN1)
|
|
chan->DataFormatFlags = DF_SWAP32;
|
|
if (nr == 0 || !one_adapter || dev->first_adapter == NULL) {
|
|
adapter = &dev->adapter[nr];
|
|
ret = dvb_register_adapter(adapter, "nGene",
|
|
THIS_MODULE,
|
|
&chan->dev->pci_dev->dev,
|
|
adapter_nr);
|
|
if (ret < 0)
|
|
return ret;
|
|
if (dev->first_adapter == NULL)
|
|
dev->first_adapter = adapter;
|
|
} else {
|
|
adapter = dev->first_adapter;
|
|
}
|
|
|
|
ret = my_dvb_dmx_ts_card_init(dvbdemux, "SW demux",
|
|
ngene_start_feed,
|
|
ngene_stop_feed, chan);
|
|
ret = my_dvb_dmxdev_ts_card_init(&chan->dmxdev, &chan->demux,
|
|
&chan->hw_frontend,
|
|
&chan->mem_frontend, adapter);
|
|
}
|
|
|
|
if (io & NGENE_IO_TSIN) {
|
|
chan->fe = NULL;
|
|
if (ni->demod_attach[nr])
|
|
ni->demod_attach[nr](chan);
|
|
if (chan->fe) {
|
|
if (dvb_register_frontend(adapter, chan->fe) < 0) {
|
|
if (chan->fe->ops.release)
|
|
chan->fe->ops.release(chan->fe);
|
|
chan->fe = NULL;
|
|
}
|
|
}
|
|
if (chan->fe && ni->tuner_attach[nr])
|
|
if (ni->tuner_attach[nr] (chan) < 0) {
|
|
printk(KERN_ERR DEVICE_NAME
|
|
": Tuner attach failed on channel %d!\n",
|
|
nr);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int init_channels(struct ngene *dev)
|
|
{
|
|
int i, j;
|
|
|
|
for (i = 0; i < MAX_STREAM; i++) {
|
|
dev->channel[i].number = i;
|
|
if (init_channel(&dev->channel[i]) < 0) {
|
|
for (j = i - 1; j >= 0; j--)
|
|
release_channel(&dev->channel[j]);
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* device probe/remove calls ************************************************/
|
|
/****************************************************************************/
|
|
|
|
void __devexit ngene_remove(struct pci_dev *pdev)
|
|
{
|
|
struct ngene *dev = (struct ngene *)pci_get_drvdata(pdev);
|
|
int i;
|
|
|
|
tasklet_kill(&dev->event_tasklet);
|
|
for (i = MAX_STREAM - 1; i >= 0; i--)
|
|
release_channel(&dev->channel[i]);
|
|
ngene_stop(dev);
|
|
ngene_release_buffers(dev);
|
|
pci_set_drvdata(pdev, NULL);
|
|
pci_disable_device(pdev);
|
|
}
|
|
|
|
int __devinit ngene_probe(struct pci_dev *pci_dev,
|
|
const struct pci_device_id *id)
|
|
{
|
|
struct ngene *dev;
|
|
int stat = 0;
|
|
|
|
if (pci_enable_device(pci_dev) < 0)
|
|
return -ENODEV;
|
|
|
|
dev = vmalloc(sizeof(struct ngene));
|
|
if (dev == NULL) {
|
|
stat = -ENOMEM;
|
|
goto fail0;
|
|
}
|
|
memset(dev, 0, sizeof(struct ngene));
|
|
|
|
dev->pci_dev = pci_dev;
|
|
dev->card_info = (struct ngene_info *)id->driver_data;
|
|
printk(KERN_INFO DEVICE_NAME ": Found %s\n", dev->card_info->name);
|
|
|
|
pci_set_drvdata(pci_dev, dev);
|
|
|
|
/* Alloc buffers and start nGene */
|
|
stat = ngene_get_buffers(dev);
|
|
if (stat < 0)
|
|
goto fail1;
|
|
stat = ngene_start(dev);
|
|
if (stat < 0)
|
|
goto fail1;
|
|
|
|
dev->i2c_current_bus = -1;
|
|
|
|
/* Register DVB adapters and devices for both channels */
|
|
if (init_channels(dev) < 0)
|
|
goto fail2;
|
|
|
|
return 0;
|
|
|
|
fail2:
|
|
ngene_stop(dev);
|
|
fail1:
|
|
ngene_release_buffers(dev);
|
|
fail0:
|
|
pci_disable_device(pci_dev);
|
|
pci_set_drvdata(pci_dev, NULL);
|
|
return stat;
|
|
}
|