162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Driver for the Auvitek USB bridge
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "au0828.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/init.h>
1362306a36Sopenharmony_ci#include <linux/device.h>
1462306a36Sopenharmony_ci#include <media/v4l2-common.h>
1562306a36Sopenharmony_ci#include <media/tuner.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include "au8522.h"
1862306a36Sopenharmony_ci#include "xc5000.h"
1962306a36Sopenharmony_ci#include "mxl5007t.h"
2062306a36Sopenharmony_ci#include "tda18271.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic int preallocate_big_buffers;
2362306a36Sopenharmony_cimodule_param_named(preallocate_big_buffers, preallocate_big_buffers, int, 0644);
2462306a36Sopenharmony_ciMODULE_PARM_DESC(preallocate_big_buffers, "Preallocate the larger transfer buffers at module load time");
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ciDVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define _AU0828_BULKPIPE 0x83
2962306a36Sopenharmony_ci#define _BULKPIPESIZE 0xe522
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic u8 hauppauge_hvr950q_led_states[] = {
3262306a36Sopenharmony_ci	0x00, /* off */
3362306a36Sopenharmony_ci	0x02, /* yellow */
3462306a36Sopenharmony_ci	0x04, /* green */
3562306a36Sopenharmony_ci};
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic struct au8522_led_config hauppauge_hvr950q_led_cfg = {
3862306a36Sopenharmony_ci	.gpio_output = 0x00e0,
3962306a36Sopenharmony_ci	.gpio_output_enable  = 0x6006,
4062306a36Sopenharmony_ci	.gpio_output_disable = 0x0660,
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	.gpio_leds = 0x00e2,
4362306a36Sopenharmony_ci	.led_states  = hauppauge_hvr950q_led_states,
4462306a36Sopenharmony_ci	.num_led_states = sizeof(hauppauge_hvr950q_led_states),
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	.vsb8_strong   = 20 /* dB */ * 10,
4762306a36Sopenharmony_ci	.qam64_strong  = 25 /* dB */ * 10,
4862306a36Sopenharmony_ci	.qam256_strong = 32 /* dB */ * 10,
4962306a36Sopenharmony_ci};
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic struct au8522_config hauppauge_hvr950q_config = {
5262306a36Sopenharmony_ci	.demod_address = 0x8e >> 1,
5362306a36Sopenharmony_ci	.status_mode   = AU8522_DEMODLOCKING,
5462306a36Sopenharmony_ci	.qam_if        = AU8522_IF_6MHZ,
5562306a36Sopenharmony_ci	.vsb_if        = AU8522_IF_6MHZ,
5662306a36Sopenharmony_ci	.led_cfg       = &hauppauge_hvr950q_led_cfg,
5762306a36Sopenharmony_ci};
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic struct au8522_config fusionhdtv7usb_config = {
6062306a36Sopenharmony_ci	.demod_address = 0x8e >> 1,
6162306a36Sopenharmony_ci	.status_mode   = AU8522_DEMODLOCKING,
6262306a36Sopenharmony_ci	.qam_if        = AU8522_IF_6MHZ,
6362306a36Sopenharmony_ci	.vsb_if        = AU8522_IF_6MHZ,
6462306a36Sopenharmony_ci};
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic struct au8522_config hauppauge_woodbury_config = {
6762306a36Sopenharmony_ci	.demod_address = 0x8e >> 1,
6862306a36Sopenharmony_ci	.status_mode   = AU8522_DEMODLOCKING,
6962306a36Sopenharmony_ci	.qam_if        = AU8522_IF_4MHZ,
7062306a36Sopenharmony_ci	.vsb_if        = AU8522_IF_3_25MHZ,
7162306a36Sopenharmony_ci};
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic struct xc5000_config hauppauge_xc5000a_config = {
7462306a36Sopenharmony_ci	.i2c_address      = 0x61,
7562306a36Sopenharmony_ci	.if_khz           = 6000,
7662306a36Sopenharmony_ci	.chip_id          = XC5000A,
7762306a36Sopenharmony_ci	.output_amp       = 0x8f,
7862306a36Sopenharmony_ci};
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic struct xc5000_config hauppauge_xc5000c_config = {
8162306a36Sopenharmony_ci	.i2c_address      = 0x61,
8262306a36Sopenharmony_ci	.if_khz           = 6000,
8362306a36Sopenharmony_ci	.chip_id          = XC5000C,
8462306a36Sopenharmony_ci	.output_amp       = 0x8f,
8562306a36Sopenharmony_ci};
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic struct mxl5007t_config mxl5007t_hvr950q_config = {
8862306a36Sopenharmony_ci	.xtal_freq_hz = MxL_XTAL_24_MHZ,
8962306a36Sopenharmony_ci	.if_freq_hz = MxL_IF_6_MHZ,
9062306a36Sopenharmony_ci};
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic struct tda18271_config hauppauge_woodbury_tunerconfig = {
9362306a36Sopenharmony_ci	.gate    = TDA18271_GATE_DIGITAL,
9462306a36Sopenharmony_ci};
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic void au0828_restart_dvb_streaming(struct work_struct *work);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic void au0828_bulk_timeout(struct timer_list *t)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	struct au0828_dev *dev = from_timer(dev, t, bulk_timeout);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	dprintk(1, "%s called\n", __func__);
10362306a36Sopenharmony_ci	dev->bulk_timeout_running = 0;
10462306a36Sopenharmony_ci	schedule_work(&dev->restart_streaming);
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci/*-------------------------------------------------------------------*/
10862306a36Sopenharmony_cistatic void urb_completion(struct urb *purb)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	struct au0828_dev *dev = purb->context;
11162306a36Sopenharmony_ci	int ptype = usb_pipetype(purb->pipe);
11262306a36Sopenharmony_ci	unsigned char *ptr;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	dprintk(2, "%s: %d\n", __func__, purb->actual_length);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	if (!dev) {
11762306a36Sopenharmony_ci		dprintk(2, "%s: no dev!\n", __func__);
11862306a36Sopenharmony_ci		return;
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	if (!dev->urb_streaming) {
12262306a36Sopenharmony_ci		dprintk(2, "%s: not streaming!\n", __func__);
12362306a36Sopenharmony_ci		return;
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	if (ptype != PIPE_BULK) {
12762306a36Sopenharmony_ci		pr_err("%s: Unsupported URB type %d\n",
12862306a36Sopenharmony_ci		       __func__, ptype);
12962306a36Sopenharmony_ci		return;
13062306a36Sopenharmony_ci	}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	/* See if the stream is corrupted (to work around a hardware
13362306a36Sopenharmony_ci	   bug where the stream gets misaligned */
13462306a36Sopenharmony_ci	ptr = purb->transfer_buffer;
13562306a36Sopenharmony_ci	if (purb->actual_length > 0 && ptr[0] != 0x47) {
13662306a36Sopenharmony_ci		dprintk(1, "Need to restart streaming %02x len=%d!\n",
13762306a36Sopenharmony_ci			ptr[0], purb->actual_length);
13862306a36Sopenharmony_ci		schedule_work(&dev->restart_streaming);
13962306a36Sopenharmony_ci		return;
14062306a36Sopenharmony_ci	} else if (dev->bulk_timeout_running == 1) {
14162306a36Sopenharmony_ci		/* The URB handler has fired, so cancel timer which would
14262306a36Sopenharmony_ci		 * restart endpoint if we hadn't
14362306a36Sopenharmony_ci		 */
14462306a36Sopenharmony_ci		dprintk(1, "%s cancelling bulk timeout\n", __func__);
14562306a36Sopenharmony_ci		dev->bulk_timeout_running = 0;
14662306a36Sopenharmony_ci		del_timer(&dev->bulk_timeout);
14762306a36Sopenharmony_ci	}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	/* Feed the transport payload into the kernel demux */
15062306a36Sopenharmony_ci	dvb_dmx_swfilter_packets(&dev->dvb.demux,
15162306a36Sopenharmony_ci		purb->transfer_buffer, purb->actual_length / 188);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	/* Clean the buffer before we requeue */
15462306a36Sopenharmony_ci	memset(purb->transfer_buffer, 0, URB_BUFSIZE);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	/* Requeue URB */
15762306a36Sopenharmony_ci	usb_submit_urb(purb, GFP_ATOMIC);
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic int stop_urb_transfer(struct au0828_dev *dev)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	int i;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	dprintk(2, "%s()\n", __func__);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	if (!dev->urb_streaming)
16762306a36Sopenharmony_ci		return 0;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	if (dev->bulk_timeout_running == 1) {
17062306a36Sopenharmony_ci		dev->bulk_timeout_running = 0;
17162306a36Sopenharmony_ci		del_timer(&dev->bulk_timeout);
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	dev->urb_streaming = false;
17562306a36Sopenharmony_ci	for (i = 0; i < URB_COUNT; i++) {
17662306a36Sopenharmony_ci		if (dev->urbs[i]) {
17762306a36Sopenharmony_ci			usb_kill_urb(dev->urbs[i]);
17862306a36Sopenharmony_ci			if (!preallocate_big_buffers)
17962306a36Sopenharmony_ci				kfree(dev->urbs[i]->transfer_buffer);
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci			usb_free_urb(dev->urbs[i]);
18262306a36Sopenharmony_ci		}
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	return 0;
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cistatic int start_urb_transfer(struct au0828_dev *dev)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	struct urb *purb;
19162306a36Sopenharmony_ci	int i, ret;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	dprintk(2, "%s()\n", __func__);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	if (dev->urb_streaming) {
19662306a36Sopenharmony_ci		dprintk(2, "%s: bulk xfer already running!\n", __func__);
19762306a36Sopenharmony_ci		return 0;
19862306a36Sopenharmony_ci	}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	for (i = 0; i < URB_COUNT; i++) {
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci		dev->urbs[i] = usb_alloc_urb(0, GFP_KERNEL);
20362306a36Sopenharmony_ci		if (!dev->urbs[i])
20462306a36Sopenharmony_ci			return -ENOMEM;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci		purb = dev->urbs[i];
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci		if (preallocate_big_buffers)
20962306a36Sopenharmony_ci			purb->transfer_buffer = dev->dig_transfer_buffer[i];
21062306a36Sopenharmony_ci		else
21162306a36Sopenharmony_ci			purb->transfer_buffer = kzalloc(URB_BUFSIZE,
21262306a36Sopenharmony_ci					GFP_KERNEL);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci		if (!purb->transfer_buffer) {
21562306a36Sopenharmony_ci			usb_free_urb(purb);
21662306a36Sopenharmony_ci			dev->urbs[i] = NULL;
21762306a36Sopenharmony_ci			ret = -ENOMEM;
21862306a36Sopenharmony_ci			pr_err("%s: failed big buffer allocation, err = %d\n",
21962306a36Sopenharmony_ci			       __func__, ret);
22062306a36Sopenharmony_ci			return ret;
22162306a36Sopenharmony_ci		}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci		purb->status = -EINPROGRESS;
22462306a36Sopenharmony_ci		usb_fill_bulk_urb(purb,
22562306a36Sopenharmony_ci				  dev->usbdev,
22662306a36Sopenharmony_ci				  usb_rcvbulkpipe(dev->usbdev,
22762306a36Sopenharmony_ci					_AU0828_BULKPIPE),
22862306a36Sopenharmony_ci				  purb->transfer_buffer,
22962306a36Sopenharmony_ci				  URB_BUFSIZE,
23062306a36Sopenharmony_ci				  urb_completion,
23162306a36Sopenharmony_ci				  dev);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	for (i = 0; i < URB_COUNT; i++) {
23662306a36Sopenharmony_ci		ret = usb_submit_urb(dev->urbs[i], GFP_ATOMIC);
23762306a36Sopenharmony_ci		if (ret != 0) {
23862306a36Sopenharmony_ci			stop_urb_transfer(dev);
23962306a36Sopenharmony_ci			pr_err("%s: failed urb submission, err = %d\n",
24062306a36Sopenharmony_ci			       __func__, ret);
24162306a36Sopenharmony_ci			return ret;
24262306a36Sopenharmony_ci		}
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	dev->urb_streaming = true;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	/* If we don't valid data within 1 second, restart stream */
24862306a36Sopenharmony_ci	mod_timer(&dev->bulk_timeout, jiffies + (HZ));
24962306a36Sopenharmony_ci	dev->bulk_timeout_running = 1;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	return 0;
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cistatic void au0828_start_transport(struct au0828_dev *dev)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	au0828_write(dev, 0x608, 0x90);
25762306a36Sopenharmony_ci	au0828_write(dev, 0x609, 0x72);
25862306a36Sopenharmony_ci	au0828_write(dev, 0x60a, 0x71);
25962306a36Sopenharmony_ci	au0828_write(dev, 0x60b, 0x01);
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_cistatic void au0828_stop_transport(struct au0828_dev *dev, int full_stop)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	if (full_stop) {
26662306a36Sopenharmony_ci		au0828_write(dev, 0x608, 0x00);
26762306a36Sopenharmony_ci		au0828_write(dev, 0x609, 0x00);
26862306a36Sopenharmony_ci		au0828_write(dev, 0x60a, 0x00);
26962306a36Sopenharmony_ci	}
27062306a36Sopenharmony_ci	au0828_write(dev, 0x60b, 0x00);
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic int au0828_dvb_start_feed(struct dvb_demux_feed *feed)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	struct dvb_demux *demux = feed->demux;
27662306a36Sopenharmony_ci	struct au0828_dev *dev = demux->priv;
27762306a36Sopenharmony_ci	struct au0828_dvb *dvb = &dev->dvb;
27862306a36Sopenharmony_ci	int ret = 0;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	dprintk(1, "%s()\n", __func__);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	if (!demux->dmx.frontend)
28362306a36Sopenharmony_ci		return -EINVAL;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	if (dvb->frontend) {
28662306a36Sopenharmony_ci		mutex_lock(&dvb->lock);
28762306a36Sopenharmony_ci		dvb->start_count++;
28862306a36Sopenharmony_ci		dprintk(1, "%s(), start_count: %d, stop_count: %d\n", __func__,
28962306a36Sopenharmony_ci			dvb->start_count, dvb->stop_count);
29062306a36Sopenharmony_ci		if (dvb->feeding++ == 0) {
29162306a36Sopenharmony_ci			/* Start transport */
29262306a36Sopenharmony_ci			au0828_start_transport(dev);
29362306a36Sopenharmony_ci			ret = start_urb_transfer(dev);
29462306a36Sopenharmony_ci			if (ret < 0) {
29562306a36Sopenharmony_ci				au0828_stop_transport(dev, 0);
29662306a36Sopenharmony_ci				dvb->feeding--;	/* We ran out of memory... */
29762306a36Sopenharmony_ci			}
29862306a36Sopenharmony_ci		}
29962306a36Sopenharmony_ci		mutex_unlock(&dvb->lock);
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	return ret;
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cistatic int au0828_dvb_stop_feed(struct dvb_demux_feed *feed)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	struct dvb_demux *demux = feed->demux;
30862306a36Sopenharmony_ci	struct au0828_dev *dev = demux->priv;
30962306a36Sopenharmony_ci	struct au0828_dvb *dvb = &dev->dvb;
31062306a36Sopenharmony_ci	int ret = 0;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	dprintk(1, "%s()\n", __func__);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	if (dvb->frontend) {
31562306a36Sopenharmony_ci		cancel_work_sync(&dev->restart_streaming);
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci		mutex_lock(&dvb->lock);
31862306a36Sopenharmony_ci		dvb->stop_count++;
31962306a36Sopenharmony_ci		dprintk(1, "%s(), start_count: %d, stop_count: %d\n", __func__,
32062306a36Sopenharmony_ci			dvb->start_count, dvb->stop_count);
32162306a36Sopenharmony_ci		if (dvb->feeding > 0) {
32262306a36Sopenharmony_ci			dvb->feeding--;
32362306a36Sopenharmony_ci			if (dvb->feeding == 0) {
32462306a36Sopenharmony_ci				/* Stop transport */
32562306a36Sopenharmony_ci				ret = stop_urb_transfer(dev);
32662306a36Sopenharmony_ci				au0828_stop_transport(dev, 0);
32762306a36Sopenharmony_ci			}
32862306a36Sopenharmony_ci		}
32962306a36Sopenharmony_ci		mutex_unlock(&dvb->lock);
33062306a36Sopenharmony_ci	}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	return ret;
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_cistatic void au0828_restart_dvb_streaming(struct work_struct *work)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	struct au0828_dev *dev = container_of(work, struct au0828_dev,
33862306a36Sopenharmony_ci					      restart_streaming);
33962306a36Sopenharmony_ci	struct au0828_dvb *dvb = &dev->dvb;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	if (!dev->urb_streaming)
34262306a36Sopenharmony_ci		return;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	dprintk(1, "Restarting streaming...!\n");
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	mutex_lock(&dvb->lock);
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	/* Stop transport */
34962306a36Sopenharmony_ci	stop_urb_transfer(dev);
35062306a36Sopenharmony_ci	au0828_stop_transport(dev, 1);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	/* Start transport */
35362306a36Sopenharmony_ci	au0828_start_transport(dev);
35462306a36Sopenharmony_ci	start_urb_transfer(dev);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	mutex_unlock(&dvb->lock);
35762306a36Sopenharmony_ci}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cistatic int au0828_set_frontend(struct dvb_frontend *fe)
36062306a36Sopenharmony_ci{
36162306a36Sopenharmony_ci	struct au0828_dev *dev = fe->dvb->priv;
36262306a36Sopenharmony_ci	struct au0828_dvb *dvb = &dev->dvb;
36362306a36Sopenharmony_ci	int ret, was_streaming;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	mutex_lock(&dvb->lock);
36662306a36Sopenharmony_ci	was_streaming = dev->urb_streaming;
36762306a36Sopenharmony_ci	if (was_streaming) {
36862306a36Sopenharmony_ci		au0828_stop_transport(dev, 1);
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci		/*
37162306a36Sopenharmony_ci		 * We can't hold a mutex here, as the restart_streaming
37262306a36Sopenharmony_ci		 * kthread may also hold it.
37362306a36Sopenharmony_ci		 */
37462306a36Sopenharmony_ci		mutex_unlock(&dvb->lock);
37562306a36Sopenharmony_ci		cancel_work_sync(&dev->restart_streaming);
37662306a36Sopenharmony_ci		mutex_lock(&dvb->lock);
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci		stop_urb_transfer(dev);
37962306a36Sopenharmony_ci	}
38062306a36Sopenharmony_ci	mutex_unlock(&dvb->lock);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	ret = dvb->set_frontend(fe);
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	if (was_streaming) {
38562306a36Sopenharmony_ci		mutex_lock(&dvb->lock);
38662306a36Sopenharmony_ci		au0828_start_transport(dev);
38762306a36Sopenharmony_ci		start_urb_transfer(dev);
38862306a36Sopenharmony_ci		mutex_unlock(&dvb->lock);
38962306a36Sopenharmony_ci	}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	return ret;
39262306a36Sopenharmony_ci}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_cistatic int dvb_register(struct au0828_dev *dev)
39562306a36Sopenharmony_ci{
39662306a36Sopenharmony_ci	struct au0828_dvb *dvb = &dev->dvb;
39762306a36Sopenharmony_ci	int result;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	dprintk(1, "%s()\n", __func__);
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	if (preallocate_big_buffers) {
40262306a36Sopenharmony_ci		int i;
40362306a36Sopenharmony_ci		for (i = 0; i < URB_COUNT; i++) {
40462306a36Sopenharmony_ci			dev->dig_transfer_buffer[i] = kzalloc(URB_BUFSIZE,
40562306a36Sopenharmony_ci					GFP_KERNEL);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci			if (!dev->dig_transfer_buffer[i]) {
40862306a36Sopenharmony_ci				result = -ENOMEM;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci				pr_err("failed buffer allocation (errno = %d)\n",
41162306a36Sopenharmony_ci				       result);
41262306a36Sopenharmony_ci				goto fail_adapter;
41362306a36Sopenharmony_ci			}
41462306a36Sopenharmony_ci		}
41562306a36Sopenharmony_ci	}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	INIT_WORK(&dev->restart_streaming, au0828_restart_dvb_streaming);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	/* register adapter */
42062306a36Sopenharmony_ci	result = dvb_register_adapter(&dvb->adapter,
42162306a36Sopenharmony_ci				      KBUILD_MODNAME, THIS_MODULE,
42262306a36Sopenharmony_ci				      &dev->usbdev->dev, adapter_nr);
42362306a36Sopenharmony_ci	if (result < 0) {
42462306a36Sopenharmony_ci		pr_err("dvb_register_adapter failed (errno = %d)\n",
42562306a36Sopenharmony_ci		       result);
42662306a36Sopenharmony_ci		goto fail_adapter;
42762306a36Sopenharmony_ci	}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER_DVB
43062306a36Sopenharmony_ci	dvb->adapter.mdev = dev->media_dev;
43162306a36Sopenharmony_ci#endif
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	dvb->adapter.priv = dev;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	/* register frontend */
43662306a36Sopenharmony_ci	result = dvb_register_frontend(&dvb->adapter, dvb->frontend);
43762306a36Sopenharmony_ci	if (result < 0) {
43862306a36Sopenharmony_ci		pr_err("dvb_register_frontend failed (errno = %d)\n",
43962306a36Sopenharmony_ci		       result);
44062306a36Sopenharmony_ci		goto fail_frontend;
44162306a36Sopenharmony_ci	}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	/* Hook dvb frontend */
44462306a36Sopenharmony_ci	dvb->set_frontend = dvb->frontend->ops.set_frontend;
44562306a36Sopenharmony_ci	dvb->frontend->ops.set_frontend = au0828_set_frontend;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	/* register demux stuff */
44862306a36Sopenharmony_ci	dvb->demux.dmx.capabilities =
44962306a36Sopenharmony_ci		DMX_TS_FILTERING | DMX_SECTION_FILTERING |
45062306a36Sopenharmony_ci		DMX_MEMORY_BASED_FILTERING;
45162306a36Sopenharmony_ci	dvb->demux.priv       = dev;
45262306a36Sopenharmony_ci	dvb->demux.filternum  = 256;
45362306a36Sopenharmony_ci	dvb->demux.feednum    = 256;
45462306a36Sopenharmony_ci	dvb->demux.start_feed = au0828_dvb_start_feed;
45562306a36Sopenharmony_ci	dvb->demux.stop_feed  = au0828_dvb_stop_feed;
45662306a36Sopenharmony_ci	result = dvb_dmx_init(&dvb->demux);
45762306a36Sopenharmony_ci	if (result < 0) {
45862306a36Sopenharmony_ci		pr_err("dvb_dmx_init failed (errno = %d)\n", result);
45962306a36Sopenharmony_ci		goto fail_dmx;
46062306a36Sopenharmony_ci	}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	dvb->dmxdev.filternum    = 256;
46362306a36Sopenharmony_ci	dvb->dmxdev.demux        = &dvb->demux.dmx;
46462306a36Sopenharmony_ci	dvb->dmxdev.capabilities = 0;
46562306a36Sopenharmony_ci	result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter);
46662306a36Sopenharmony_ci	if (result < 0) {
46762306a36Sopenharmony_ci		pr_err("dvb_dmxdev_init failed (errno = %d)\n", result);
46862306a36Sopenharmony_ci		goto fail_dmxdev;
46962306a36Sopenharmony_ci	}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	dvb->fe_hw.source = DMX_FRONTEND_0;
47262306a36Sopenharmony_ci	result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw);
47362306a36Sopenharmony_ci	if (result < 0) {
47462306a36Sopenharmony_ci		pr_err("add_frontend failed (DMX_FRONTEND_0, errno = %d)\n",
47562306a36Sopenharmony_ci		       result);
47662306a36Sopenharmony_ci		goto fail_fe_hw;
47762306a36Sopenharmony_ci	}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	dvb->fe_mem.source = DMX_MEMORY_FE;
48062306a36Sopenharmony_ci	result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem);
48162306a36Sopenharmony_ci	if (result < 0) {
48262306a36Sopenharmony_ci		pr_err("add_frontend failed (DMX_MEMORY_FE, errno = %d)\n",
48362306a36Sopenharmony_ci		       result);
48462306a36Sopenharmony_ci		goto fail_fe_mem;
48562306a36Sopenharmony_ci	}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw);
48862306a36Sopenharmony_ci	if (result < 0) {
48962306a36Sopenharmony_ci		pr_err("connect_frontend failed (errno = %d)\n", result);
49062306a36Sopenharmony_ci		goto fail_fe_conn;
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	/* register network adapter */
49462306a36Sopenharmony_ci	dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	dvb->start_count = 0;
49762306a36Sopenharmony_ci	dvb->stop_count = 0;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	result = dvb_create_media_graph(&dvb->adapter, false);
50062306a36Sopenharmony_ci	if (result < 0)
50162306a36Sopenharmony_ci		goto fail_create_graph;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	return 0;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_cifail_create_graph:
50662306a36Sopenharmony_ci	dvb_net_release(&dvb->net);
50762306a36Sopenharmony_cifail_fe_conn:
50862306a36Sopenharmony_ci	dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
50962306a36Sopenharmony_cifail_fe_mem:
51062306a36Sopenharmony_ci	dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
51162306a36Sopenharmony_cifail_fe_hw:
51262306a36Sopenharmony_ci	dvb_dmxdev_release(&dvb->dmxdev);
51362306a36Sopenharmony_cifail_dmxdev:
51462306a36Sopenharmony_ci	dvb_dmx_release(&dvb->demux);
51562306a36Sopenharmony_cifail_dmx:
51662306a36Sopenharmony_ci	dvb_unregister_frontend(dvb->frontend);
51762306a36Sopenharmony_cifail_frontend:
51862306a36Sopenharmony_ci	dvb_frontend_detach(dvb->frontend);
51962306a36Sopenharmony_ci	dvb_unregister_adapter(&dvb->adapter);
52062306a36Sopenharmony_cifail_adapter:
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	if (preallocate_big_buffers) {
52362306a36Sopenharmony_ci		int i;
52462306a36Sopenharmony_ci		for (i = 0; i < URB_COUNT; i++)
52562306a36Sopenharmony_ci			kfree(dev->dig_transfer_buffer[i]);
52662306a36Sopenharmony_ci	}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	return result;
52962306a36Sopenharmony_ci}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_civoid au0828_dvb_unregister(struct au0828_dev *dev)
53262306a36Sopenharmony_ci{
53362306a36Sopenharmony_ci	struct au0828_dvb *dvb = &dev->dvb;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	dprintk(1, "%s()\n", __func__);
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	if (dvb->frontend == NULL)
53862306a36Sopenharmony_ci		return;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	cancel_work_sync(&dev->restart_streaming);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	dvb_net_release(&dvb->net);
54362306a36Sopenharmony_ci	dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
54462306a36Sopenharmony_ci	dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
54562306a36Sopenharmony_ci	dvb_dmxdev_release(&dvb->dmxdev);
54662306a36Sopenharmony_ci	dvb_dmx_release(&dvb->demux);
54762306a36Sopenharmony_ci	dvb_unregister_frontend(dvb->frontend);
54862306a36Sopenharmony_ci	dvb_frontend_detach(dvb->frontend);
54962306a36Sopenharmony_ci	dvb_unregister_adapter(&dvb->adapter);
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	if (preallocate_big_buffers) {
55262306a36Sopenharmony_ci		int i;
55362306a36Sopenharmony_ci		for (i = 0; i < URB_COUNT; i++)
55462306a36Sopenharmony_ci			kfree(dev->dig_transfer_buffer[i]);
55562306a36Sopenharmony_ci	}
55662306a36Sopenharmony_ci	dvb->frontend = NULL;
55762306a36Sopenharmony_ci}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci/* All the DVB attach calls go here, this function gets modified
56062306a36Sopenharmony_ci * for each new card. No other function in this file needs
56162306a36Sopenharmony_ci * to change.
56262306a36Sopenharmony_ci */
56362306a36Sopenharmony_ciint au0828_dvb_register(struct au0828_dev *dev)
56462306a36Sopenharmony_ci{
56562306a36Sopenharmony_ci	struct au0828_dvb *dvb = &dev->dvb;
56662306a36Sopenharmony_ci	int ret;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	dprintk(1, "%s()\n", __func__);
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	/* init frontend */
57162306a36Sopenharmony_ci	switch (dev->boardnr) {
57262306a36Sopenharmony_ci	case AU0828_BOARD_HAUPPAUGE_HVR850:
57362306a36Sopenharmony_ci	case AU0828_BOARD_HAUPPAUGE_HVR950Q:
57462306a36Sopenharmony_ci		dvb->frontend = dvb_attach(au8522_attach,
57562306a36Sopenharmony_ci				&hauppauge_hvr950q_config,
57662306a36Sopenharmony_ci				&dev->i2c_adap);
57762306a36Sopenharmony_ci		if (dvb->frontend != NULL)
57862306a36Sopenharmony_ci			switch (dev->board.tuner_type) {
57962306a36Sopenharmony_ci			default:
58062306a36Sopenharmony_ci			case TUNER_XC5000:
58162306a36Sopenharmony_ci				dvb_attach(xc5000_attach, dvb->frontend,
58262306a36Sopenharmony_ci					   &dev->i2c_adap,
58362306a36Sopenharmony_ci					   &hauppauge_xc5000a_config);
58462306a36Sopenharmony_ci				break;
58562306a36Sopenharmony_ci			case TUNER_XC5000C:
58662306a36Sopenharmony_ci				dvb_attach(xc5000_attach, dvb->frontend,
58762306a36Sopenharmony_ci					   &dev->i2c_adap,
58862306a36Sopenharmony_ci					   &hauppauge_xc5000c_config);
58962306a36Sopenharmony_ci				break;
59062306a36Sopenharmony_ci			}
59162306a36Sopenharmony_ci		break;
59262306a36Sopenharmony_ci	case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL:
59362306a36Sopenharmony_ci		dvb->frontend = dvb_attach(au8522_attach,
59462306a36Sopenharmony_ci				&hauppauge_hvr950q_config,
59562306a36Sopenharmony_ci				&dev->i2c_adap);
59662306a36Sopenharmony_ci		if (dvb->frontend != NULL)
59762306a36Sopenharmony_ci			dvb_attach(mxl5007t_attach, dvb->frontend,
59862306a36Sopenharmony_ci				   &dev->i2c_adap, 0x60,
59962306a36Sopenharmony_ci				   &mxl5007t_hvr950q_config);
60062306a36Sopenharmony_ci		break;
60162306a36Sopenharmony_ci	case AU0828_BOARD_HAUPPAUGE_WOODBURY:
60262306a36Sopenharmony_ci		dvb->frontend = dvb_attach(au8522_attach,
60362306a36Sopenharmony_ci				&hauppauge_woodbury_config,
60462306a36Sopenharmony_ci				&dev->i2c_adap);
60562306a36Sopenharmony_ci		if (dvb->frontend != NULL)
60662306a36Sopenharmony_ci			dvb_attach(tda18271_attach, dvb->frontend,
60762306a36Sopenharmony_ci				   0x60, &dev->i2c_adap,
60862306a36Sopenharmony_ci				   &hauppauge_woodbury_tunerconfig);
60962306a36Sopenharmony_ci		break;
61062306a36Sopenharmony_ci	case AU0828_BOARD_DVICO_FUSIONHDTV7:
61162306a36Sopenharmony_ci		dvb->frontend = dvb_attach(au8522_attach,
61262306a36Sopenharmony_ci				&fusionhdtv7usb_config,
61362306a36Sopenharmony_ci				&dev->i2c_adap);
61462306a36Sopenharmony_ci		if (dvb->frontend != NULL) {
61562306a36Sopenharmony_ci			dvb_attach(xc5000_attach, dvb->frontend,
61662306a36Sopenharmony_ci				&dev->i2c_adap,
61762306a36Sopenharmony_ci				&hauppauge_xc5000a_config);
61862306a36Sopenharmony_ci		}
61962306a36Sopenharmony_ci		break;
62062306a36Sopenharmony_ci	default:
62162306a36Sopenharmony_ci		pr_warn("The frontend of your DVB/ATSC card isn't supported yet\n");
62262306a36Sopenharmony_ci		break;
62362306a36Sopenharmony_ci	}
62462306a36Sopenharmony_ci	if (NULL == dvb->frontend) {
62562306a36Sopenharmony_ci		pr_err("%s() Frontend initialization failed\n",
62662306a36Sopenharmony_ci		       __func__);
62762306a36Sopenharmony_ci		return -1;
62862306a36Sopenharmony_ci	}
62962306a36Sopenharmony_ci	/* define general-purpose callback pointer */
63062306a36Sopenharmony_ci	dvb->frontend->callback = au0828_tuner_callback;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	/* register everything */
63362306a36Sopenharmony_ci	ret = dvb_register(dev);
63462306a36Sopenharmony_ci	if (ret < 0) {
63562306a36Sopenharmony_ci		if (dvb->frontend->ops.release)
63662306a36Sopenharmony_ci			dvb->frontend->ops.release(dvb->frontend);
63762306a36Sopenharmony_ci		dvb->frontend = NULL;
63862306a36Sopenharmony_ci		return ret;
63962306a36Sopenharmony_ci	}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	timer_setup(&dev->bulk_timeout, au0828_bulk_timeout, 0);
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	return 0;
64462306a36Sopenharmony_ci}
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_civoid au0828_dvb_suspend(struct au0828_dev *dev)
64762306a36Sopenharmony_ci{
64862306a36Sopenharmony_ci	struct au0828_dvb *dvb = &dev->dvb;
64962306a36Sopenharmony_ci	int rc;
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	if (dvb->frontend) {
65262306a36Sopenharmony_ci		if (dev->urb_streaming) {
65362306a36Sopenharmony_ci			cancel_work_sync(&dev->restart_streaming);
65462306a36Sopenharmony_ci			/* Stop transport */
65562306a36Sopenharmony_ci			mutex_lock(&dvb->lock);
65662306a36Sopenharmony_ci			stop_urb_transfer(dev);
65762306a36Sopenharmony_ci			au0828_stop_transport(dev, 1);
65862306a36Sopenharmony_ci			mutex_unlock(&dvb->lock);
65962306a36Sopenharmony_ci			dev->need_urb_start = true;
66062306a36Sopenharmony_ci		}
66162306a36Sopenharmony_ci		/* suspend frontend - does tuner and fe to sleep */
66262306a36Sopenharmony_ci		rc = dvb_frontend_suspend(dvb->frontend);
66362306a36Sopenharmony_ci		pr_info("au0828_dvb_suspend(): Suspending DVB fe %d\n", rc);
66462306a36Sopenharmony_ci	}
66562306a36Sopenharmony_ci}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_civoid au0828_dvb_resume(struct au0828_dev *dev)
66862306a36Sopenharmony_ci{
66962306a36Sopenharmony_ci	struct au0828_dvb *dvb = &dev->dvb;
67062306a36Sopenharmony_ci	int rc;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	if (dvb->frontend) {
67362306a36Sopenharmony_ci		/* resume frontend - does fe and tuner init */
67462306a36Sopenharmony_ci		rc = dvb_frontend_resume(dvb->frontend);
67562306a36Sopenharmony_ci		pr_info("au0828_dvb_resume(): Resuming DVB fe %d\n", rc);
67662306a36Sopenharmony_ci		if (dev->need_urb_start) {
67762306a36Sopenharmony_ci			/* Start transport */
67862306a36Sopenharmony_ci			mutex_lock(&dvb->lock);
67962306a36Sopenharmony_ci			au0828_start_transport(dev);
68062306a36Sopenharmony_ci			start_urb_transfer(dev);
68162306a36Sopenharmony_ci			mutex_unlock(&dvb->lock);
68262306a36Sopenharmony_ci		}
68362306a36Sopenharmony_ci	}
68462306a36Sopenharmony_ci}
685