2005-04-17 00:20:36 +02:00
|
|
|
/*
|
|
|
|
*
|
|
|
|
* Support for the mpeg transport stream transfers
|
|
|
|
* PCI function #2 of the cx2388x.
|
|
|
|
*
|
|
|
|
* (c) 2004 Jelle Foks <jelle@foks.8m.com>
|
|
|
|
* (c) 2004 Chris Pascoe <c.pascoe@itee.uq.edu.au>
|
|
|
|
* (c) 2004 Gerd Knorr <kraxel@bytesex.org>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/device.h>
|
2007-05-06 23:51:55 +02:00
|
|
|
#include <linux/dma-mapping.h>
|
2005-04-17 00:20:36 +02:00
|
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <asm/delay.h>
|
|
|
|
|
|
|
|
#include "cx88.h"
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
|
|
|
|
MODULE_DESCRIPTION("mpeg driver for cx2388x based TV cards");
|
|
|
|
MODULE_AUTHOR("Jelle Foks <jelle@foks.8m.com>");
|
|
|
|
MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
|
|
|
|
MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
|
|
|
|
static unsigned int debug = 0;
|
|
|
|
module_param(debug,int,0644);
|
|
|
|
MODULE_PARM_DESC(debug,"enable debug messages [mpeg]");
|
|
|
|
|
|
|
|
#define dprintk(level,fmt, arg...) if (debug >= level) \
|
2006-12-03 00:15:51 +01:00
|
|
|
printk(KERN_DEBUG "%s/2-mpeg: " fmt, dev->core->name, ## arg)
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2006-12-03 00:15:51 +01:00
|
|
|
#define mpeg_dbg(level,fmt, arg...) if (debug >= level) \
|
|
|
|
printk(KERN_DEBUG "%s/2-mpeg: " fmt, core->name, ## arg)
|
|
|
|
|
2007-04-27 17:31:06 +02:00
|
|
|
#if defined(CONFIG_MODULES) && defined(MODULE)
|
|
|
|
static void request_module_async(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct cx8802_dev *dev=container_of(work, struct cx8802_dev, request_module_wk);
|
2007-04-27 17:31:07 +02:00
|
|
|
|
2007-08-15 19:41:57 +02:00
|
|
|
if (dev->core->board.mpeg & CX88_MPEG_DVB)
|
2007-04-27 17:31:06 +02:00
|
|
|
request_module("cx88-dvb");
|
2007-08-15 19:41:57 +02:00
|
|
|
if (dev->core->board.mpeg & CX88_MPEG_BLACKBIRD)
|
2007-04-27 17:31:07 +02:00
|
|
|
request_module("cx88-blackbird");
|
2007-04-27 17:31:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void request_modules(struct cx8802_dev *dev)
|
|
|
|
{
|
|
|
|
INIT_WORK(&dev->request_module_wk, request_module_async);
|
|
|
|
schedule_work(&dev->request_module_wk);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
#define request_modules(dev)
|
|
|
|
#endif /* CONFIG_MODULES */
|
|
|
|
|
|
|
|
|
2006-12-03 00:15:51 +01:00
|
|
|
static LIST_HEAD(cx8802_devlist);
|
2005-04-17 00:20:36 +02:00
|
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
|
|
|
|
static int cx8802_start_dma(struct cx8802_dev *dev,
|
|
|
|
struct cx88_dmaqueue *q,
|
|
|
|
struct cx88_buffer *buf)
|
|
|
|
{
|
|
|
|
struct cx88_core *core = dev->core;
|
|
|
|
|
2007-10-14 07:52:16 +02:00
|
|
|
dprintk(1, "cx8802_start_dma w: %d, h: %d, f: %d\n",
|
|
|
|
buf->vb.width, buf->vb.height, buf->vb.field);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
/* setup fifo + format */
|
|
|
|
cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH28],
|
|
|
|
dev->ts_packet_size, buf->risc.dma);
|
|
|
|
|
|
|
|
/* write TS length to chip */
|
|
|
|
cx_write(MO_TS_LNGTH, buf->vb.width);
|
|
|
|
|
|
|
|
/* FIXME: this needs a review.
|
|
|
|
* also: move to cx88-blackbird + cx88-dvb source files? */
|
|
|
|
|
2006-12-03 00:15:51 +01:00
|
|
|
dprintk( 1, "core->active_type_id = 0x%08x\n", core->active_type_id);
|
|
|
|
|
|
|
|
if ( (core->active_type_id == CX88_MPEG_DVB) &&
|
2007-08-15 19:41:57 +02:00
|
|
|
(core->board.mpeg & CX88_MPEG_DVB) ) {
|
2006-12-03 00:15:51 +01:00
|
|
|
|
|
|
|
dprintk( 1, "cx8802_start_dma doing .dvb\n");
|
2005-04-17 00:20:36 +02:00
|
|
|
/* negedge driven & software reset */
|
2005-07-08 02:58:39 +02:00
|
|
|
cx_write(TS_GEN_CNTRL, 0x0040 | dev->ts_gen_cntrl);
|
2005-04-17 00:20:36 +02:00
|
|
|
udelay(100);
|
|
|
|
cx_write(MO_PINMUX_IO, 0x00);
|
2008-01-05 20:53:01 +01:00
|
|
|
cx_write(TS_HW_SOP_CNTRL, 0x47<<16|188<<4|0x01);
|
2007-08-15 19:41:57 +02:00
|
|
|
switch (core->boardnr) {
|
2005-09-09 22:03:41 +02:00
|
|
|
case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q:
|
|
|
|
case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T:
|
|
|
|
case CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD:
|
2006-04-07 07:21:31 +02:00
|
|
|
case CX88_BOARD_PCHDTV_HD5500:
|
2005-07-12 22:58:44 +02:00
|
|
|
cx_write(TS_SOP_STAT, 1<<13);
|
2005-09-09 22:03:41 +02:00
|
|
|
break;
|
2006-01-09 18:25:02 +01:00
|
|
|
case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1:
|
|
|
|
case CX88_BOARD_HAUPPAUGE_NOVASE2_S1:
|
|
|
|
cx_write(MO_PINMUX_IO, 0x88); /* Enable MPEG parallel IO and video signal pins */
|
|
|
|
udelay(100);
|
|
|
|
break;
|
2006-12-03 00:15:51 +01:00
|
|
|
case CX88_BOARD_HAUPPAUGE_HVR1300:
|
|
|
|
break;
|
2008-01-05 20:53:01 +01:00
|
|
|
case CX88_BOARD_PINNACLE_PCTV_HD_800i:
|
2008-01-16 01:57:14 +01:00
|
|
|
/* Enable MPEG parallel IO and video signal pins */
|
|
|
|
cx_write(MO_PINMUX_IO, 0x88);
|
|
|
|
cx_write(TS_HW_SOP_CNTRL, (0x47 << 16) | (188 << 4));
|
|
|
|
dev->ts_gen_cntrl = 5;
|
|
|
|
cx_write(TS_SOP_STAT, 0);
|
|
|
|
cx_write(TS_VALERR_CNTRL, 0);
|
2008-01-05 20:53:01 +01:00
|
|
|
udelay(100);
|
|
|
|
break;
|
2005-09-09 22:03:41 +02:00
|
|
|
default:
|
2005-07-12 22:58:44 +02:00
|
|
|
cx_write(TS_SOP_STAT, 0x00);
|
2005-09-09 22:03:41 +02:00
|
|
|
break;
|
2005-07-08 02:58:39 +02:00
|
|
|
}
|
2005-04-17 00:20:36 +02:00
|
|
|
cx_write(TS_GEN_CNTRL, dev->ts_gen_cntrl);
|
|
|
|
udelay(100);
|
2006-12-03 00:15:51 +01:00
|
|
|
} else if ( (core->active_type_id == CX88_MPEG_BLACKBIRD) &&
|
2007-08-15 19:41:57 +02:00
|
|
|
(core->board.mpeg & CX88_MPEG_BLACKBIRD) ) {
|
2006-12-03 00:15:51 +01:00
|
|
|
dprintk( 1, "cx8802_start_dma doing .blackbird\n");
|
2005-04-17 00:20:36 +02:00
|
|
|
cx_write(MO_PINMUX_IO, 0x88); /* enable MPEG parallel IO */
|
|
|
|
|
|
|
|
cx_write(TS_GEN_CNTRL, 0x46); /* punctured clock TS & posedge driven & software reset */
|
|
|
|
udelay(100);
|
|
|
|
|
|
|
|
cx_write(TS_HW_SOP_CNTRL, 0x408); /* mpeg start byte */
|
|
|
|
cx_write(TS_VALERR_CNTRL, 0x2000);
|
|
|
|
|
|
|
|
cx_write(TS_GEN_CNTRL, 0x06); /* punctured clock TS & posedge driven */
|
|
|
|
udelay(100);
|
2006-12-03 00:15:51 +01:00
|
|
|
} else {
|
|
|
|
printk( "%s() Failed. Unsupported value in .mpeg (0x%08x)\n", __FUNCTION__,
|
2007-08-15 19:41:57 +02:00
|
|
|
core->board.mpeg );
|
2006-12-03 00:15:51 +01:00
|
|
|
return -EINVAL;
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* reset counter */
|
|
|
|
cx_write(MO_TS_GPCNTRL, GP_COUNT_CONTROL_RESET);
|
|
|
|
q->count = 1;
|
|
|
|
|
|
|
|
/* enable irqs */
|
2006-04-10 14:27:08 +02:00
|
|
|
dprintk( 1, "setting the interrupt mask\n" );
|
2007-08-18 11:57:55 +02:00
|
|
|
cx_set(MO_PCI_INTMSK, core->pci_irqmask | PCI_INT_TSINT);
|
2005-06-24 07:05:03 +02:00
|
|
|
cx_set(MO_TS_INTMSK, 0x1f0011);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
/* start dma */
|
2005-06-24 07:05:03 +02:00
|
|
|
cx_set(MO_DEV_CNTRL2, (1<<5));
|
|
|
|
cx_set(MO_TS_DMACNTRL, 0x11);
|
2005-04-17 00:20:36 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cx8802_stop_dma(struct cx8802_dev *dev)
|
|
|
|
{
|
|
|
|
struct cx88_core *core = dev->core;
|
2006-04-10 14:27:08 +02:00
|
|
|
dprintk( 1, "cx8802_stop_dma\n" );
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
/* stop dma */
|
|
|
|
cx_clear(MO_TS_DMACNTRL, 0x11);
|
|
|
|
|
|
|
|
/* disable irqs */
|
2007-08-18 11:57:55 +02:00
|
|
|
cx_clear(MO_PCI_INTMSK, PCI_INT_TSINT);
|
2005-04-17 00:20:36 +02:00
|
|
|
cx_clear(MO_TS_INTMSK, 0x1f0011);
|
|
|
|
|
|
|
|
/* Reset the controller */
|
|
|
|
cx_write(TS_GEN_CNTRL, 0xcd);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cx8802_restart_queue(struct cx8802_dev *dev,
|
|
|
|
struct cx88_dmaqueue *q)
|
|
|
|
{
|
|
|
|
struct cx88_buffer *buf;
|
|
|
|
|
2007-08-27 23:16:54 +02:00
|
|
|
dprintk( 1, "cx8802_restart_queue\n" );
|
2005-04-17 00:20:36 +02:00
|
|
|
if (list_empty(&q->active))
|
2005-06-24 07:05:03 +02:00
|
|
|
{
|
2007-08-27 23:16:54 +02:00
|
|
|
struct cx88_buffer *prev;
|
|
|
|
prev = NULL;
|
|
|
|
|
|
|
|
dprintk(1, "cx8802_restart_queue: queue is empty\n" );
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
if (list_empty(&q->queued))
|
|
|
|
return 0;
|
|
|
|
buf = list_entry(q->queued.next, struct cx88_buffer, vb.queue);
|
|
|
|
if (NULL == prev) {
|
|
|
|
list_del(&buf->vb.queue);
|
|
|
|
list_add_tail(&buf->vb.queue,&q->active);
|
|
|
|
cx8802_start_dma(dev, q, buf);
|
2007-11-07 00:02:36 +01:00
|
|
|
buf->vb.state = VIDEOBUF_ACTIVE;
|
2007-08-27 23:16:54 +02:00
|
|
|
buf->count = q->count++;
|
|
|
|
mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
|
|
|
|
dprintk(1,"[%p/%d] restart_queue - first active\n",
|
|
|
|
buf,buf->vb.i);
|
|
|
|
|
|
|
|
} else if (prev->vb.width == buf->vb.width &&
|
|
|
|
prev->vb.height == buf->vb.height &&
|
|
|
|
prev->fmt == buf->fmt) {
|
|
|
|
list_del(&buf->vb.queue);
|
|
|
|
list_add_tail(&buf->vb.queue,&q->active);
|
2007-11-07 00:02:36 +01:00
|
|
|
buf->vb.state = VIDEOBUF_ACTIVE;
|
2007-08-27 23:16:54 +02:00
|
|
|
buf->count = q->count++;
|
|
|
|
prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
|
|
|
|
dprintk(1,"[%p/%d] restart_queue - move to active\n",
|
|
|
|
buf,buf->vb.i);
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
prev = buf;
|
|
|
|
}
|
2005-04-17 00:20:36 +02:00
|
|
|
return 0;
|
2005-06-24 07:05:03 +02:00
|
|
|
}
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
|
|
|
|
dprintk(2,"restart_queue [%p/%d]: restart dma\n",
|
|
|
|
buf, buf->vb.i);
|
|
|
|
cx8802_start_dma(dev, q, buf);
|
2007-10-14 07:52:17 +02:00
|
|
|
list_for_each_entry(buf, &q->active, vb.queue)
|
2005-04-17 00:20:36 +02:00
|
|
|
buf->count = q->count++;
|
|
|
|
mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
|
2006-03-10 16:29:15 +01:00
|
|
|
int cx8802_buf_prepare(struct videobuf_queue *q, struct cx8802_dev *dev,
|
|
|
|
struct cx88_buffer *buf, enum v4l2_field field)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
int size = dev->ts_packet_size * dev->ts_packet_count;
|
2007-08-23 21:37:49 +02:00
|
|
|
struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
|
2005-04-17 00:20:36 +02:00
|
|
|
int rc;
|
|
|
|
|
|
|
|
dprintk(1, "%s: %p\n", __FUNCTION__, buf);
|
|
|
|
if (0 != buf->vb.baddr && buf->vb.bsize < size)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2007-11-07 00:02:36 +01:00
|
|
|
if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
|
2005-04-17 00:20:36 +02:00
|
|
|
buf->vb.width = dev->ts_packet_size;
|
|
|
|
buf->vb.height = dev->ts_packet_count;
|
|
|
|
buf->vb.size = size;
|
2005-11-09 06:36:17 +01:00
|
|
|
buf->vb.field = field /*V4L2_FIELD_TOP*/;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2006-03-10 16:29:15 +01:00
|
|
|
if (0 != (rc = videobuf_iolock(q,&buf->vb,NULL)))
|
2005-04-17 00:20:36 +02:00
|
|
|
goto fail;
|
|
|
|
cx88_risc_databuffer(dev->pci, &buf->risc,
|
2007-08-23 21:37:49 +02:00
|
|
|
dma->sglist,
|
V4L/DVB (6083): cx88-alsa: Rework buffer handling
Rework the way the DMA buffer is handled and IRQs are generated.
ALSA uses a ring-buffer of multiple periods. Each period is supposed to
corrispond to one IRQ.
The existing driver was generating one interrupt per ring-buffer, as opposed
to per period. This meant that as soon as the IRQ was generated, the hardware
was already starting to re-write the beginning of the buffer. Since the DMA
happens on a per-line basis, there was only a narrow window to copy the data
out before the buffer was overwritten.
The cx88 core RISC program generator is modified so that it can set the IRQ
and counter flags to count every X lines of DMA transfer. This way we can
generate an interrupt every period instead of every full ring-buffer. Right
now only period of one line are supported, but it should be possible to
support longer periods. Note that a WRITE instruction generates an IRQ when
it starts, not when the transfer is finished. Thus to generate an IRQ when
line X is done, one must set the IRQ flag on the instruction that starts line
X+1, not the one that ends line X.
Change the line size so that there are four lines in the SRAM FIFO. If there
are not four lines, the analog output from the cx88's internal DACs is full of
clicks and pops.
Try to handle FIFO sync errors. Sometimes the chip generates many of these
errors before audio data starts. Up to 50 sync errors will be ignored and the
counter reset.
Have the IRQ handler save the RISC counter to the chip struct, and then have
the pointer callback use this to calculate the pointer position. We could
read the counter from the pointer callback, but sometimes the sync errors on
start up cause the counter to go crazy. ALSA sees this and thinks there has
been an overrun. The IRQ hander can avoid saving the counter position on
sync errors.
The chip "opened" flag wasn't necessary. ALSA won't try to open the same
substream multiple times. Probably this code was cut&pasted from the bt87x
driver, which has multiple sub-streams for one chip.
Do error checking for the videobuf mapping functions.
snd_card_cx88_runtime_free() is useless and can be deleted.
Signed-off-by: Trent Piepho <xyzzy@speakeasy.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
2007-08-24 06:06:34 +02:00
|
|
|
buf->vb.width, buf->vb.height, 0);
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
2007-11-07 00:02:36 +01:00
|
|
|
buf->vb.state = VIDEOBUF_PREPARED;
|
2005-04-17 00:20:36 +02:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
fail:
|
2006-03-10 16:29:15 +01:00
|
|
|
cx88_free_buffer(q,buf);
|
2005-04-17 00:20:36 +02:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf)
|
|
|
|
{
|
|
|
|
struct cx88_buffer *prev;
|
2006-03-10 16:29:15 +01:00
|
|
|
struct cx88_dmaqueue *cx88q = &dev->mpegq;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2005-06-24 07:05:03 +02:00
|
|
|
dprintk( 1, "cx8802_buf_queue\n" );
|
2005-04-17 00:20:36 +02:00
|
|
|
/* add jump to stopper */
|
|
|
|
buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
|
2006-03-10 16:29:15 +01:00
|
|
|
buf->risc.jmp[1] = cpu_to_le32(cx88q->stopper.dma);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2006-03-10 16:29:15 +01:00
|
|
|
if (list_empty(&cx88q->active)) {
|
2006-04-10 14:27:08 +02:00
|
|
|
dprintk( 1, "queue is empty - first active\n" );
|
2006-03-10 16:29:15 +01:00
|
|
|
list_add_tail(&buf->vb.queue,&cx88q->active);
|
|
|
|
cx8802_start_dma(dev, cx88q, buf);
|
2007-11-07 00:02:36 +01:00
|
|
|
buf->vb.state = VIDEOBUF_ACTIVE;
|
2006-03-10 16:29:15 +01:00
|
|
|
buf->count = cx88q->count++;
|
|
|
|
mod_timer(&cx88q->timeout, jiffies+BUFFER_TIMEOUT);
|
2006-04-10 14:27:08 +02:00
|
|
|
dprintk(1,"[%p/%d] %s - first active\n",
|
2005-04-17 00:20:36 +02:00
|
|
|
buf, buf->vb.i, __FUNCTION__);
|
|
|
|
|
|
|
|
} else {
|
2005-06-24 07:05:03 +02:00
|
|
|
dprintk( 1, "queue is not empty - append to active\n" );
|
2006-03-10 16:29:15 +01:00
|
|
|
prev = list_entry(cx88q->active.prev, struct cx88_buffer, vb.queue);
|
|
|
|
list_add_tail(&buf->vb.queue,&cx88q->active);
|
2007-11-07 00:02:36 +01:00
|
|
|
buf->vb.state = VIDEOBUF_ACTIVE;
|
2006-03-10 16:29:15 +01:00
|
|
|
buf->count = cx88q->count++;
|
2005-04-17 00:20:36 +02:00
|
|
|
prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
|
2005-06-24 07:05:03 +02:00
|
|
|
dprintk( 1, "[%p/%d] %s - append to active\n",
|
2005-04-17 00:20:36 +02:00
|
|
|
buf, buf->vb.i, __FUNCTION__);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ----------------------------------------------------------- */
|
|
|
|
|
|
|
|
static void do_cancel_buffers(struct cx8802_dev *dev, char *reason, int restart)
|
|
|
|
{
|
|
|
|
struct cx88_dmaqueue *q = &dev->mpegq;
|
|
|
|
struct cx88_buffer *buf;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&dev->slock,flags);
|
|
|
|
while (!list_empty(&q->active)) {
|
|
|
|
buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
|
|
|
|
list_del(&buf->vb.queue);
|
2007-11-07 00:02:36 +01:00
|
|
|
buf->vb.state = VIDEOBUF_ERROR;
|
2005-04-17 00:20:36 +02:00
|
|
|
wake_up(&buf->vb.done);
|
|
|
|
dprintk(1,"[%p/%d] %s - dma=0x%08lx\n",
|
|
|
|
buf, buf->vb.i, reason, (unsigned long)buf->risc.dma);
|
|
|
|
}
|
|
|
|
if (restart)
|
2005-06-24 07:05:03 +02:00
|
|
|
{
|
2006-04-10 14:27:08 +02:00
|
|
|
dprintk(1, "restarting queue\n" );
|
2005-04-17 00:20:36 +02:00
|
|
|
cx8802_restart_queue(dev,q);
|
2005-06-24 07:05:03 +02:00
|
|
|
}
|
2005-04-17 00:20:36 +02:00
|
|
|
spin_unlock_irqrestore(&dev->slock,flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
void cx8802_cancel_buffers(struct cx8802_dev *dev)
|
|
|
|
{
|
|
|
|
struct cx88_dmaqueue *q = &dev->mpegq;
|
|
|
|
|
2005-06-24 07:05:03 +02:00
|
|
|
dprintk( 1, "cx8802_cancel_buffers" );
|
2005-04-17 00:20:36 +02:00
|
|
|
del_timer_sync(&q->timeout);
|
|
|
|
cx8802_stop_dma(dev);
|
|
|
|
do_cancel_buffers(dev,"cancel",0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void cx8802_timeout(unsigned long data)
|
|
|
|
{
|
|
|
|
struct cx8802_dev *dev = (struct cx8802_dev*)data;
|
|
|
|
|
2007-06-22 22:26:54 +02:00
|
|
|
dprintk(1, "%s\n",__FUNCTION__);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
if (debug)
|
|
|
|
cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH28]);
|
|
|
|
cx8802_stop_dma(dev);
|
|
|
|
do_cancel_buffers(dev,"timeout",1);
|
|
|
|
}
|
|
|
|
|
2005-07-12 22:58:44 +02:00
|
|
|
static char *cx88_mpeg_irqs[32] = {
|
|
|
|
"ts_risci1", NULL, NULL, NULL,
|
|
|
|
"ts_risci2", NULL, NULL, NULL,
|
|
|
|
"ts_oflow", NULL, NULL, NULL,
|
|
|
|
"ts_sync", NULL, NULL, NULL,
|
|
|
|
"opc_err", "par_err", "rip_err", "pci_abort",
|
|
|
|
"ts_err?",
|
|
|
|
};
|
|
|
|
|
2005-04-17 00:20:36 +02:00
|
|
|
static void cx8802_mpeg_irq(struct cx8802_dev *dev)
|
|
|
|
{
|
|
|
|
struct cx88_core *core = dev->core;
|
|
|
|
u32 status, mask, count;
|
|
|
|
|
2005-06-24 07:05:03 +02:00
|
|
|
dprintk( 1, "cx8802_mpeg_irq\n" );
|
2005-04-17 00:20:36 +02:00
|
|
|
status = cx_read(MO_TS_INTSTAT);
|
|
|
|
mask = cx_read(MO_TS_INTMSK);
|
|
|
|
if (0 == (status & mask))
|
|
|
|
return;
|
|
|
|
|
|
|
|
cx_write(MO_TS_INTSTAT, status);
|
2005-07-12 22:58:44 +02:00
|
|
|
|
2005-04-17 00:20:36 +02:00
|
|
|
if (debug || (status & mask & ~0xff))
|
|
|
|
cx88_print_irqbits(core->name, "irq mpeg ",
|
2007-03-29 13:47:04 +02:00
|
|
|
cx88_mpeg_irqs, ARRAY_SIZE(cx88_mpeg_irqs),
|
|
|
|
status, mask);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
/* risc op code error */
|
|
|
|
if (status & (1 << 16)) {
|
|
|
|
printk(KERN_WARNING "%s: mpeg risc op code error\n",core->name);
|
|
|
|
cx_clear(MO_TS_DMACNTRL, 0x11);
|
|
|
|
cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH28]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* risc1 y */
|
|
|
|
if (status & 0x01) {
|
2005-06-24 07:05:03 +02:00
|
|
|
dprintk( 1, "wake up\n" );
|
2005-04-17 00:20:36 +02:00
|
|
|
spin_lock(&dev->slock);
|
|
|
|
count = cx_read(MO_TS_GPCNT);
|
|
|
|
cx88_wakeup(dev->core, &dev->mpegq, count);
|
|
|
|
spin_unlock(&dev->slock);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* risc2 y */
|
|
|
|
if (status & 0x10) {
|
|
|
|
spin_lock(&dev->slock);
|
|
|
|
cx8802_restart_queue(dev,&dev->mpegq);
|
|
|
|
spin_unlock(&dev->slock);
|
|
|
|
}
|
|
|
|
|
2005-11-09 06:37:43 +01:00
|
|
|
/* other general errors */
|
|
|
|
if (status & 0x1f0100) {
|
2005-06-24 07:05:03 +02:00
|
|
|
dprintk( 0, "general errors: 0x%08x\n", status & 0x1f0100 );
|
2005-11-09 06:37:43 +01:00
|
|
|
spin_lock(&dev->slock);
|
2005-04-17 00:20:36 +02:00
|
|
|
cx8802_stop_dma(dev);
|
2005-11-09 06:37:43 +01:00
|
|
|
cx8802_restart_queue(dev,&dev->mpegq);
|
|
|
|
spin_unlock(&dev->slock);
|
|
|
|
}
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
2005-06-24 07:05:03 +02:00
|
|
|
#define MAX_IRQ_LOOP 10
|
|
|
|
|
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 15:55:46 +02:00
|
|
|
static irqreturn_t cx8802_irq(int irq, void *dev_id)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
struct cx8802_dev *dev = dev_id;
|
|
|
|
struct cx88_core *core = dev->core;
|
|
|
|
u32 status;
|
|
|
|
int loop, handled = 0;
|
|
|
|
|
2005-06-24 07:05:03 +02:00
|
|
|
for (loop = 0; loop < MAX_IRQ_LOOP; loop++) {
|
2007-08-18 11:57:55 +02:00
|
|
|
status = cx_read(MO_PCI_INTSTAT) &
|
|
|
|
(core->pci_irqmask | PCI_INT_TSINT);
|
2005-04-17 00:20:36 +02:00
|
|
|
if (0 == status)
|
|
|
|
goto out;
|
2005-06-24 07:05:03 +02:00
|
|
|
dprintk( 1, "cx8802_irq\n" );
|
|
|
|
dprintk( 1, " loop: %d/%d\n", loop, MAX_IRQ_LOOP );
|
|
|
|
dprintk( 1, " status: %d\n", status );
|
2005-04-17 00:20:36 +02:00
|
|
|
handled = 1;
|
|
|
|
cx_write(MO_PCI_INTSTAT, status);
|
|
|
|
|
|
|
|
if (status & core->pci_irqmask)
|
|
|
|
cx88_core_irq(core,status);
|
2007-08-18 11:57:55 +02:00
|
|
|
if (status & PCI_INT_TSINT)
|
2005-04-17 00:20:36 +02:00
|
|
|
cx8802_mpeg_irq(dev);
|
|
|
|
};
|
2005-06-24 07:05:03 +02:00
|
|
|
if (MAX_IRQ_LOOP == loop) {
|
|
|
|
dprintk( 0, "clearing mask\n" );
|
2005-04-17 00:20:36 +02:00
|
|
|
printk(KERN_WARNING "%s/0: irq loop -- clearing mask\n",
|
|
|
|
core->name);
|
|
|
|
cx_write(MO_PCI_INTMSK,0);
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
return IRQ_RETVAL(handled);
|
|
|
|
}
|
|
|
|
|
2007-11-05 18:07:22 +01:00
|
|
|
static int cx8802_init_common(struct cx8802_dev *dev)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
struct cx88_core *core = dev->core;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
/* pci init */
|
|
|
|
if (pci_enable_device(dev->pci))
|
|
|
|
return -EIO;
|
|
|
|
pci_set_master(dev->pci);
|
2007-03-30 15:58:01 +02:00
|
|
|
if (!pci_dma_supported(dev->pci,DMA_32BIT_MASK)) {
|
2005-04-17 00:20:36 +02:00
|
|
|
printk("%s/2: Oops: no 32bit PCI DMA ???\n",dev->core->name);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
pci_read_config_byte(dev->pci, PCI_CLASS_REVISION, &dev->pci_rev);
|
2005-11-09 06:37:43 +01:00
|
|
|
pci_read_config_byte(dev->pci, PCI_LATENCY_TIMER, &dev->pci_lat);
|
|
|
|
printk(KERN_INFO "%s/2: found at %s, rev: %d, irq: %d, "
|
2006-06-13 00:16:52 +02:00
|
|
|
"latency: %d, mmio: 0x%llx\n", dev->core->name,
|
2005-04-17 00:20:36 +02:00
|
|
|
pci_name(dev->pci), dev->pci_rev, dev->pci->irq,
|
2006-06-13 00:16:52 +02:00
|
|
|
dev->pci_lat,(unsigned long long)pci_resource_start(dev->pci,0));
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
/* initialize driver struct */
|
|
|
|
spin_lock_init(&dev->slock);
|
|
|
|
|
|
|
|
/* init dma queue */
|
|
|
|
INIT_LIST_HEAD(&dev->mpegq.active);
|
|
|
|
INIT_LIST_HEAD(&dev->mpegq.queued);
|
|
|
|
dev->mpegq.timeout.function = cx8802_timeout;
|
|
|
|
dev->mpegq.timeout.data = (unsigned long)dev;
|
|
|
|
init_timer(&dev->mpegq.timeout);
|
|
|
|
cx88_risc_stopper(dev->pci,&dev->mpegq.stopper,
|
|
|
|
MO_TS_DMACNTRL,0x11,0x00);
|
|
|
|
|
|
|
|
/* get irq */
|
|
|
|
err = request_irq(dev->pci->irq, cx8802_irq,
|
2006-07-02 04:29:37 +02:00
|
|
|
IRQF_SHARED | IRQF_DISABLED, dev->core->name, dev);
|
2005-04-17 00:20:36 +02:00
|
|
|
if (err < 0) {
|
|
|
|
printk(KERN_ERR "%s: can't get IRQ %d\n",
|
|
|
|
dev->core->name, dev->pci->irq);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
cx_set(MO_PCI_INTMSK, core->pci_irqmask);
|
|
|
|
|
|
|
|
/* everything worked */
|
|
|
|
pci_set_drvdata(dev->pci,dev);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-11-05 18:07:22 +01:00
|
|
|
static void cx8802_fini_common(struct cx8802_dev *dev)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-06-24 07:05:03 +02:00
|
|
|
dprintk( 2, "cx8802_fini_common\n" );
|
2005-04-17 00:20:36 +02:00
|
|
|
cx8802_stop_dma(dev);
|
|
|
|
pci_disable_device(dev->pci);
|
|
|
|
|
|
|
|
/* unregister stuff */
|
|
|
|
free_irq(dev->pci->irq, dev);
|
|
|
|
pci_set_drvdata(dev->pci, NULL);
|
|
|
|
|
|
|
|
/* free memory */
|
|
|
|
btcx_riscmem_free(dev->pci,&dev->mpegq.stopper);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ----------------------------------------------------------- */
|
|
|
|
|
2007-11-05 18:07:22 +01:00
|
|
|
static int cx8802_suspend_common(struct pci_dev *pci_dev, pm_message_t state)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-09 06:37:43 +01:00
|
|
|
struct cx8802_dev *dev = pci_get_drvdata(pci_dev);
|
2005-04-17 00:20:36 +02:00
|
|
|
struct cx88_core *core = dev->core;
|
|
|
|
|
|
|
|
/* stop mpeg dma */
|
|
|
|
spin_lock(&dev->slock);
|
|
|
|
if (!list_empty(&dev->mpegq.active)) {
|
2005-06-24 07:05:03 +02:00
|
|
|
dprintk( 2, "suspend\n" );
|
2005-04-17 00:20:36 +02:00
|
|
|
printk("%s: suspend mpeg\n", core->name);
|
|
|
|
cx8802_stop_dma(dev);
|
|
|
|
del_timer(&dev->mpegq.timeout);
|
|
|
|
}
|
|
|
|
spin_unlock(&dev->slock);
|
|
|
|
|
|
|
|
/* FIXME -- shutdown device */
|
|
|
|
cx88_shutdown(dev->core);
|
|
|
|
|
|
|
|
pci_save_state(pci_dev);
|
|
|
|
if (0 != pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state))) {
|
|
|
|
pci_disable_device(pci_dev);
|
|
|
|
dev->state.disabled = 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-11-05 18:07:22 +01:00
|
|
|
static int cx8802_resume_common(struct pci_dev *pci_dev)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-09-09 22:03:55 +02:00
|
|
|
struct cx8802_dev *dev = pci_get_drvdata(pci_dev);
|
2005-04-17 00:20:36 +02:00
|
|
|
struct cx88_core *core = dev->core;
|
2005-09-09 22:03:55 +02:00
|
|
|
int err;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
if (dev->state.disabled) {
|
2005-09-09 22:03:55 +02:00
|
|
|
err=pci_enable_device(pci_dev);
|
|
|
|
if (err) {
|
|
|
|
printk(KERN_ERR "%s: can't enable device\n",
|
|
|
|
dev->core->name);
|
|
|
|
return err;
|
|
|
|
}
|
2005-04-17 00:20:36 +02:00
|
|
|
dev->state.disabled = 0;
|
|
|
|
}
|
2005-09-09 22:03:55 +02:00
|
|
|
err=pci_set_power_state(pci_dev, PCI_D0);
|
|
|
|
if (err) {
|
|
|
|
printk(KERN_ERR "%s: can't enable device\n",
|
|
|
|
dev->core->name);
|
|
|
|
pci_disable_device(pci_dev);
|
|
|
|
dev->state.disabled = 1;
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
2005-04-17 00:20:36 +02:00
|
|
|
pci_restore_state(pci_dev);
|
|
|
|
|
|
|
|
/* FIXME: re-initialize hardware */
|
|
|
|
cx88_reset(dev->core);
|
|
|
|
|
|
|
|
/* restart video+vbi capture */
|
|
|
|
spin_lock(&dev->slock);
|
|
|
|
if (!list_empty(&dev->mpegq.active)) {
|
|
|
|
printk("%s: resume mpeg\n", core->name);
|
|
|
|
cx8802_restart_queue(dev,&dev->mpegq);
|
|
|
|
}
|
|
|
|
spin_unlock(&dev->slock);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-10-14 07:52:16 +02:00
|
|
|
#if defined(CONFIG_VIDEO_CX88_BLACKBIRD) || \
|
|
|
|
defined(CONFIG_VIDEO_CX88_BLACKBIRD_MODULE)
|
2006-12-03 00:15:51 +01:00
|
|
|
struct cx8802_dev * cx8802_get_device(struct inode *inode)
|
|
|
|
{
|
|
|
|
int minor = iminor(inode);
|
2007-10-14 07:52:17 +02:00
|
|
|
struct cx8802_dev *dev;
|
2006-12-03 00:15:51 +01:00
|
|
|
|
2007-10-14 07:52:17 +02:00
|
|
|
list_for_each_entry(dev, &cx8802_devlist, devlist)
|
|
|
|
if (dev->mpeg_dev && dev->mpeg_dev->minor == minor)
|
|
|
|
return dev;
|
2006-12-03 00:15:51 +01:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
2007-10-14 07:52:16 +02:00
|
|
|
EXPORT_SYMBOL(cx8802_get_device);
|
|
|
|
#endif
|
2006-12-03 00:15:51 +01:00
|
|
|
|
|
|
|
struct cx8802_driver * cx8802_get_driver(struct cx8802_dev *dev, enum cx88_board_type btype)
|
|
|
|
{
|
V4L/DVB (6334): cx88: Change (struct cx8802_dev)->drvlist to a list_head and fix bugs
It was a struct cx8802_driver for no apparent reason. Nothing uses a
cx8802_driver in the cx8802_dev struct. The only field that was used was
devlist, a list_head.
The code in cx8802_remove() that removed any loaded sub-drivers was broken.
It would delete the current list entry, but didn't use list_for_each_safe. It
also called list_del() on the list _head_ inside the list_for_each loop? It
would crash if it was run, which I don't think can ever happen.
Since the cx8802 sub-drivers use the cx8802 driver, they have to be unloaded
first. So there isn't any way for a sub-driver to still be loaded when
cx8802_remove() is called... Except maybe with PCI hot-plug, if one removes
the PCI card while the drivers are loaded?
So I left some code in to handle that if it's actually possible. It will
remove the sub-drivers from the device cx8802_remove() was called on, and only
that device. If one has two DVB cards and unplugs one, there is no reason to
unload the DVB drivers for both cards. I have no way to test this, but it
can't be worse than what was there before.
cx8802_get_driver() is passed a cx8802_dev pointer and looks for the requested
driver on that device. It first loops over the cx8802 device list looking for
the device it was passed, which is pointless. It doesn't need to find the
device pointer in the list, as it already has the pointer.
The list_head in the cx8802_driver struct, which joins all the _drivers_
attached to a device, was named devlist. Changed that to drvlist, since the
devlist is used for a list of _devices_ in other cx8802 structs.
Signed-off-by: Trent Piepho <xyzzy@speakeasy.org>
Reviewed-by: Michael Krufky <mkrufky@linuxtv.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
2007-10-14 07:52:17 +02:00
|
|
|
struct cx8802_driver *d;
|
2006-12-03 00:15:51 +01:00
|
|
|
|
2007-10-14 07:52:17 +02:00
|
|
|
list_for_each_entry(d, &dev->drvlist, drvlist)
|
V4L/DVB (6334): cx88: Change (struct cx8802_dev)->drvlist to a list_head and fix bugs
It was a struct cx8802_driver for no apparent reason. Nothing uses a
cx8802_driver in the cx8802_dev struct. The only field that was used was
devlist, a list_head.
The code in cx8802_remove() that removed any loaded sub-drivers was broken.
It would delete the current list entry, but didn't use list_for_each_safe. It
also called list_del() on the list _head_ inside the list_for_each loop? It
would crash if it was run, which I don't think can ever happen.
Since the cx8802 sub-drivers use the cx8802 driver, they have to be unloaded
first. So there isn't any way for a sub-driver to still be loaded when
cx8802_remove() is called... Except maybe with PCI hot-plug, if one removes
the PCI card while the drivers are loaded?
So I left some code in to handle that if it's actually possible. It will
remove the sub-drivers from the device cx8802_remove() was called on, and only
that device. If one has two DVB cards and unplugs one, there is no reason to
unload the DVB drivers for both cards. I have no way to test this, but it
can't be worse than what was there before.
cx8802_get_driver() is passed a cx8802_dev pointer and looks for the requested
driver on that device. It first loops over the cx8802 device list looking for
the device it was passed, which is pointless. It doesn't need to find the
device pointer in the list, as it already has the pointer.
The list_head in the cx8802_driver struct, which joins all the _drivers_
attached to a device, was named devlist. Changed that to drvlist, since the
devlist is used for a list of _devices_ in other cx8802 structs.
Signed-off-by: Trent Piepho <xyzzy@speakeasy.org>
Reviewed-by: Michael Krufky <mkrufky@linuxtv.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
2007-10-14 07:52:17 +02:00
|
|
|
if (d->type_id == btype)
|
|
|
|
return d;
|
2006-12-03 00:15:51 +01:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Driver asked for hardware access. */
|
2007-05-01 13:35:10 +02:00
|
|
|
static int cx8802_request_acquire(struct cx8802_driver *drv)
|
2006-12-03 00:15:51 +01:00
|
|
|
{
|
|
|
|
struct cx88_core *core = drv->core;
|
|
|
|
|
|
|
|
/* Fail a request for hardware if the device is busy. */
|
2008-02-11 17:00:34 +01:00
|
|
|
if (core->active_type_id != CX88_BOARD_NONE &&
|
|
|
|
core->active_type_id != drv->type_id)
|
2006-12-03 00:15:51 +01:00
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
if (drv->advise_acquire)
|
|
|
|
{
|
2008-02-11 17:00:34 +01:00
|
|
|
core->active_ref++;
|
|
|
|
mutex_lock(&drv->core->lock);
|
|
|
|
if (core->active_type_id == CX88_BOARD_NONE) {
|
|
|
|
core->active_type_id = drv->type_id;
|
|
|
|
drv->advise_acquire(drv);
|
|
|
|
}
|
|
|
|
mutex_unlock(&drv->core->lock);
|
2006-12-03 00:15:51 +01:00
|
|
|
|
|
|
|
mpeg_dbg(1,"%s() Post acquire GPIO=%x\n", __FUNCTION__, cx_read(MO_GP0_IO));
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Driver asked to release hardware. */
|
2007-05-01 13:35:10 +02:00
|
|
|
static int cx8802_request_release(struct cx8802_driver *drv)
|
2006-12-03 00:15:51 +01:00
|
|
|
{
|
|
|
|
struct cx88_core *core = drv->core;
|
|
|
|
|
2008-02-11 17:00:34 +01:00
|
|
|
if (drv->advise_release && --core->active_ref == 0)
|
2006-12-03 00:15:51 +01:00
|
|
|
{
|
2008-02-11 17:00:34 +01:00
|
|
|
mutex_lock(&drv->core->lock);
|
2006-12-03 00:15:51 +01:00
|
|
|
drv->advise_release(drv);
|
|
|
|
core->active_type_id = CX88_BOARD_NONE;
|
2008-02-11 17:00:34 +01:00
|
|
|
mutex_unlock(&drv->core->lock);
|
2006-12-03 00:15:51 +01:00
|
|
|
mpeg_dbg(1,"%s() Post release GPIO=%x\n", __FUNCTION__, cx_read(MO_GP0_IO));
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cx8802_check_driver(struct cx8802_driver *drv)
|
|
|
|
{
|
|
|
|
if (drv == NULL)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
if ((drv->type_id != CX88_MPEG_DVB) &&
|
|
|
|
(drv->type_id != CX88_MPEG_BLACKBIRD))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if ((drv->hw_access != CX8802_DRVCTL_SHARED) &&
|
|
|
|
(drv->hw_access != CX8802_DRVCTL_EXCLUSIVE))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if ((drv->probe == NULL) ||
|
|
|
|
(drv->remove == NULL) ||
|
|
|
|
(drv->advise_acquire == NULL) ||
|
|
|
|
(drv->advise_release == NULL))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int cx8802_register_driver(struct cx8802_driver *drv)
|
|
|
|
{
|
2007-10-14 07:52:17 +02:00
|
|
|
struct cx8802_dev *dev;
|
2006-12-03 00:15:51 +01:00
|
|
|
struct cx8802_driver *driver;
|
2007-10-14 07:52:17 +02:00
|
|
|
int err, i = 0;
|
2006-12-03 00:15:51 +01:00
|
|
|
|
V4L/DVB (6023): cx88: Clean up some ugly and inconsistent printk()s
Get rid of the "CORE" prefix from cx88 printks. It was only used a few times,
and it makes it look like they're coming from the kernel core or something.
Fix the message, "TV tuner 60 at 0x1fe, Radio tuner -1 at 0x1fe", by adding a
"cx88[0]" prefix to be consistent, and to keep people who grep their dmesg
output for cx88 from missing it. Get rid of the addresses, which are always
wrong. The addresses are always set to -1, but because it's an unsigned 8-bit
value, the left shift converts it to the nonsense address 0x1fe.
In the cx8802 driver, some cut and pasted code prefixed lines with "CORE
cx88[0]:", which has been changed to "cx88[0]/2:" like the other printks from
the cx8802 driver.
Also fix some ugly printks in the cx8802 driver that used __FUNCTION__ for
KERN_INFO and KERN_ERR messages.
The changed printks in cx88-mpeg.c also needed lots of whitespace and
80-column fixes.
A bunch of misc changes in cx88-dvb.c and cx88-video.c to add message levels
or a consistent "cx88[?]/2" or "cx88[?]/0" prefix.
Signed-off-by: Trent Piepho <xyzzy@speakeasy.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
2007-08-15 19:41:59 +02:00
|
|
|
printk(KERN_INFO
|
|
|
|
"cx88/2: registering cx8802 driver, type: %s access: %s\n",
|
|
|
|
drv->type_id == CX88_MPEG_DVB ? "dvb" : "blackbird",
|
|
|
|
drv->hw_access == CX8802_DRVCTL_SHARED ? "shared" : "exclusive");
|
2006-12-03 00:15:51 +01:00
|
|
|
|
|
|
|
if ((err = cx8802_check_driver(drv)) != 0) {
|
V4L/DVB (6023): cx88: Clean up some ugly and inconsistent printk()s
Get rid of the "CORE" prefix from cx88 printks. It was only used a few times,
and it makes it look like they're coming from the kernel core or something.
Fix the message, "TV tuner 60 at 0x1fe, Radio tuner -1 at 0x1fe", by adding a
"cx88[0]" prefix to be consistent, and to keep people who grep their dmesg
output for cx88 from missing it. Get rid of the addresses, which are always
wrong. The addresses are always set to -1, but because it's an unsigned 8-bit
value, the left shift converts it to the nonsense address 0x1fe.
In the cx8802 driver, some cut and pasted code prefixed lines with "CORE
cx88[0]:", which has been changed to "cx88[0]/2:" like the other printks from
the cx8802 driver.
Also fix some ugly printks in the cx8802 driver that used __FUNCTION__ for
KERN_INFO and KERN_ERR messages.
The changed printks in cx88-mpeg.c also needed lots of whitespace and
80-column fixes.
A bunch of misc changes in cx88-dvb.c and cx88-video.c to add message levels
or a consistent "cx88[?]/2" or "cx88[?]/0" prefix.
Signed-off-by: Trent Piepho <xyzzy@speakeasy.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
2007-08-15 19:41:59 +02:00
|
|
|
printk(KERN_ERR "cx88/2: cx8802_driver is invalid\n");
|
2006-12-03 00:15:51 +01:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2007-10-14 07:52:17 +02:00
|
|
|
list_for_each_entry(dev, &cx8802_devlist, devlist) {
|
V4L/DVB (6023): cx88: Clean up some ugly and inconsistent printk()s
Get rid of the "CORE" prefix from cx88 printks. It was only used a few times,
and it makes it look like they're coming from the kernel core or something.
Fix the message, "TV tuner 60 at 0x1fe, Radio tuner -1 at 0x1fe", by adding a
"cx88[0]" prefix to be consistent, and to keep people who grep their dmesg
output for cx88 from missing it. Get rid of the addresses, which are always
wrong. The addresses are always set to -1, but because it's an unsigned 8-bit
value, the left shift converts it to the nonsense address 0x1fe.
In the cx8802 driver, some cut and pasted code prefixed lines with "CORE
cx88[0]:", which has been changed to "cx88[0]/2:" like the other printks from
the cx8802 driver.
Also fix some ugly printks in the cx8802 driver that used __FUNCTION__ for
KERN_INFO and KERN_ERR messages.
The changed printks in cx88-mpeg.c also needed lots of whitespace and
80-column fixes.
A bunch of misc changes in cx88-dvb.c and cx88-video.c to add message levels
or a consistent "cx88[?]/2" or "cx88[?]/0" prefix.
Signed-off-by: Trent Piepho <xyzzy@speakeasy.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
2007-08-15 19:41:59 +02:00
|
|
|
printk(KERN_INFO
|
|
|
|
"%s/2: subsystem: %04x:%04x, board: %s [card=%d]\n",
|
2007-10-14 07:52:17 +02:00
|
|
|
dev->core->name, dev->pci->subsystem_vendor,
|
|
|
|
dev->pci->subsystem_device, dev->core->board.name,
|
|
|
|
dev->core->boardnr);
|
2006-12-03 00:15:51 +01:00
|
|
|
|
|
|
|
/* Bring up a new struct for each driver instance */
|
|
|
|
driver = kzalloc(sizeof(*drv),GFP_KERNEL);
|
|
|
|
if (driver == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
/* Snapshot of the driver registration data */
|
2007-10-14 07:52:17 +02:00
|
|
|
drv->core = dev->core;
|
2006-12-03 00:15:51 +01:00
|
|
|
drv->suspend = cx8802_suspend_common;
|
|
|
|
drv->resume = cx8802_resume_common;
|
|
|
|
drv->request_acquire = cx8802_request_acquire;
|
|
|
|
drv->request_release = cx8802_request_release;
|
|
|
|
memcpy(driver, drv, sizeof(*driver));
|
|
|
|
|
|
|
|
err = drv->probe(driver);
|
|
|
|
if (err == 0) {
|
2006-10-07 02:29:25 +02:00
|
|
|
i++;
|
2006-12-03 00:15:51 +01:00
|
|
|
mutex_lock(&drv->core->lock);
|
2007-10-14 07:52:17 +02:00
|
|
|
list_add_tail(&driver->drvlist, &dev->drvlist);
|
2006-12-03 00:15:51 +01:00
|
|
|
mutex_unlock(&drv->core->lock);
|
|
|
|
} else {
|
V4L/DVB (6023): cx88: Clean up some ugly and inconsistent printk()s
Get rid of the "CORE" prefix from cx88 printks. It was only used a few times,
and it makes it look like they're coming from the kernel core or something.
Fix the message, "TV tuner 60 at 0x1fe, Radio tuner -1 at 0x1fe", by adding a
"cx88[0]" prefix to be consistent, and to keep people who grep their dmesg
output for cx88 from missing it. Get rid of the addresses, which are always
wrong. The addresses are always set to -1, but because it's an unsigned 8-bit
value, the left shift converts it to the nonsense address 0x1fe.
In the cx8802 driver, some cut and pasted code prefixed lines with "CORE
cx88[0]:", which has been changed to "cx88[0]/2:" like the other printks from
the cx8802 driver.
Also fix some ugly printks in the cx8802 driver that used __FUNCTION__ for
KERN_INFO and KERN_ERR messages.
The changed printks in cx88-mpeg.c also needed lots of whitespace and
80-column fixes.
A bunch of misc changes in cx88-dvb.c and cx88-video.c to add message levels
or a consistent "cx88[?]/2" or "cx88[?]/0" prefix.
Signed-off-by: Trent Piepho <xyzzy@speakeasy.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
2007-08-15 19:41:59 +02:00
|
|
|
printk(KERN_ERR
|
|
|
|
"%s/2: cx8802 probe failed, err = %d\n",
|
2007-10-14 07:52:17 +02:00
|
|
|
dev->core->name, err);
|
2006-12-03 00:15:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2007-10-14 07:52:17 +02:00
|
|
|
return i ? 0 : -ENODEV;
|
2006-12-03 00:15:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int cx8802_unregister_driver(struct cx8802_driver *drv)
|
|
|
|
{
|
2007-10-14 07:52:17 +02:00
|
|
|
struct cx8802_dev *dev;
|
|
|
|
struct cx8802_driver *d, *dtmp;
|
|
|
|
int err = 0;
|
2006-12-03 00:15:51 +01:00
|
|
|
|
V4L/DVB (6023): cx88: Clean up some ugly and inconsistent printk()s
Get rid of the "CORE" prefix from cx88 printks. It was only used a few times,
and it makes it look like they're coming from the kernel core or something.
Fix the message, "TV tuner 60 at 0x1fe, Radio tuner -1 at 0x1fe", by adding a
"cx88[0]" prefix to be consistent, and to keep people who grep their dmesg
output for cx88 from missing it. Get rid of the addresses, which are always
wrong. The addresses are always set to -1, but because it's an unsigned 8-bit
value, the left shift converts it to the nonsense address 0x1fe.
In the cx8802 driver, some cut and pasted code prefixed lines with "CORE
cx88[0]:", which has been changed to "cx88[0]/2:" like the other printks from
the cx8802 driver.
Also fix some ugly printks in the cx8802 driver that used __FUNCTION__ for
KERN_INFO and KERN_ERR messages.
The changed printks in cx88-mpeg.c also needed lots of whitespace and
80-column fixes.
A bunch of misc changes in cx88-dvb.c and cx88-video.c to add message levels
or a consistent "cx88[?]/2" or "cx88[?]/0" prefix.
Signed-off-by: Trent Piepho <xyzzy@speakeasy.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
2007-08-15 19:41:59 +02:00
|
|
|
printk(KERN_INFO
|
|
|
|
"cx88/2: unregistering cx8802 driver, type: %s access: %s\n",
|
|
|
|
drv->type_id == CX88_MPEG_DVB ? "dvb" : "blackbird",
|
|
|
|
drv->hw_access == CX8802_DRVCTL_SHARED ? "shared" : "exclusive");
|
2006-12-03 00:15:51 +01:00
|
|
|
|
2007-10-14 07:52:17 +02:00
|
|
|
list_for_each_entry(dev, &cx8802_devlist, devlist) {
|
V4L/DVB (6023): cx88: Clean up some ugly and inconsistent printk()s
Get rid of the "CORE" prefix from cx88 printks. It was only used a few times,
and it makes it look like they're coming from the kernel core or something.
Fix the message, "TV tuner 60 at 0x1fe, Radio tuner -1 at 0x1fe", by adding a
"cx88[0]" prefix to be consistent, and to keep people who grep their dmesg
output for cx88 from missing it. Get rid of the addresses, which are always
wrong. The addresses are always set to -1, but because it's an unsigned 8-bit
value, the left shift converts it to the nonsense address 0x1fe.
In the cx8802 driver, some cut and pasted code prefixed lines with "CORE
cx88[0]:", which has been changed to "cx88[0]/2:" like the other printks from
the cx8802 driver.
Also fix some ugly printks in the cx8802 driver that used __FUNCTION__ for
KERN_INFO and KERN_ERR messages.
The changed printks in cx88-mpeg.c also needed lots of whitespace and
80-column fixes.
A bunch of misc changes in cx88-dvb.c and cx88-video.c to add message levels
or a consistent "cx88[?]/2" or "cx88[?]/0" prefix.
Signed-off-by: Trent Piepho <xyzzy@speakeasy.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
2007-08-15 19:41:59 +02:00
|
|
|
printk(KERN_INFO
|
|
|
|
"%s/2: subsystem: %04x:%04x, board: %s [card=%d]\n",
|
2007-10-14 07:52:17 +02:00
|
|
|
dev->core->name, dev->pci->subsystem_vendor,
|
|
|
|
dev->pci->subsystem_device, dev->core->board.name,
|
|
|
|
dev->core->boardnr);
|
2006-12-03 00:15:51 +01:00
|
|
|
|
2007-10-14 07:52:17 +02:00
|
|
|
list_for_each_entry_safe(d, dtmp, &dev->drvlist, drvlist) {
|
2006-12-03 00:15:51 +01:00
|
|
|
/* only unregister the correct driver type */
|
|
|
|
if (d->type_id != drv->type_id)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
err = d->remove(d);
|
|
|
|
if (err == 0) {
|
|
|
|
mutex_lock(&drv->core->lock);
|
2007-10-14 07:52:17 +02:00
|
|
|
list_del(&d->drvlist);
|
2006-12-03 00:15:51 +01:00
|
|
|
mutex_unlock(&drv->core->lock);
|
2007-10-05 16:28:09 +02:00
|
|
|
kfree(d);
|
2006-12-03 00:15:51 +01:00
|
|
|
} else
|
V4L/DVB (6023): cx88: Clean up some ugly and inconsistent printk()s
Get rid of the "CORE" prefix from cx88 printks. It was only used a few times,
and it makes it look like they're coming from the kernel core or something.
Fix the message, "TV tuner 60 at 0x1fe, Radio tuner -1 at 0x1fe", by adding a
"cx88[0]" prefix to be consistent, and to keep people who grep their dmesg
output for cx88 from missing it. Get rid of the addresses, which are always
wrong. The addresses are always set to -1, but because it's an unsigned 8-bit
value, the left shift converts it to the nonsense address 0x1fe.
In the cx8802 driver, some cut and pasted code prefixed lines with "CORE
cx88[0]:", which has been changed to "cx88[0]/2:" like the other printks from
the cx8802 driver.
Also fix some ugly printks in the cx8802 driver that used __FUNCTION__ for
KERN_INFO and KERN_ERR messages.
The changed printks in cx88-mpeg.c also needed lots of whitespace and
80-column fixes.
A bunch of misc changes in cx88-dvb.c and cx88-video.c to add message levels
or a consistent "cx88[?]/2" or "cx88[?]/0" prefix.
Signed-off-by: Trent Piepho <xyzzy@speakeasy.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
2007-08-15 19:41:59 +02:00
|
|
|
printk(KERN_ERR "%s/2: cx8802 driver remove "
|
2007-10-14 07:52:17 +02:00
|
|
|
"failed (%d)\n", dev->core->name, err);
|
2006-12-03 00:15:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2005-04-17 00:20:36 +02:00
|
|
|
/* ----------------------------------------------------------- */
|
2006-12-03 00:15:51 +01:00
|
|
|
static int __devinit cx8802_probe(struct pci_dev *pci_dev,
|
|
|
|
const struct pci_device_id *pci_id)
|
|
|
|
{
|
|
|
|
struct cx8802_dev *dev;
|
|
|
|
struct cx88_core *core;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
/* general setup */
|
|
|
|
core = cx88_core_get(pci_dev);
|
|
|
|
if (NULL == core)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
printk("%s/2: cx2388x 8802 Driver Manager\n", core->name);
|
|
|
|
|
|
|
|
err = -ENODEV;
|
2007-08-15 19:41:57 +02:00
|
|
|
if (!core->board.mpeg)
|
2006-12-03 00:15:51 +01:00
|
|
|
goto fail_core;
|
|
|
|
|
|
|
|
err = -ENOMEM;
|
|
|
|
dev = kzalloc(sizeof(*dev),GFP_KERNEL);
|
|
|
|
if (NULL == dev)
|
|
|
|
goto fail_core;
|
|
|
|
dev->pci = pci_dev;
|
|
|
|
dev->core = core;
|
|
|
|
|
|
|
|
err = cx8802_init_common(dev);
|
|
|
|
if (err != 0)
|
|
|
|
goto fail_free;
|
|
|
|
|
V4L/DVB (6334): cx88: Change (struct cx8802_dev)->drvlist to a list_head and fix bugs
It was a struct cx8802_driver for no apparent reason. Nothing uses a
cx8802_driver in the cx8802_dev struct. The only field that was used was
devlist, a list_head.
The code in cx8802_remove() that removed any loaded sub-drivers was broken.
It would delete the current list entry, but didn't use list_for_each_safe. It
also called list_del() on the list _head_ inside the list_for_each loop? It
would crash if it was run, which I don't think can ever happen.
Since the cx8802 sub-drivers use the cx8802 driver, they have to be unloaded
first. So there isn't any way for a sub-driver to still be loaded when
cx8802_remove() is called... Except maybe with PCI hot-plug, if one removes
the PCI card while the drivers are loaded?
So I left some code in to handle that if it's actually possible. It will
remove the sub-drivers from the device cx8802_remove() was called on, and only
that device. If one has two DVB cards and unplugs one, there is no reason to
unload the DVB drivers for both cards. I have no way to test this, but it
can't be worse than what was there before.
cx8802_get_driver() is passed a cx8802_dev pointer and looks for the requested
driver on that device. It first loops over the cx8802 device list looking for
the device it was passed, which is pointless. It doesn't need to find the
device pointer in the list, as it already has the pointer.
The list_head in the cx8802_driver struct, which joins all the _drivers_
attached to a device, was named devlist. Changed that to drvlist, since the
devlist is used for a list of _devices_ in other cx8802 structs.
Signed-off-by: Trent Piepho <xyzzy@speakeasy.org>
Reviewed-by: Michael Krufky <mkrufky@linuxtv.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
2007-10-14 07:52:17 +02:00
|
|
|
INIT_LIST_HEAD(&dev->drvlist);
|
2006-12-03 00:15:51 +01:00
|
|
|
list_add_tail(&dev->devlist,&cx8802_devlist);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2006-12-03 00:15:51 +01:00
|
|
|
/* Maintain a reference so cx88-video can query the 8802 device. */
|
|
|
|
core->dvbdev = dev;
|
2007-04-27 17:31:06 +02:00
|
|
|
|
|
|
|
/* now autoload cx88-dvb or cx88-blackbird */
|
|
|
|
request_modules(dev);
|
2006-12-03 00:15:51 +01:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
fail_free:
|
|
|
|
kfree(dev);
|
|
|
|
fail_core:
|
|
|
|
cx88_core_put(core,pci_dev);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __devexit cx8802_remove(struct pci_dev *pci_dev)
|
|
|
|
{
|
|
|
|
struct cx8802_dev *dev;
|
|
|
|
|
|
|
|
dev = pci_get_drvdata(pci_dev);
|
|
|
|
|
|
|
|
dprintk( 1, "%s\n", __FUNCTION__);
|
|
|
|
|
V4L/DVB (6334): cx88: Change (struct cx8802_dev)->drvlist to a list_head and fix bugs
It was a struct cx8802_driver for no apparent reason. Nothing uses a
cx8802_driver in the cx8802_dev struct. The only field that was used was
devlist, a list_head.
The code in cx8802_remove() that removed any loaded sub-drivers was broken.
It would delete the current list entry, but didn't use list_for_each_safe. It
also called list_del() on the list _head_ inside the list_for_each loop? It
would crash if it was run, which I don't think can ever happen.
Since the cx8802 sub-drivers use the cx8802 driver, they have to be unloaded
first. So there isn't any way for a sub-driver to still be loaded when
cx8802_remove() is called... Except maybe with PCI hot-plug, if one removes
the PCI card while the drivers are loaded?
So I left some code in to handle that if it's actually possible. It will
remove the sub-drivers from the device cx8802_remove() was called on, and only
that device. If one has two DVB cards and unplugs one, there is no reason to
unload the DVB drivers for both cards. I have no way to test this, but it
can't be worse than what was there before.
cx8802_get_driver() is passed a cx8802_dev pointer and looks for the requested
driver on that device. It first loops over the cx8802 device list looking for
the device it was passed, which is pointless. It doesn't need to find the
device pointer in the list, as it already has the pointer.
The list_head in the cx8802_driver struct, which joins all the _drivers_
attached to a device, was named devlist. Changed that to drvlist, since the
devlist is used for a list of _devices_ in other cx8802 structs.
Signed-off-by: Trent Piepho <xyzzy@speakeasy.org>
Reviewed-by: Michael Krufky <mkrufky@linuxtv.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
2007-10-14 07:52:17 +02:00
|
|
|
if (!list_empty(&dev->drvlist)) {
|
2007-10-14 07:52:17 +02:00
|
|
|
struct cx8802_driver *drv, *tmp;
|
V4L/DVB (6334): cx88: Change (struct cx8802_dev)->drvlist to a list_head and fix bugs
It was a struct cx8802_driver for no apparent reason. Nothing uses a
cx8802_driver in the cx8802_dev struct. The only field that was used was
devlist, a list_head.
The code in cx8802_remove() that removed any loaded sub-drivers was broken.
It would delete the current list entry, but didn't use list_for_each_safe. It
also called list_del() on the list _head_ inside the list_for_each loop? It
would crash if it was run, which I don't think can ever happen.
Since the cx8802 sub-drivers use the cx8802 driver, they have to be unloaded
first. So there isn't any way for a sub-driver to still be loaded when
cx8802_remove() is called... Except maybe with PCI hot-plug, if one removes
the PCI card while the drivers are loaded?
So I left some code in to handle that if it's actually possible. It will
remove the sub-drivers from the device cx8802_remove() was called on, and only
that device. If one has two DVB cards and unplugs one, there is no reason to
unload the DVB drivers for both cards. I have no way to test this, but it
can't be worse than what was there before.
cx8802_get_driver() is passed a cx8802_dev pointer and looks for the requested
driver on that device. It first loops over the cx8802 device list looking for
the device it was passed, which is pointless. It doesn't need to find the
device pointer in the list, as it already has the pointer.
The list_head in the cx8802_driver struct, which joins all the _drivers_
attached to a device, was named devlist. Changed that to drvlist, since the
devlist is used for a list of _devices_ in other cx8802 structs.
Signed-off-by: Trent Piepho <xyzzy@speakeasy.org>
Reviewed-by: Michael Krufky <mkrufky@linuxtv.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
2007-10-14 07:52:17 +02:00
|
|
|
int err;
|
|
|
|
|
|
|
|
printk(KERN_WARNING "%s/2: Trying to remove cx8802 driver "
|
|
|
|
"while cx8802 sub-drivers still loaded?!\n",
|
|
|
|
dev->core->name);
|
|
|
|
|
2007-10-14 07:52:17 +02:00
|
|
|
list_for_each_entry_safe(drv, tmp, &dev->drvlist, drvlist) {
|
V4L/DVB (6334): cx88: Change (struct cx8802_dev)->drvlist to a list_head and fix bugs
It was a struct cx8802_driver for no apparent reason. Nothing uses a
cx8802_driver in the cx8802_dev struct. The only field that was used was
devlist, a list_head.
The code in cx8802_remove() that removed any loaded sub-drivers was broken.
It would delete the current list entry, but didn't use list_for_each_safe. It
also called list_del() on the list _head_ inside the list_for_each loop? It
would crash if it was run, which I don't think can ever happen.
Since the cx8802 sub-drivers use the cx8802 driver, they have to be unloaded
first. So there isn't any way for a sub-driver to still be loaded when
cx8802_remove() is called... Except maybe with PCI hot-plug, if one removes
the PCI card while the drivers are loaded?
So I left some code in to handle that if it's actually possible. It will
remove the sub-drivers from the device cx8802_remove() was called on, and only
that device. If one has two DVB cards and unplugs one, there is no reason to
unload the DVB drivers for both cards. I have no way to test this, but it
can't be worse than what was there before.
cx8802_get_driver() is passed a cx8802_dev pointer and looks for the requested
driver on that device. It first loops over the cx8802 device list looking for
the device it was passed, which is pointless. It doesn't need to find the
device pointer in the list, as it already has the pointer.
The list_head in the cx8802_driver struct, which joins all the _drivers_
attached to a device, was named devlist. Changed that to drvlist, since the
devlist is used for a list of _devices_ in other cx8802 structs.
Signed-off-by: Trent Piepho <xyzzy@speakeasy.org>
Reviewed-by: Michael Krufky <mkrufky@linuxtv.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
2007-10-14 07:52:17 +02:00
|
|
|
err = drv->remove(drv);
|
|
|
|
if (err == 0) {
|
|
|
|
mutex_lock(&drv->core->lock);
|
2007-10-14 07:52:17 +02:00
|
|
|
list_del(&drv->drvlist);
|
V4L/DVB (6334): cx88: Change (struct cx8802_dev)->drvlist to a list_head and fix bugs
It was a struct cx8802_driver for no apparent reason. Nothing uses a
cx8802_driver in the cx8802_dev struct. The only field that was used was
devlist, a list_head.
The code in cx8802_remove() that removed any loaded sub-drivers was broken.
It would delete the current list entry, but didn't use list_for_each_safe. It
also called list_del() on the list _head_ inside the list_for_each loop? It
would crash if it was run, which I don't think can ever happen.
Since the cx8802 sub-drivers use the cx8802 driver, they have to be unloaded
first. So there isn't any way for a sub-driver to still be loaded when
cx8802_remove() is called... Except maybe with PCI hot-plug, if one removes
the PCI card while the drivers are loaded?
So I left some code in to handle that if it's actually possible. It will
remove the sub-drivers from the device cx8802_remove() was called on, and only
that device. If one has two DVB cards and unplugs one, there is no reason to
unload the DVB drivers for both cards. I have no way to test this, but it
can't be worse than what was there before.
cx8802_get_driver() is passed a cx8802_dev pointer and looks for the requested
driver on that device. It first loops over the cx8802 device list looking for
the device it was passed, which is pointless. It doesn't need to find the
device pointer in the list, as it already has the pointer.
The list_head in the cx8802_driver struct, which joins all the _drivers_
attached to a device, was named devlist. Changed that to drvlist, since the
devlist is used for a list of _devices_ in other cx8802 structs.
Signed-off-by: Trent Piepho <xyzzy@speakeasy.org>
Reviewed-by: Michael Krufky <mkrufky@linuxtv.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
2007-10-14 07:52:17 +02:00
|
|
|
mutex_unlock(&drv->core->lock);
|
|
|
|
} else
|
|
|
|
printk(KERN_ERR "%s/2: cx8802 driver remove "
|
|
|
|
"failed (%d)\n", dev->core->name, err);
|
2007-10-05 16:28:09 +02:00
|
|
|
kfree(drv);
|
2006-12-03 00:15:51 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Destroy any 8802 reference. */
|
|
|
|
dev->core->dvbdev = NULL;
|
|
|
|
|
|
|
|
/* common */
|
|
|
|
cx8802_fini_common(dev);
|
|
|
|
cx88_core_put(dev->core,dev->pci);
|
|
|
|
kfree(dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct pci_device_id cx8802_pci_tbl[] = {
|
|
|
|
{
|
|
|
|
.vendor = 0x14f1,
|
|
|
|
.device = 0x8802,
|
|
|
|
.subvendor = PCI_ANY_ID,
|
|
|
|
.subdevice = PCI_ANY_ID,
|
|
|
|
},{
|
|
|
|
/* --- end of list --- */
|
|
|
|
}
|
|
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(pci, cx8802_pci_tbl);
|
|
|
|
|
|
|
|
static struct pci_driver cx8802_pci_driver = {
|
|
|
|
.name = "cx88-mpeg driver manager",
|
|
|
|
.id_table = cx8802_pci_tbl,
|
|
|
|
.probe = cx8802_probe,
|
|
|
|
.remove = __devexit_p(cx8802_remove),
|
|
|
|
};
|
|
|
|
|
|
|
|
static int cx8802_init(void)
|
|
|
|
{
|
V4L/DVB (6023): cx88: Clean up some ugly and inconsistent printk()s
Get rid of the "CORE" prefix from cx88 printks. It was only used a few times,
and it makes it look like they're coming from the kernel core or something.
Fix the message, "TV tuner 60 at 0x1fe, Radio tuner -1 at 0x1fe", by adding a
"cx88[0]" prefix to be consistent, and to keep people who grep their dmesg
output for cx88 from missing it. Get rid of the addresses, which are always
wrong. The addresses are always set to -1, but because it's an unsigned 8-bit
value, the left shift converts it to the nonsense address 0x1fe.
In the cx8802 driver, some cut and pasted code prefixed lines with "CORE
cx88[0]:", which has been changed to "cx88[0]/2:" like the other printks from
the cx8802 driver.
Also fix some ugly printks in the cx8802 driver that used __FUNCTION__ for
KERN_INFO and KERN_ERR messages.
The changed printks in cx88-mpeg.c also needed lots of whitespace and
80-column fixes.
A bunch of misc changes in cx88-dvb.c and cx88-video.c to add message levels
or a consistent "cx88[?]/2" or "cx88[?]/0" prefix.
Signed-off-by: Trent Piepho <xyzzy@speakeasy.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
2007-08-15 19:41:59 +02:00
|
|
|
printk(KERN_INFO "cx88/2: cx2388x MPEG-TS Driver Manager version %d.%d.%d loaded\n",
|
2006-12-03 00:15:51 +01:00
|
|
|
(CX88_VERSION_CODE >> 16) & 0xff,
|
|
|
|
(CX88_VERSION_CODE >> 8) & 0xff,
|
|
|
|
CX88_VERSION_CODE & 0xff);
|
|
|
|
#ifdef SNAPSHOT
|
|
|
|
printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n",
|
|
|
|
SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100);
|
|
|
|
#endif
|
|
|
|
return pci_register_driver(&cx8802_pci_driver);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void cx8802_fini(void)
|
|
|
|
{
|
|
|
|
pci_unregister_driver(&cx8802_pci_driver);
|
|
|
|
}
|
|
|
|
|
|
|
|
module_init(cx8802_init);
|
|
|
|
module_exit(cx8802_fini);
|
2005-04-17 00:20:36 +02:00
|
|
|
EXPORT_SYMBOL(cx8802_buf_prepare);
|
|
|
|
EXPORT_SYMBOL(cx8802_buf_queue);
|
|
|
|
EXPORT_SYMBOL(cx8802_cancel_buffers);
|
|
|
|
|
2006-12-03 00:15:51 +01:00
|
|
|
EXPORT_SYMBOL(cx8802_register_driver);
|
|
|
|
EXPORT_SYMBOL(cx8802_unregister_driver);
|
|
|
|
EXPORT_SYMBOL(cx8802_get_driver);
|
2005-04-17 00:20:36 +02:00
|
|
|
/* ----------------------------------------------------------- */
|
|
|
|
/*
|
|
|
|
* Local variables:
|
|
|
|
* c-basic-offset: 8
|
|
|
|
* End:
|
2005-06-24 07:05:03 +02:00
|
|
|
* kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off
|
2005-04-17 00:20:36 +02:00
|
|
|
*/
|