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