xref: /linux/drivers/media/usb/as102/as102_drv.c (revision d6317c68f3324e086799e12e4864231bb71829cb)
1*d6317c68SMauro Carvalho Chehab /*
2*d6317c68SMauro Carvalho Chehab  * Abilis Systems Single DVB-T Receiver
3*d6317c68SMauro Carvalho Chehab  * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
4*d6317c68SMauro Carvalho Chehab  * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com>
5*d6317c68SMauro Carvalho Chehab  *
6*d6317c68SMauro Carvalho Chehab  * This program is free software; you can redistribute it and/or modify
7*d6317c68SMauro Carvalho Chehab  * it under the terms of the GNU General Public License as published by
8*d6317c68SMauro Carvalho Chehab  * the Free Software Foundation; either version 2, or (at your option)
9*d6317c68SMauro Carvalho Chehab  * any later version.
10*d6317c68SMauro Carvalho Chehab  *
11*d6317c68SMauro Carvalho Chehab  * This program is distributed in the hope that it will be useful,
12*d6317c68SMauro Carvalho Chehab  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13*d6317c68SMauro Carvalho Chehab  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14*d6317c68SMauro Carvalho Chehab  * GNU General Public License for more details.
15*d6317c68SMauro Carvalho Chehab  *
16*d6317c68SMauro Carvalho Chehab  * You should have received a copy of the GNU General Public License
17*d6317c68SMauro Carvalho Chehab  * along with this program; if not, write to the Free Software
18*d6317c68SMauro Carvalho Chehab  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19*d6317c68SMauro Carvalho Chehab  */
20*d6317c68SMauro Carvalho Chehab #include <linux/kernel.h>
21*d6317c68SMauro Carvalho Chehab #include <linux/errno.h>
22*d6317c68SMauro Carvalho Chehab #include <linux/slab.h>
23*d6317c68SMauro Carvalho Chehab #include <linux/module.h>
24*d6317c68SMauro Carvalho Chehab #include <linux/mm.h>
25*d6317c68SMauro Carvalho Chehab #include <linux/kref.h>
26*d6317c68SMauro Carvalho Chehab #include <linux/uaccess.h>
27*d6317c68SMauro Carvalho Chehab #include <linux/usb.h>
28*d6317c68SMauro Carvalho Chehab 
29*d6317c68SMauro Carvalho Chehab /* header file for usb device driver*/
30*d6317c68SMauro Carvalho Chehab #include "as102_drv.h"
31*d6317c68SMauro Carvalho Chehab #include "as102_fw.h"
32*d6317c68SMauro Carvalho Chehab #include "dvbdev.h"
33*d6317c68SMauro Carvalho Chehab 
34*d6317c68SMauro Carvalho Chehab int dual_tuner;
35*d6317c68SMauro Carvalho Chehab module_param_named(dual_tuner, dual_tuner, int, 0644);
36*d6317c68SMauro Carvalho Chehab MODULE_PARM_DESC(dual_tuner, "Activate Dual-Tuner config (default: off)");
37*d6317c68SMauro Carvalho Chehab 
38*d6317c68SMauro Carvalho Chehab static int fw_upload = 1;
39*d6317c68SMauro Carvalho Chehab module_param_named(fw_upload, fw_upload, int, 0644);
40*d6317c68SMauro Carvalho Chehab MODULE_PARM_DESC(fw_upload, "Turn on/off default FW upload (default: on)");
41*d6317c68SMauro Carvalho Chehab 
42*d6317c68SMauro Carvalho Chehab static int pid_filtering;
43*d6317c68SMauro Carvalho Chehab module_param_named(pid_filtering, pid_filtering, int, 0644);
44*d6317c68SMauro Carvalho Chehab MODULE_PARM_DESC(pid_filtering, "Activate HW PID filtering (default: off)");
45*d6317c68SMauro Carvalho Chehab 
46*d6317c68SMauro Carvalho Chehab static int ts_auto_disable;
47*d6317c68SMauro Carvalho Chehab module_param_named(ts_auto_disable, ts_auto_disable, int, 0644);
48*d6317c68SMauro Carvalho Chehab MODULE_PARM_DESC(ts_auto_disable, "Stream Auto Enable on FW (default: off)");
49*d6317c68SMauro Carvalho Chehab 
50*d6317c68SMauro Carvalho Chehab int elna_enable = 1;
51*d6317c68SMauro Carvalho Chehab module_param_named(elna_enable, elna_enable, int, 0644);
52*d6317c68SMauro Carvalho Chehab MODULE_PARM_DESC(elna_enable, "Activate eLNA (default: on)");
53*d6317c68SMauro Carvalho Chehab 
54*d6317c68SMauro Carvalho Chehab DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
55*d6317c68SMauro Carvalho Chehab 
56*d6317c68SMauro Carvalho Chehab static void as102_stop_stream(struct as102_dev_t *dev)
57*d6317c68SMauro Carvalho Chehab {
58*d6317c68SMauro Carvalho Chehab 	struct as10x_bus_adapter_t *bus_adap;
59*d6317c68SMauro Carvalho Chehab 
60*d6317c68SMauro Carvalho Chehab 	if (dev != NULL)
61*d6317c68SMauro Carvalho Chehab 		bus_adap = &dev->bus_adap;
62*d6317c68SMauro Carvalho Chehab 	else
63*d6317c68SMauro Carvalho Chehab 		return;
64*d6317c68SMauro Carvalho Chehab 
65*d6317c68SMauro Carvalho Chehab 	if (bus_adap->ops->stop_stream != NULL)
66*d6317c68SMauro Carvalho Chehab 		bus_adap->ops->stop_stream(dev);
67*d6317c68SMauro Carvalho Chehab 
68*d6317c68SMauro Carvalho Chehab 	if (ts_auto_disable) {
69*d6317c68SMauro Carvalho Chehab 		if (mutex_lock_interruptible(&dev->bus_adap.lock))
70*d6317c68SMauro Carvalho Chehab 			return;
71*d6317c68SMauro Carvalho Chehab 
72*d6317c68SMauro Carvalho Chehab 		if (as10x_cmd_stop_streaming(bus_adap) < 0)
73*d6317c68SMauro Carvalho Chehab 			dev_dbg(&dev->bus_adap.usb_dev->dev,
74*d6317c68SMauro Carvalho Chehab 				"as10x_cmd_stop_streaming failed\n");
75*d6317c68SMauro Carvalho Chehab 
76*d6317c68SMauro Carvalho Chehab 		mutex_unlock(&dev->bus_adap.lock);
77*d6317c68SMauro Carvalho Chehab 	}
78*d6317c68SMauro Carvalho Chehab }
79*d6317c68SMauro Carvalho Chehab 
80*d6317c68SMauro Carvalho Chehab static int as102_start_stream(struct as102_dev_t *dev)
81*d6317c68SMauro Carvalho Chehab {
82*d6317c68SMauro Carvalho Chehab 	struct as10x_bus_adapter_t *bus_adap;
83*d6317c68SMauro Carvalho Chehab 	int ret = -EFAULT;
84*d6317c68SMauro Carvalho Chehab 
85*d6317c68SMauro Carvalho Chehab 	if (dev != NULL)
86*d6317c68SMauro Carvalho Chehab 		bus_adap = &dev->bus_adap;
87*d6317c68SMauro Carvalho Chehab 	else
88*d6317c68SMauro Carvalho Chehab 		return ret;
89*d6317c68SMauro Carvalho Chehab 
90*d6317c68SMauro Carvalho Chehab 	if (bus_adap->ops->start_stream != NULL)
91*d6317c68SMauro Carvalho Chehab 		ret = bus_adap->ops->start_stream(dev);
92*d6317c68SMauro Carvalho Chehab 
93*d6317c68SMauro Carvalho Chehab 	if (ts_auto_disable) {
94*d6317c68SMauro Carvalho Chehab 		if (mutex_lock_interruptible(&dev->bus_adap.lock))
95*d6317c68SMauro Carvalho Chehab 			return -EFAULT;
96*d6317c68SMauro Carvalho Chehab 
97*d6317c68SMauro Carvalho Chehab 		ret = as10x_cmd_start_streaming(bus_adap);
98*d6317c68SMauro Carvalho Chehab 
99*d6317c68SMauro Carvalho Chehab 		mutex_unlock(&dev->bus_adap.lock);
100*d6317c68SMauro Carvalho Chehab 	}
101*d6317c68SMauro Carvalho Chehab 
102*d6317c68SMauro Carvalho Chehab 	return ret;
103*d6317c68SMauro Carvalho Chehab }
104*d6317c68SMauro Carvalho Chehab 
105*d6317c68SMauro Carvalho Chehab static int as10x_pid_filter(struct as102_dev_t *dev,
106*d6317c68SMauro Carvalho Chehab 			    int index, u16 pid, int onoff) {
107*d6317c68SMauro Carvalho Chehab 
108*d6317c68SMauro Carvalho Chehab 	struct as10x_bus_adapter_t *bus_adap = &dev->bus_adap;
109*d6317c68SMauro Carvalho Chehab 	int ret = -EFAULT;
110*d6317c68SMauro Carvalho Chehab 
111*d6317c68SMauro Carvalho Chehab 	if (mutex_lock_interruptible(&dev->bus_adap.lock)) {
112*d6317c68SMauro Carvalho Chehab 		dev_dbg(&dev->bus_adap.usb_dev->dev,
113*d6317c68SMauro Carvalho Chehab 			"amutex_lock_interruptible(lock) failed !\n");
114*d6317c68SMauro Carvalho Chehab 		return -EBUSY;
115*d6317c68SMauro Carvalho Chehab 	}
116*d6317c68SMauro Carvalho Chehab 
117*d6317c68SMauro Carvalho Chehab 	switch (onoff) {
118*d6317c68SMauro Carvalho Chehab 	case 0:
119*d6317c68SMauro Carvalho Chehab 		ret = as10x_cmd_del_PID_filter(bus_adap, (uint16_t) pid);
120*d6317c68SMauro Carvalho Chehab 		dev_dbg(&dev->bus_adap.usb_dev->dev,
121*d6317c68SMauro Carvalho Chehab 			"DEL_PID_FILTER([%02d] 0x%04x) ret = %d\n",
122*d6317c68SMauro Carvalho Chehab 			index, pid, ret);
123*d6317c68SMauro Carvalho Chehab 		break;
124*d6317c68SMauro Carvalho Chehab 	case 1:
125*d6317c68SMauro Carvalho Chehab 	{
126*d6317c68SMauro Carvalho Chehab 		struct as10x_ts_filter filter;
127*d6317c68SMauro Carvalho Chehab 
128*d6317c68SMauro Carvalho Chehab 		filter.type = TS_PID_TYPE_TS;
129*d6317c68SMauro Carvalho Chehab 		filter.idx = 0xFF;
130*d6317c68SMauro Carvalho Chehab 		filter.pid = pid;
131*d6317c68SMauro Carvalho Chehab 
132*d6317c68SMauro Carvalho Chehab 		ret = as10x_cmd_add_PID_filter(bus_adap, &filter);
133*d6317c68SMauro Carvalho Chehab 		dev_dbg(&dev->bus_adap.usb_dev->dev,
134*d6317c68SMauro Carvalho Chehab 			"ADD_PID_FILTER([%02d -> %02d], 0x%04x) ret = %d\n",
135*d6317c68SMauro Carvalho Chehab 			index, filter.idx, filter.pid, ret);
136*d6317c68SMauro Carvalho Chehab 		break;
137*d6317c68SMauro Carvalho Chehab 	}
138*d6317c68SMauro Carvalho Chehab 	}
139*d6317c68SMauro Carvalho Chehab 
140*d6317c68SMauro Carvalho Chehab 	mutex_unlock(&dev->bus_adap.lock);
141*d6317c68SMauro Carvalho Chehab 	return ret;
142*d6317c68SMauro Carvalho Chehab }
143*d6317c68SMauro Carvalho Chehab 
144*d6317c68SMauro Carvalho Chehab static int as102_dvb_dmx_start_feed(struct dvb_demux_feed *dvbdmxfeed)
145*d6317c68SMauro Carvalho Chehab {
146*d6317c68SMauro Carvalho Chehab 	int ret = 0;
147*d6317c68SMauro Carvalho Chehab 	struct dvb_demux *demux = dvbdmxfeed->demux;
148*d6317c68SMauro Carvalho Chehab 	struct as102_dev_t *as102_dev = demux->priv;
149*d6317c68SMauro Carvalho Chehab 
150*d6317c68SMauro Carvalho Chehab 	if (mutex_lock_interruptible(&as102_dev->sem))
151*d6317c68SMauro Carvalho Chehab 		return -ERESTARTSYS;
152*d6317c68SMauro Carvalho Chehab 
153*d6317c68SMauro Carvalho Chehab 	if (pid_filtering)
154*d6317c68SMauro Carvalho Chehab 		as10x_pid_filter(as102_dev, dvbdmxfeed->index,
155*d6317c68SMauro Carvalho Chehab 				 dvbdmxfeed->pid, 1);
156*d6317c68SMauro Carvalho Chehab 
157*d6317c68SMauro Carvalho Chehab 	if (as102_dev->streaming++ == 0)
158*d6317c68SMauro Carvalho Chehab 		ret = as102_start_stream(as102_dev);
159*d6317c68SMauro Carvalho Chehab 
160*d6317c68SMauro Carvalho Chehab 	mutex_unlock(&as102_dev->sem);
161*d6317c68SMauro Carvalho Chehab 	return ret;
162*d6317c68SMauro Carvalho Chehab }
163*d6317c68SMauro Carvalho Chehab 
164*d6317c68SMauro Carvalho Chehab static int as102_dvb_dmx_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
165*d6317c68SMauro Carvalho Chehab {
166*d6317c68SMauro Carvalho Chehab 	struct dvb_demux *demux = dvbdmxfeed->demux;
167*d6317c68SMauro Carvalho Chehab 	struct as102_dev_t *as102_dev = demux->priv;
168*d6317c68SMauro Carvalho Chehab 
169*d6317c68SMauro Carvalho Chehab 	if (mutex_lock_interruptible(&as102_dev->sem))
170*d6317c68SMauro Carvalho Chehab 		return -ERESTARTSYS;
171*d6317c68SMauro Carvalho Chehab 
172*d6317c68SMauro Carvalho Chehab 	if (--as102_dev->streaming == 0)
173*d6317c68SMauro Carvalho Chehab 		as102_stop_stream(as102_dev);
174*d6317c68SMauro Carvalho Chehab 
175*d6317c68SMauro Carvalho Chehab 	if (pid_filtering)
176*d6317c68SMauro Carvalho Chehab 		as10x_pid_filter(as102_dev, dvbdmxfeed->index,
177*d6317c68SMauro Carvalho Chehab 				 dvbdmxfeed->pid, 0);
178*d6317c68SMauro Carvalho Chehab 
179*d6317c68SMauro Carvalho Chehab 	mutex_unlock(&as102_dev->sem);
180*d6317c68SMauro Carvalho Chehab 	return 0;
181*d6317c68SMauro Carvalho Chehab }
182*d6317c68SMauro Carvalho Chehab 
183*d6317c68SMauro Carvalho Chehab int as102_dvb_register(struct as102_dev_t *as102_dev)
184*d6317c68SMauro Carvalho Chehab {
185*d6317c68SMauro Carvalho Chehab 	struct device *dev = &as102_dev->bus_adap.usb_dev->dev;
186*d6317c68SMauro Carvalho Chehab 	int ret;
187*d6317c68SMauro Carvalho Chehab 
188*d6317c68SMauro Carvalho Chehab 	ret = dvb_register_adapter(&as102_dev->dvb_adap,
189*d6317c68SMauro Carvalho Chehab 			   as102_dev->name, THIS_MODULE,
190*d6317c68SMauro Carvalho Chehab 			   dev, adapter_nr);
191*d6317c68SMauro Carvalho Chehab 	if (ret < 0) {
192*d6317c68SMauro Carvalho Chehab 		dev_err(dev, "%s: dvb_register_adapter() failed: %d\n",
193*d6317c68SMauro Carvalho Chehab 			__func__, ret);
194*d6317c68SMauro Carvalho Chehab 		return ret;
195*d6317c68SMauro Carvalho Chehab 	}
196*d6317c68SMauro Carvalho Chehab 
197*d6317c68SMauro Carvalho Chehab 	as102_dev->dvb_dmx.priv = as102_dev;
198*d6317c68SMauro Carvalho Chehab 	as102_dev->dvb_dmx.filternum = pid_filtering ? 16 : 256;
199*d6317c68SMauro Carvalho Chehab 	as102_dev->dvb_dmx.feednum = 256;
200*d6317c68SMauro Carvalho Chehab 	as102_dev->dvb_dmx.start_feed = as102_dvb_dmx_start_feed;
201*d6317c68SMauro Carvalho Chehab 	as102_dev->dvb_dmx.stop_feed = as102_dvb_dmx_stop_feed;
202*d6317c68SMauro Carvalho Chehab 
203*d6317c68SMauro Carvalho Chehab 	as102_dev->dvb_dmx.dmx.capabilities = DMX_TS_FILTERING |
204*d6317c68SMauro Carvalho Chehab 					      DMX_SECTION_FILTERING;
205*d6317c68SMauro Carvalho Chehab 
206*d6317c68SMauro Carvalho Chehab 	as102_dev->dvb_dmxdev.filternum = as102_dev->dvb_dmx.filternum;
207*d6317c68SMauro Carvalho Chehab 	as102_dev->dvb_dmxdev.demux = &as102_dev->dvb_dmx.dmx;
208*d6317c68SMauro Carvalho Chehab 	as102_dev->dvb_dmxdev.capabilities = 0;
209*d6317c68SMauro Carvalho Chehab 
210*d6317c68SMauro Carvalho Chehab 	ret = dvb_dmx_init(&as102_dev->dvb_dmx);
211*d6317c68SMauro Carvalho Chehab 	if (ret < 0) {
212*d6317c68SMauro Carvalho Chehab 		dev_err(dev, "%s: dvb_dmx_init() failed: %d\n", __func__, ret);
213*d6317c68SMauro Carvalho Chehab 		goto edmxinit;
214*d6317c68SMauro Carvalho Chehab 	}
215*d6317c68SMauro Carvalho Chehab 
216*d6317c68SMauro Carvalho Chehab 	ret = dvb_dmxdev_init(&as102_dev->dvb_dmxdev, &as102_dev->dvb_adap);
217*d6317c68SMauro Carvalho Chehab 	if (ret < 0) {
218*d6317c68SMauro Carvalho Chehab 		dev_err(dev, "%s: dvb_dmxdev_init() failed: %d\n",
219*d6317c68SMauro Carvalho Chehab 			__func__, ret);
220*d6317c68SMauro Carvalho Chehab 		goto edmxdinit;
221*d6317c68SMauro Carvalho Chehab 	}
222*d6317c68SMauro Carvalho Chehab 
223*d6317c68SMauro Carvalho Chehab 	ret = as102_dvb_register_fe(as102_dev, &as102_dev->dvb_fe);
224*d6317c68SMauro Carvalho Chehab 	if (ret < 0) {
225*d6317c68SMauro Carvalho Chehab 		dev_err(dev, "%s: as102_dvb_register_frontend() failed: %d",
226*d6317c68SMauro Carvalho Chehab 		    __func__, ret);
227*d6317c68SMauro Carvalho Chehab 		goto efereg;
228*d6317c68SMauro Carvalho Chehab 	}
229*d6317c68SMauro Carvalho Chehab 
230*d6317c68SMauro Carvalho Chehab 	/* init bus mutex for token locking */
231*d6317c68SMauro Carvalho Chehab 	mutex_init(&as102_dev->bus_adap.lock);
232*d6317c68SMauro Carvalho Chehab 
233*d6317c68SMauro Carvalho Chehab 	/* init start / stop stream mutex */
234*d6317c68SMauro Carvalho Chehab 	mutex_init(&as102_dev->sem);
235*d6317c68SMauro Carvalho Chehab 
236*d6317c68SMauro Carvalho Chehab 	/*
237*d6317c68SMauro Carvalho Chehab 	 * try to load as102 firmware. If firmware upload failed, we'll be
238*d6317c68SMauro Carvalho Chehab 	 * able to upload it later.
239*d6317c68SMauro Carvalho Chehab 	 */
240*d6317c68SMauro Carvalho Chehab 	if (fw_upload)
241*d6317c68SMauro Carvalho Chehab 		try_then_request_module(as102_fw_upload(&as102_dev->bus_adap),
242*d6317c68SMauro Carvalho Chehab 				"firmware_class");
243*d6317c68SMauro Carvalho Chehab 
244*d6317c68SMauro Carvalho Chehab 	pr_info("Registered device %s", as102_dev->name);
245*d6317c68SMauro Carvalho Chehab 	return 0;
246*d6317c68SMauro Carvalho Chehab 
247*d6317c68SMauro Carvalho Chehab efereg:
248*d6317c68SMauro Carvalho Chehab 	dvb_dmxdev_release(&as102_dev->dvb_dmxdev);
249*d6317c68SMauro Carvalho Chehab edmxdinit:
250*d6317c68SMauro Carvalho Chehab 	dvb_dmx_release(&as102_dev->dvb_dmx);
251*d6317c68SMauro Carvalho Chehab edmxinit:
252*d6317c68SMauro Carvalho Chehab 	dvb_unregister_adapter(&as102_dev->dvb_adap);
253*d6317c68SMauro Carvalho Chehab 	return ret;
254*d6317c68SMauro Carvalho Chehab }
255*d6317c68SMauro Carvalho Chehab 
256*d6317c68SMauro Carvalho Chehab void as102_dvb_unregister(struct as102_dev_t *as102_dev)
257*d6317c68SMauro Carvalho Chehab {
258*d6317c68SMauro Carvalho Chehab 	/* unregister as102 frontend */
259*d6317c68SMauro Carvalho Chehab 	as102_dvb_unregister_fe(&as102_dev->dvb_fe);
260*d6317c68SMauro Carvalho Chehab 
261*d6317c68SMauro Carvalho Chehab 	/* unregister demux device */
262*d6317c68SMauro Carvalho Chehab 	dvb_dmxdev_release(&as102_dev->dvb_dmxdev);
263*d6317c68SMauro Carvalho Chehab 	dvb_dmx_release(&as102_dev->dvb_dmx);
264*d6317c68SMauro Carvalho Chehab 
265*d6317c68SMauro Carvalho Chehab 	/* unregister dvb adapter */
266*d6317c68SMauro Carvalho Chehab 	dvb_unregister_adapter(&as102_dev->dvb_adap);
267*d6317c68SMauro Carvalho Chehab 
268*d6317c68SMauro Carvalho Chehab 	pr_info("Unregistered device %s", as102_dev->name);
269*d6317c68SMauro Carvalho Chehab }
270*d6317c68SMauro Carvalho Chehab 
271*d6317c68SMauro Carvalho Chehab module_usb_driver(as102_usb_driver);
272*d6317c68SMauro Carvalho Chehab 
273*d6317c68SMauro Carvalho Chehab /* modinfo details */
274*d6317c68SMauro Carvalho Chehab MODULE_DESCRIPTION(DRIVER_FULL_NAME);
275*d6317c68SMauro Carvalho Chehab MODULE_LICENSE("GPL");
276*d6317c68SMauro Carvalho Chehab MODULE_AUTHOR("Pierrick Hascoet <pierrick.hascoet@abilis.com>");
277