--- zzzz-none-000/linux-3.10.107/drivers/media/usb/au0828/au0828-dvb.c 2017-06-27 09:49:32.000000000 +0000 +++ scorpion-7490-727/linux-3.10.107/drivers/media/usb/au0828/au0828-dvb.c 2021-02-04 17:41:59.000000000 +0000 @@ -19,20 +19,24 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include "au0828.h" + #include #include #include #include -#include #include #include -#include "au0828.h" #include "au8522.h" #include "xc5000.h" #include "mxl5007t.h" #include "tda18271.h" +static int preallocate_big_buffers; +module_param_named(preallocate_big_buffers, preallocate_big_buffers, int, 0644); +MODULE_PARM_DESC(preallocate_big_buffers, "Preallocate the larger transfer buffers at module load time"); + DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); #define _AU0828_BULKPIPE 0x83 @@ -84,12 +88,14 @@ .i2c_address = 0x61, .if_khz = 6000, .chip_id = XC5000A, + .output_amp = 0x8f, }; static struct xc5000_config hauppauge_xc5000c_config = { .i2c_address = 0x61, .if_khz = 6000, .chip_id = XC5000C, + .output_amp = 0x8f, }; static struct mxl5007t_config mxl5007t_hvr950q_config = { @@ -110,16 +116,20 @@ int ptype = usb_pipetype(purb->pipe); unsigned char *ptr; - dprintk(2, "%s()\n", __func__); + dprintk(2, "%s: %d\n", __func__, purb->actual_length); - if (!dev) + if (!dev) { + dprintk(2, "%s: no dev!\n", __func__); return; + } - if (dev->urb_streaming == 0) + if (!dev->urb_streaming) { + dprintk(2, "%s: not streaming!\n", __func__); return; + } if (ptype != PIPE_BULK) { - printk(KERN_ERR "%s() Unsupported URB type %d\n", + pr_err("%s: Unsupported URB type %d\n", __func__, ptype); return; } @@ -151,11 +161,18 @@ dprintk(2, "%s()\n", __func__); - dev->urb_streaming = 0; + if (!dev->urb_streaming) + return 0; + + dev->urb_streaming = false; for (i = 0; i < URB_COUNT; i++) { - usb_kill_urb(dev->urbs[i]); - kfree(dev->urbs[i]->transfer_buffer); - usb_free_urb(dev->urbs[i]); + if (dev->urbs[i]) { + usb_kill_urb(dev->urbs[i]); + if (!preallocate_big_buffers) + kfree(dev->urbs[i]->transfer_buffer); + + usb_free_urb(dev->urbs[i]); + } } return 0; @@ -181,10 +198,17 @@ purb = dev->urbs[i]; - purb->transfer_buffer = kzalloc(URB_BUFSIZE, GFP_KERNEL); + if (preallocate_big_buffers) + purb->transfer_buffer = dev->dig_transfer_buffer[i]; + else + purb->transfer_buffer = kzalloc(URB_BUFSIZE, + GFP_KERNEL); + if (!purb->transfer_buffer) { usb_free_urb(purb); dev->urbs[i] = NULL; + pr_err("%s: failed big buffer allocation, err = %d\n", + __func__, ret); goto err; } @@ -204,19 +228,38 @@ ret = usb_submit_urb(dev->urbs[i], GFP_ATOMIC); if (ret != 0) { stop_urb_transfer(dev); - printk(KERN_ERR "%s: failed urb submission, " - "err = %d\n", __func__, ret); + pr_err("%s: failed urb submission, err = %d\n", + __func__, ret); return ret; } } - dev->urb_streaming = 1; + dev->urb_streaming = true; ret = 0; err: return ret; } +static void au0828_start_transport(struct au0828_dev *dev) +{ + au0828_write(dev, 0x608, 0x90); + au0828_write(dev, 0x609, 0x72); + au0828_write(dev, 0x60a, 0x71); + au0828_write(dev, 0x60b, 0x01); + +} + +static void au0828_stop_transport(struct au0828_dev *dev, int full_stop) +{ + if (full_stop) { + au0828_write(dev, 0x608, 0x00); + au0828_write(dev, 0x609, 0x00); + au0828_write(dev, 0x60a, 0x00); + } + au0828_write(dev, 0x60b, 0x00); +} + static int au0828_dvb_start_feed(struct dvb_demux_feed *feed) { struct dvb_demux *demux = feed->demux; @@ -229,15 +272,19 @@ if (!demux->dmx.frontend) return -EINVAL; - if (dvb) { + if (dvb->frontend) { mutex_lock(&dvb->lock); + dvb->start_count++; + dprintk(1, "%s(), start_count: %d, stop_count: %d\n", __func__, + dvb->start_count, dvb->stop_count); if (dvb->feeding++ == 0) { /* Start transport */ - au0828_write(dev, 0x608, 0x90); - au0828_write(dev, 0x609, 0x72); - au0828_write(dev, 0x60a, 0x71); - au0828_write(dev, 0x60b, 0x01); + au0828_start_transport(dev); ret = start_urb_transfer(dev); + if (ret < 0) { + au0828_stop_transport(dev, 0); + dvb->feeding--; /* We ran out of memory... */ + } } mutex_unlock(&dvb->lock); } @@ -254,12 +301,20 @@ dprintk(1, "%s()\n", __func__); - if (dvb) { + if (dvb->frontend) { + cancel_work_sync(&dev->restart_streaming); + mutex_lock(&dvb->lock); - if (--dvb->feeding == 0) { - /* Stop transport */ - ret = stop_urb_transfer(dev); - au0828_write(dev, 0x60b, 0x00); + dvb->stop_count++; + dprintk(1, "%s(), start_count: %d, stop_count: %d\n", __func__, + dvb->start_count, dvb->stop_count); + if (dvb->feeding > 0) { + dvb->feeding--; + if (dvb->feeding == 0) { + /* Stop transport */ + ret = stop_urb_transfer(dev); + au0828_stop_transport(dev, 0); + } } mutex_unlock(&dvb->lock); } @@ -273,7 +328,7 @@ restart_streaming); struct au0828_dvb *dvb = &dev->dvb; - if (dev->urb_streaming == 0) + if (!dev->urb_streaming) return; dprintk(1, "Restarting streaming...!\n"); @@ -282,21 +337,50 @@ /* Stop transport */ stop_urb_transfer(dev); - au0828_write(dev, 0x608, 0x00); - au0828_write(dev, 0x609, 0x00); - au0828_write(dev, 0x60a, 0x00); - au0828_write(dev, 0x60b, 0x00); + au0828_stop_transport(dev, 1); /* Start transport */ - au0828_write(dev, 0x608, 0x90); - au0828_write(dev, 0x609, 0x72); - au0828_write(dev, 0x60a, 0x71); - au0828_write(dev, 0x60b, 0x01); + au0828_start_transport(dev); start_urb_transfer(dev); 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; @@ -304,14 +388,31 @@ dprintk(1, "%s()\n", __func__); + if (preallocate_big_buffers) { + int i; + for (i = 0; i < URB_COUNT; i++) { + dev->dig_transfer_buffer[i] = kzalloc(URB_BUFSIZE, + GFP_KERNEL); + + if (!dev->dig_transfer_buffer[i]) { + result = -ENOMEM; + + pr_err("failed buffer allocation (errno = %d)\n", + result); + goto fail_adapter; + } + } + } + INIT_WORK(&dev->restart_streaming, au0828_restart_dvb_streaming); /* register adapter */ - result = dvb_register_adapter(&dvb->adapter, DRIVER_NAME, THIS_MODULE, + result = dvb_register_adapter(&dvb->adapter, + KBUILD_MODNAME, THIS_MODULE, &dev->usbdev->dev, adapter_nr); if (result < 0) { - printk(KERN_ERR "%s: dvb_register_adapter failed " - "(errno = %d)\n", DRIVER_NAME, result); + pr_err("dvb_register_adapter failed (errno = %d)\n", + result); goto fail_adapter; } dvb->adapter.priv = dev; @@ -319,11 +420,15 @@ /* register frontend */ result = dvb_register_frontend(&dvb->adapter, dvb->frontend); if (result < 0) { - printk(KERN_ERR "%s: dvb_register_frontend failed " - "(errno = %d)\n", DRIVER_NAME, result); + pr_err("dvb_register_frontend failed (errno = %d)\n", + result); 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 | @@ -335,8 +440,7 @@ dvb->demux.stop_feed = au0828_dvb_stop_feed; result = dvb_dmx_init(&dvb->demux); if (result < 0) { - printk(KERN_ERR "%s: dvb_dmx_init failed (errno = %d)\n", - DRIVER_NAME, result); + pr_err("dvb_dmx_init failed (errno = %d)\n", result); goto fail_dmx; } @@ -345,36 +449,37 @@ dvb->dmxdev.capabilities = 0; result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter); if (result < 0) { - printk(KERN_ERR "%s: dvb_dmxdev_init failed (errno = %d)\n", - DRIVER_NAME, result); + pr_err("dvb_dmxdev_init failed (errno = %d)\n", result); goto fail_dmxdev; } dvb->fe_hw.source = DMX_FRONTEND_0; result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw); if (result < 0) { - printk(KERN_ERR "%s: add_frontend failed " - "(DMX_FRONTEND_0, errno = %d)\n", DRIVER_NAME, result); + pr_err("add_frontend failed (DMX_FRONTEND_0, errno = %d)\n", + result); goto fail_fe_hw; } dvb->fe_mem.source = DMX_MEMORY_FE; result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem); if (result < 0) { - printk(KERN_ERR "%s: add_frontend failed " - "(DMX_MEMORY_FE, errno = %d)\n", DRIVER_NAME, result); + pr_err("add_frontend failed (DMX_MEMORY_FE, errno = %d)\n", + result); goto fail_fe_mem; } result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw); if (result < 0) { - printk(KERN_ERR "%s: connect_frontend failed (errno = %d)\n", - DRIVER_NAME, result); + pr_err("connect_frontend failed (errno = %d)\n", result); goto fail_fe_conn; } /* register network adapter */ dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx); + + dvb->start_count = 0; + dvb->stop_count = 0; return 0; fail_fe_conn: @@ -391,6 +496,13 @@ dvb_frontend_detach(dvb->frontend); dvb_unregister_adapter(&dvb->adapter); fail_adapter: + + if (preallocate_big_buffers) { + int i; + for (i = 0; i < URB_COUNT; i++) + kfree(dev->dig_transfer_buffer[i]); + } + return result; } @@ -403,6 +515,8 @@ if (dvb->frontend == NULL) return; + cancel_work_sync(&dev->restart_streaming); + dvb_net_release(&dvb->net); dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); @@ -411,6 +525,13 @@ dvb_unregister_frontend(dvb->frontend); dvb_frontend_detach(dvb->frontend); dvb_unregister_adapter(&dvb->adapter); + + if (preallocate_big_buffers) { + int i; + for (i = 0; i < URB_COUNT; i++) + kfree(dev->dig_transfer_buffer[i]); + } + dvb->frontend = NULL; } /* All the DVB attach calls go here, this function get's modified @@ -475,12 +596,11 @@ } break; default: - printk(KERN_WARNING "The frontend of your DVB/ATSC card " - "isn't supported yet\n"); + pr_warn("The frontend of your DVB/ATSC card isn't supported yet\n"); break; } if (NULL == dvb->frontend) { - printk(KERN_ERR "%s() Frontend initialization failed\n", + pr_err("%s() Frontend initialization failed\n", __func__); return -1; } @@ -492,8 +612,49 @@ if (ret < 0) { if (dvb->frontend->ops.release) dvb->frontend->ops.release(dvb->frontend); + dvb->frontend = NULL; return ret; } return 0; } + +void au0828_dvb_suspend(struct au0828_dev *dev) +{ + struct au0828_dvb *dvb = &dev->dvb; + int rc; + + if (dvb->frontend) { + if (dev->urb_streaming) { + cancel_work_sync(&dev->restart_streaming); + /* Stop transport */ + mutex_lock(&dvb->lock); + stop_urb_transfer(dev); + au0828_stop_transport(dev, 1); + mutex_unlock(&dvb->lock); + dev->need_urb_start = true; + } + /* suspend frontend - does tuner and fe to sleep */ + rc = dvb_frontend_suspend(dvb->frontend); + pr_info("au0828_dvb_suspend(): Suspending DVB fe %d\n", rc); + } +} + +void au0828_dvb_resume(struct au0828_dev *dev) +{ + struct au0828_dvb *dvb = &dev->dvb; + int rc; + + if (dvb->frontend) { + /* resume frontend - does fe and tuner init */ + rc = dvb_frontend_resume(dvb->frontend); + pr_info("au0828_dvb_resume(): Resuming DVB fe %d\n", rc); + if (dev->need_urb_start) { + /* Start transport */ + mutex_lock(&dvb->lock); + au0828_start_transport(dev); + start_urb_transfer(dev); + mutex_unlock(&dvb->lock); + } + } +}