[media] au0828: reset streaming when a new frequency is set
authorMauro Carvalho Chehab <m.chehab@samsung.com>
Fri, 9 May 2014 09:17:55 +0000 (06:17 -0300)
committerMauro Carvalho Chehab <m.chehab@samsung.com>
Sun, 25 May 2014 20:50:02 +0000 (17:50 -0300)
As reported by Trevor, doing several opening/streaming/closing
operations to the demux causes it to fail.

I was able to simulate this bug too. I also noticed that,
sometimes, changing channels with au0828, the same thing
happens.

Most of the issues seem to be due to some hardware bug, that
causes the device to not fill all the URBs allocated. When
the bug happens, the only known fix is to either replug the
device, or to send an USB reset to it.

There's also a hack a the au0828 driver that starts a thread
that tries to reset the device when a package doesn't start
with a sync.

One of the culpits for this bad hardware behavior seem to be
caused by the lack of stopping and restarting the stream every
time a new channel is set.

This patch warrants that the stream will be properly reset
every time the set_frontend callback is called, partially
solving the problem.

A complete fix, however, would also need to check the PM
conditions for the tuner and demux.

Reported-by: Trevor Graffa <tlgraffa@gmail.com>
Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
drivers/media/usb/au0828/au0828-dvb.c
drivers/media/usb/au0828/au0828.h

index ab5f93643021c27fd6a57475ca3fe97e664b3cd3..d8b5d9480279a29441f0cadb47a4e36c59936855 100755 (executable)
@@ -256,8 +256,6 @@ static void au0828_stop_transport(struct au0828_dev *dev, int full_stop)
        au0828_write(dev, 0x60b, 0x00);
 }
 
-
-
 static int au0828_dvb_start_feed(struct dvb_demux_feed *feed)
 {
        struct dvb_demux *demux = feed->demux;
@@ -300,6 +298,8 @@ static int au0828_dvb_stop_feed(struct dvb_demux_feed *feed)
        dprintk(1, "%s()\n", __func__);
 
        if (dvb) {
+               cancel_work_sync(&dev->restart_streaming);
+
                mutex_lock(&dvb->lock);
                dvb->stop_count++;
                dprintk(1, "%s(), start_count: %d, stop_count: %d\n", __func__,
@@ -342,6 +342,41 @@ static void au0828_restart_dvb_streaming(struct work_struct *work)
        mutex_unlock(&dvb->lock);
 }
 
+static int au0828_set_frontend(struct dvb_frontend *fe)
+{
+       struct au0828_dev *dev = fe->dvb->priv;
+       struct au0828_dvb *dvb = &dev->dvb;
+       int ret, was_streaming;
+
+       mutex_lock(&dvb->lock);
+       was_streaming = dev->urb_streaming;
+       if (was_streaming) {
+               au0828_stop_transport(dev, 1);
+
+               /*
+                * We can't hold a mutex here, as the restart_streaming
+                * kthread may also hold it.
+                */
+               mutex_unlock(&dvb->lock);
+               cancel_work_sync(&dev->restart_streaming);
+               mutex_lock(&dvb->lock);
+
+               stop_urb_transfer(dev);
+       }
+       mutex_unlock(&dvb->lock);
+
+       ret = dvb->set_frontend(fe);
+
+       if (was_streaming) {
+               mutex_lock(&dvb->lock);
+               au0828_start_transport(dev);
+               start_urb_transfer(dev);
+               mutex_unlock(&dvb->lock);
+       }
+
+       return ret;
+}
+
 static int dvb_register(struct au0828_dev *dev)
 {
        struct au0828_dvb *dvb = &dev->dvb;
@@ -386,6 +421,10 @@ static int dvb_register(struct au0828_dev *dev)
                goto fail_frontend;
        }
 
+       /* Hook dvb frontend */
+       dvb->set_frontend = dvb->frontend->ops.set_frontend;
+       dvb->frontend->ops.set_frontend = au0828_set_frontend;
+
        /* register demux stuff */
        dvb->demux.dmx.capabilities =
                DMX_TS_FILTERING | DMX_SECTION_FILTERING |
index 5439772c15517b3594d0c64a7d3d81a9b62b7fa0..7112b9d956fa5a1b94b0df51c39f28adb9a6ffed 100644 (file)
@@ -104,6 +104,8 @@ struct au0828_dvb {
        int feeding;
        int start_count;
        int stop_count;
+
+       int (*set_frontend)(struct dvb_frontend *fe);
 };
 
 enum au0828_stream_state {