c334bffa36e4a30e78b8e81ba6b0e2d714f68d64
[pandora-kernel.git] / drivers / staging / media / as102 / as102_drv.c
1 /*
2  * Abilis Systems Single DVB-T Receiver
3  * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 #include <linux/kernel.h>
20 #include <linux/errno.h>
21 #include <linux/init.h>
22 #include <linux/slab.h>
23 #include <linux/module.h>
24 #include <linux/mm.h>
25 #include <linux/kref.h>
26 #include <asm/uaccess.h>
27 #include <linux/usb.h>
28
29 /* header file for Usb device driver*/
30 #include "as102_drv.h"
31 #include "as102_fw.h"
32
33 #if defined(CONFIG_DVB_CORE) || defined(CONFIG_DVB_CORE_MODULE)
34 #include "dvbdev.h"
35 #else
36 #warning >>> DVB_CORE not defined !!! <<<
37 #endif
38
39 int debug = 0;
40 module_param_named(debug, debug, int, 0644);
41 MODULE_PARM_DESC(debug, "Turn on/off debugging (default: off)");
42
43 int dual_tuner = 0;
44 module_param_named(dual_tuner, dual_tuner, int, 0644);
45 MODULE_PARM_DESC(dual_tuner, "Activate Dual-Tuner configuration (default: off)");
46
47 static int fw_upload = 1;
48 module_param_named(fw_upload, fw_upload, int, 0644);
49 MODULE_PARM_DESC(fw_upload, "Turn on/off default FW upload (default: on)");
50
51 static int pid_filtering = 0;
52 module_param_named(pid_filtering, pid_filtering, int, 0644);
53 MODULE_PARM_DESC(pid_filtering, "Activate HW PID filtering (default: off)");
54
55 static int ts_auto_disable = 0;
56 module_param_named(ts_auto_disable, ts_auto_disable, int, 0644);
57 MODULE_PARM_DESC(ts_auto_disable, "Stream Auto Enable on FW (default: off)");
58
59 int elna_enable = 1;
60 module_param_named(elna_enable, elna_enable, int, 0644);
61 MODULE_PARM_DESC(elna_enable, "Activate eLNA (default: on)");
62
63 #ifdef DVB_DEFINE_MOD_OPT_ADAPTER_NR
64 DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
65 #endif
66
67 #if defined(CONFIG_DVB_CORE) || defined(CONFIG_DVB_CORE_MODULE)
68 static void as102_stop_stream(struct as102_dev_t *dev) {
69         struct as102_bus_adapter_t *bus_adap;
70
71         if (dev != NULL)
72                 bus_adap = &dev->bus_adap;
73         else
74                 return;
75
76         if (bus_adap->ops->stop_stream != NULL)
77                 bus_adap->ops->stop_stream(dev);
78
79         if (ts_auto_disable) {
80                 if (mutex_lock_interruptible(&dev->bus_adap.lock))
81                         return;
82
83                 if (as10x_cmd_stop_streaming(bus_adap) < 0) {
84                         dprintk(debug, "as10x_cmd_stop_streaming failed\n");
85                 }
86
87                 mutex_unlock(&dev->bus_adap.lock);
88         }
89 }
90
91 static int as102_start_stream(struct as102_dev_t *dev) {
92
93         struct as102_bus_adapter_t *bus_adap;
94         int ret = -EFAULT;
95
96         if (dev != NULL)
97                 bus_adap = &dev->bus_adap;
98         else
99                 return ret;
100
101         if (bus_adap->ops->start_stream != NULL) {
102                 ret = bus_adap->ops->start_stream(dev);
103         }
104
105         if (ts_auto_disable) {
106                 if (mutex_lock_interruptible(&dev->bus_adap.lock))
107                         return -EFAULT;
108
109                 ret = as10x_cmd_start_streaming(bus_adap);
110
111                 mutex_unlock(&dev->bus_adap.lock);
112         }
113
114         return ret;
115 }
116
117 static int as10x_pid_filter(struct as102_dev_t *dev,
118                             int index, u16 pid, int onoff) {
119
120         struct as102_bus_adapter_t *bus_adap = &dev->bus_adap;
121         int ret = -EFAULT;
122
123         ENTER();
124
125         if (mutex_lock_interruptible(&dev->bus_adap.lock)) {
126                 dprintk(debug, "mutex_lock_interruptible(lock) failed !\n");
127                 return -EBUSY;
128         }
129
130         switch(onoff) {
131                 case 0:
132                         ret = as10x_cmd_del_PID_filter(bus_adap, (uint16_t) pid);
133                         dprintk(debug, "DEL_PID_FILTER([%02d] 0x%04x) ret = %d\n",
134                                         index, pid, ret);
135                         break;
136                 case 1:
137                 {
138                         struct as10x_ts_filter filter;
139
140                         filter.type = TS_PID_TYPE_TS;
141                         filter.idx = 0xFF;
142                         filter.pid = pid;
143
144                         ret = as10x_cmd_add_PID_filter(bus_adap, &filter);
145                         dprintk(debug, "ADD_PID_FILTER([%02d -> %02d], 0x%04x) ret = %d\n",
146                                         index, filter.idx, filter.pid, ret);
147                         break;
148                 }
149         }
150
151         mutex_unlock(&dev->bus_adap.lock);
152
153         LEAVE();
154         return ret;
155 }
156
157 static int as102_dvb_dmx_start_feed(struct dvb_demux_feed *dvbdmxfeed) {
158         int ret = 0;
159         struct dvb_demux *demux = dvbdmxfeed->demux;
160         struct as102_dev_t *as102_dev = demux->priv;
161
162         ENTER();
163
164         if (mutex_lock_interruptible(&as102_dev->sem))
165                 return -ERESTARTSYS;
166
167         if (pid_filtering) {
168                 as10x_pid_filter(as102_dev,
169                                 dvbdmxfeed->index, dvbdmxfeed->pid, 1);
170         }
171
172         if (as102_dev->streaming++ == 0) {
173                 ret = as102_start_stream(as102_dev);
174         }
175
176         mutex_unlock(&as102_dev->sem);
177         LEAVE();
178         return ret;
179 }
180
181 static int as102_dvb_dmx_stop_feed(struct dvb_demux_feed *dvbdmxfeed) {
182         struct dvb_demux *demux = dvbdmxfeed->demux;
183         struct as102_dev_t *as102_dev = demux->priv;
184
185         ENTER();
186
187         if (mutex_lock_interruptible(&as102_dev->sem))
188                 return -ERESTARTSYS;
189
190         if (--as102_dev->streaming == 0) {
191                 as102_stop_stream(as102_dev);
192         }
193
194         if (pid_filtering) {
195                 as10x_pid_filter(as102_dev,
196                                 dvbdmxfeed->index, dvbdmxfeed->pid, 0);
197         }
198
199         mutex_unlock(&as102_dev->sem);
200         LEAVE();
201         return 0;
202 }
203 #endif
204
205 int as102_dvb_register(struct as102_dev_t *as102_dev) {
206         int ret = 0;
207         ENTER();
208
209 #if defined(CONFIG_DVB_CORE) || defined(CONFIG_DVB_CORE_MODULE)
210         ret = dvb_register_adapter(&as102_dev->dvb_adap,
211                                    DEVICE_FULL_NAME,
212                                    THIS_MODULE,
213 #if defined(CONFIG_AS102_USB)
214                                    &as102_dev->bus_adap.usb_dev->dev
215 #elif defined(CONFIG_AS102_SPI)
216                                    &as102_dev->bus_adap.spi_dev->dev
217 #else
218 #error >>> dvb_register_adapter <<<
219 #endif
220 #ifdef DVB_DEFINE_MOD_OPT_ADAPTER_NR
221                                    , adapter_nr
222 #endif
223                                    );
224         if (ret < 0) {
225                 err("%s: dvb_register_adapter() failed (errno = %d)",
226                     __FUNCTION__, ret);
227                 goto failed;
228         }
229
230         as102_dev->dvb_dmx.priv = as102_dev;
231         as102_dev->dvb_dmx.filternum = pid_filtering ? 16 : 256;
232         as102_dev->dvb_dmx.feednum = 256;
233         as102_dev->dvb_dmx.start_feed = as102_dvb_dmx_start_feed;
234         as102_dev->dvb_dmx.stop_feed = as102_dvb_dmx_stop_feed;
235
236         as102_dev->dvb_dmx.dmx.capabilities = DMX_TS_FILTERING |
237                                               DMX_SECTION_FILTERING;
238
239         as102_dev->dvb_dmxdev.filternum = as102_dev->dvb_dmx.filternum;
240         as102_dev->dvb_dmxdev.demux = &as102_dev->dvb_dmx.dmx;
241         as102_dev->dvb_dmxdev.capabilities = 0;
242
243         if ((ret = dvb_dmx_init(&as102_dev->dvb_dmx)) < 0) {
244                 err("%s: dvb_dmx_init() failed (errno = %d)",
245                     __FUNCTION__, ret);
246                 goto failed;
247         }
248
249         ret = dvb_dmxdev_init(&as102_dev->dvb_dmxdev, &as102_dev->dvb_adap);
250         if (ret < 0) {
251                 err("%s: dvb_dmxdev_init() failed (errno = %d)",
252                     __FUNCTION__, ret);
253                 goto failed;
254         }
255
256         ret = as102_dvb_register_fe(as102_dev, &as102_dev->dvb_fe);
257         if (ret < 0) {
258                 err("%s: as102_dvb_register_frontend() failed (errno = %d)",
259                     __FUNCTION__, ret);
260                 goto failed;
261         }
262 #endif
263
264         /* init bus mutex for token locking */
265         mutex_init(&as102_dev->bus_adap.lock);
266
267         /* init start / stop stream mutex */
268         mutex_init(&as102_dev->sem);
269
270 #if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE)
271         /*
272          * try to load as102 firmware. If firmware upload failed, we'll be
273          * able to upload it later.
274          */
275         if (fw_upload)
276                 try_then_request_module(as102_fw_upload(&as102_dev->bus_adap),
277                                 "firmware_class");
278 #endif
279
280 failed:
281         LEAVE();
282         /* FIXME: free dvb_XXX */
283         return ret;
284 }
285
286 void as102_dvb_unregister(struct as102_dev_t *as102_dev) {
287         ENTER();
288
289 #if defined(CONFIG_DVB_CORE) || defined(CONFIG_DVB_CORE_MODULE)
290         /* unregister as102 frontend */
291         as102_dvb_unregister_fe(&as102_dev->dvb_fe);
292
293         /* unregister demux device */
294         dvb_dmxdev_release(&as102_dev->dvb_dmxdev);
295         dvb_dmx_release(&as102_dev->dvb_dmx);
296
297         /* unregister dvb adapter */
298         dvb_unregister_adapter(&as102_dev->dvb_adap);
299 #endif
300         LEAVE();
301 }
302
303 static int __init as102_driver_init(void) {
304         int ret = 0;
305
306         ENTER();
307
308         /* register this driver with the low level subsystem */
309 #if defined(CONFIG_AS102_USB)
310         ret = usb_register(&as102_usb_driver);
311         if (ret)
312                 err("usb_register failed (ret = %d)", ret);
313 #endif
314 #if defined(CONFIG_AS102_SPI)
315         ret = spi_register_driver(&as102_spi_driver);
316         if (ret)
317                 printk(KERN_ERR "spi_register failed (ret = %d)", ret);
318 #endif
319
320         LEAVE();
321         return ret;
322 }
323
324 /*
325  * Mandatory function : Adds a special section to the module indicating
326  * where initialisation function is defined
327  */
328 module_init(as102_driver_init);
329
330 /**
331  * \brief as102 driver exit point. This function is called when device has
332  *       to be removed.
333  */
334 static void __exit as102_driver_exit(void) {
335         ENTER();
336         /* deregister this driver with the low level bus subsystem */
337 #if defined(CONFIG_AS102_USB)
338         usb_deregister(&as102_usb_driver);
339 #endif
340 #if defined(CONFIG_AS102_SPI)
341         spi_unregister_driver(&as102_spi_driver);
342 #endif
343         LEAVE();
344 }
345
346 /*
347  * required function for unload: Adds a special section to the module
348  * indicating where unload function is defined
349  */
350 module_exit(as102_driver_exit);
351 /* modinfo details */
352 MODULE_DESCRIPTION(DRIVER_FULL_NAME);
353 MODULE_LICENSE("GPL");
354 MODULE_AUTHOR("Pierrick Hascoet <pierrick.hascoet@abilis.com>");
355
356 /* EOF - vim: set textwidth=80 ts=8 sw=8 sts=8 noet: */