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