Commit 43f2cccf authored by Devin Heitmueller's avatar Devin Heitmueller Committed by Mauro Carvalho Chehab

[media] au8522: properly recover from the au8522 delivering misaligned TS streams

There is an apparent bug in the au8522 TS clocking which can result in
it delivering a TS payload to the au0828 that is shifted by some number
of bits. For example, the device will announce a packet containing "FA
38 FF F8" which if you shift left one bit is "1F 47 1F FF F0..."

This presents itself as no TS stream being delivered from the kernel to
userland, since the kernel demux will drop every packet.

In the event that this condition occurs, restart the DVB stream.

Also, this patch includes a couple of lines of cleanup to not change the
FIFO configuration while the FIFO is running (which can screw up the
state machine), and dequeue the buffers before turning off the FIFO.
This puts the logic in sync with the Windows driver.
Signed-off-by: default avatarDevin Heitmueller <dheitmueller@kernellabs.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent b0c4878e
...@@ -101,11 +101,14 @@ static struct tda18271_config hauppauge_woodbury_tunerconfig = { ...@@ -101,11 +101,14 @@ static struct tda18271_config hauppauge_woodbury_tunerconfig = {
.gate = TDA18271_GATE_DIGITAL, .gate = TDA18271_GATE_DIGITAL,
}; };
static void au0828_restart_dvb_streaming(struct work_struct *work);
/*-------------------------------------------------------------------*/ /*-------------------------------------------------------------------*/
static void urb_completion(struct urb *purb) static void urb_completion(struct urb *purb)
{ {
struct au0828_dev *dev = purb->context; struct au0828_dev *dev = purb->context;
int ptype = usb_pipetype(purb->pipe); int ptype = usb_pipetype(purb->pipe);
unsigned char *ptr;
dprintk(2, "%s()\n", __func__); dprintk(2, "%s()\n", __func__);
...@@ -121,6 +124,16 @@ static void urb_completion(struct urb *purb) ...@@ -121,6 +124,16 @@ static void urb_completion(struct urb *purb)
return; return;
} }
/* See if the stream is corrupted (to work around a hardware
bug where the stream gets misaligned */
ptr = purb->transfer_buffer;
if (purb->actual_length > 0 && ptr[0] != 0x47) {
dprintk(1, "Need to restart streaming %02x len=%d!\n",
ptr[0], purb->actual_length);
schedule_work(&dev->restart_streaming);
return;
}
/* Feed the transport payload into the kernel demux */ /* Feed the transport payload into the kernel demux */
dvb_dmx_swfilter_packets(&dev->dvb.demux, dvb_dmx_swfilter_packets(&dev->dvb.demux,
purb->transfer_buffer, purb->actual_length / 188); purb->transfer_buffer, purb->actual_length / 188);
...@@ -138,14 +151,13 @@ static int stop_urb_transfer(struct au0828_dev *dev) ...@@ -138,14 +151,13 @@ static int stop_urb_transfer(struct au0828_dev *dev)
dprintk(2, "%s()\n", __func__); dprintk(2, "%s()\n", __func__);
dev->urb_streaming = 0;
for (i = 0; i < URB_COUNT; i++) { for (i = 0; i < URB_COUNT; i++) {
usb_kill_urb(dev->urbs[i]); usb_kill_urb(dev->urbs[i]);
kfree(dev->urbs[i]->transfer_buffer); kfree(dev->urbs[i]->transfer_buffer);
usb_free_urb(dev->urbs[i]); usb_free_urb(dev->urbs[i]);
} }
dev->urb_streaming = 0;
return 0; return 0;
} }
...@@ -246,11 +258,8 @@ static int au0828_dvb_stop_feed(struct dvb_demux_feed *feed) ...@@ -246,11 +258,8 @@ static int au0828_dvb_stop_feed(struct dvb_demux_feed *feed)
mutex_lock(&dvb->lock); mutex_lock(&dvb->lock);
if (--dvb->feeding == 0) { if (--dvb->feeding == 0) {
/* Stop transport */ /* Stop transport */
au0828_write(dev, 0x608, 0x00);
au0828_write(dev, 0x609, 0x00);
au0828_write(dev, 0x60a, 0x00);
au0828_write(dev, 0x60b, 0x00);
ret = stop_urb_transfer(dev); ret = stop_urb_transfer(dev);
au0828_write(dev, 0x60b, 0x00);
} }
mutex_unlock(&dvb->lock); mutex_unlock(&dvb->lock);
} }
...@@ -258,6 +267,37 @@ static int au0828_dvb_stop_feed(struct dvb_demux_feed *feed) ...@@ -258,6 +267,37 @@ static int au0828_dvb_stop_feed(struct dvb_demux_feed *feed)
return ret; return ret;
} }
static void au0828_restart_dvb_streaming(struct work_struct *work)
{
struct au0828_dev *dev = container_of(work, struct au0828_dev,
restart_streaming);
struct au0828_dvb *dvb = &dev->dvb;
int ret;
if (dev->urb_streaming == 0)
return;
dprintk(1, "Restarting streaming...!\n");
mutex_lock(&dvb->lock);
/* Stop transport */
ret = stop_urb_transfer(dev);
au0828_write(dev, 0x608, 0x00);
au0828_write(dev, 0x609, 0x00);
au0828_write(dev, 0x60a, 0x00);
au0828_write(dev, 0x60b, 0x00);
/* Start transport */
au0828_write(dev, 0x608, 0x90);
au0828_write(dev, 0x609, 0x72);
au0828_write(dev, 0x60a, 0x71);
au0828_write(dev, 0x60b, 0x01);
ret = start_urb_transfer(dev);
mutex_unlock(&dvb->lock);
}
static int dvb_register(struct au0828_dev *dev) static int dvb_register(struct au0828_dev *dev)
{ {
struct au0828_dvb *dvb = &dev->dvb; struct au0828_dvb *dvb = &dev->dvb;
...@@ -265,6 +305,8 @@ static int dvb_register(struct au0828_dev *dev) ...@@ -265,6 +305,8 @@ static int dvb_register(struct au0828_dev *dev)
dprintk(1, "%s()\n", __func__); dprintk(1, "%s()\n", __func__);
INIT_WORK(&dev->restart_streaming, au0828_restart_dvb_streaming);
/* register adapter */ /* register adapter */
result = dvb_register_adapter(&dvb->adapter, DRIVER_NAME, THIS_MODULE, result = dvb_register_adapter(&dvb->adapter, DRIVER_NAME, THIS_MODULE,
&dev->usbdev->dev, adapter_nr); &dev->usbdev->dev, adapter_nr);
......
...@@ -197,6 +197,7 @@ struct au0828_dev { ...@@ -197,6 +197,7 @@ struct au0828_dev {
/* Digital */ /* Digital */
struct au0828_dvb dvb; struct au0828_dvb dvb;
struct work_struct restart_streaming;
/* Analog */ /* Analog */
struct v4l2_device v4l2_dev; struct v4l2_device v4l2_dev;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment