media: move dvb kAPI headers to include/media
[linux-2.6-block.git] / drivers / media / usb / as102 / as102_drv.c
CommitLineData
41b44e04
PH
1/*
2 * Abilis Systems Single DVB-T Receiver
3 * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
c6ccdca9 4 * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com>
41b44e04
PH
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.
41b44e04
PH
15 */
16#include <linux/kernel.h>
17#include <linux/errno.h>
41b44e04
PH
18#include <linux/slab.h>
19#include <linux/module.h>
20#include <linux/mm.h>
21#include <linux/kref.h>
3288e201 22#include <linux/uaccess.h>
41b44e04
PH
23#include <linux/usb.h>
24
b47acf2a 25/* header file for usb device driver*/
41b44e04 26#include "as102_drv.h"
47f79129
MCC
27#include "as10x_cmd.h"
28#include "as102_fe.h"
41b44e04 29#include "as102_fw.h"
fada1935 30#include <media/dvbdev.h>
41b44e04 31
c6ccdca9 32int dual_tuner;
41b44e04 33module_param_named(dual_tuner, dual_tuner, int, 0644);
c6ccdca9 34MODULE_PARM_DESC(dual_tuner, "Activate Dual-Tuner config (default: off)");
41b44e04
PH
35
36static int fw_upload = 1;
37module_param_named(fw_upload, fw_upload, int, 0644);
38MODULE_PARM_DESC(fw_upload, "Turn on/off default FW upload (default: on)");
39
c6ccdca9 40static int pid_filtering;
41b44e04
PH
41module_param_named(pid_filtering, pid_filtering, int, 0644);
42MODULE_PARM_DESC(pid_filtering, "Activate HW PID filtering (default: off)");
43
c6ccdca9 44static int ts_auto_disable;
41b44e04
PH
45module_param_named(ts_auto_disable, ts_auto_disable, int, 0644);
46MODULE_PARM_DESC(ts_auto_disable, "Stream Auto Enable on FW (default: off)");
47
48int elna_enable = 1;
49module_param_named(elna_enable, elna_enable, int, 0644);
50MODULE_PARM_DESC(elna_enable, "Activate eLNA (default: on)");
51
41b44e04 52DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
41b44e04 53
c6ccdca9
DH
54static void as102_stop_stream(struct as102_dev_t *dev)
55{
34490a0a 56 struct as10x_bus_adapter_t *bus_adap;
41b44e04
PH
57
58 if (dev != NULL)
59 bus_adap = &dev->bus_adap;
60 else
61 return;
62
63 if (bus_adap->ops->stop_stream != NULL)
64 bus_adap->ops->stop_stream(dev);
65
66 if (ts_auto_disable) {
67 if (mutex_lock_interruptible(&dev->bus_adap.lock))
68 return;
69
c6ccdca9 70 if (as10x_cmd_stop_streaming(bus_adap) < 0)
2179de60
MK
71 dev_dbg(&dev->bus_adap.usb_dev->dev,
72 "as10x_cmd_stop_streaming failed\n");
41b44e04
PH
73
74 mutex_unlock(&dev->bus_adap.lock);
75 }
76}
77
c6ccdca9
DH
78static int as102_start_stream(struct as102_dev_t *dev)
79{
34490a0a 80 struct as10x_bus_adapter_t *bus_adap;
41b44e04
PH
81 int ret = -EFAULT;
82
83 if (dev != NULL)
84 bus_adap = &dev->bus_adap;
85 else
86 return ret;
87
c6ccdca9 88 if (bus_adap->ops->start_stream != NULL)
41b44e04 89 ret = bus_adap->ops->start_stream(dev);
41b44e04
PH
90
91 if (ts_auto_disable) {
92 if (mutex_lock_interruptible(&dev->bus_adap.lock))
93 return -EFAULT;
94
95 ret = as10x_cmd_start_streaming(bus_adap);
96
97 mutex_unlock(&dev->bus_adap.lock);
98 }
99
100 return ret;
101}
102
103static int as10x_pid_filter(struct as102_dev_t *dev,
104 int index, u16 pid, int onoff) {
105
34490a0a 106 struct as10x_bus_adapter_t *bus_adap = &dev->bus_adap;
41b44e04
PH
107 int ret = -EFAULT;
108
41b44e04 109 if (mutex_lock_interruptible(&dev->bus_adap.lock)) {
2179de60
MK
110 dev_dbg(&dev->bus_adap.usb_dev->dev,
111 "amutex_lock_interruptible(lock) failed !\n");
41b44e04
PH
112 return -EBUSY;
113 }
114
c6ccdca9
DH
115 switch (onoff) {
116 case 0:
14e0e4bf 117 ret = as10x_cmd_del_PID_filter(bus_adap, (uint16_t) pid);
2179de60
MK
118 dev_dbg(&dev->bus_adap.usb_dev->dev,
119 "DEL_PID_FILTER([%02d] 0x%04x) ret = %d\n",
14e0e4bf
SN
120 index, pid, ret);
121 break;
c6ccdca9
DH
122 case 1:
123 {
14e0e4bf 124 struct as10x_ts_filter filter;
c6ccdca9 125
14e0e4bf
SN
126 filter.type = TS_PID_TYPE_TS;
127 filter.idx = 0xFF;
128 filter.pid = pid;
c6ccdca9 129
14e0e4bf 130 ret = as10x_cmd_add_PID_filter(bus_adap, &filter);
2179de60 131 dev_dbg(&dev->bus_adap.usb_dev->dev,
f52e9828 132 "ADD_PID_FILTER([%02d -> %02d], 0x%04x) ret = %d\n",
14e0e4bf
SN
133 index, filter.idx, filter.pid, ret);
134 break;
c6ccdca9 135 }
41b44e04
PH
136 }
137
138 mutex_unlock(&dev->bus_adap.lock);
41b44e04
PH
139 return ret;
140}
141
c6ccdca9
DH
142static int as102_dvb_dmx_start_feed(struct dvb_demux_feed *dvbdmxfeed)
143{
41b44e04
PH
144 int ret = 0;
145 struct dvb_demux *demux = dvbdmxfeed->demux;
146 struct as102_dev_t *as102_dev = demux->priv;
147
41b44e04
PH
148 if (mutex_lock_interruptible(&as102_dev->sem))
149 return -ERESTARTSYS;
150
14e0e4bf
SN
151 if (pid_filtering)
152 as10x_pid_filter(as102_dev, dvbdmxfeed->index,
153 dvbdmxfeed->pid, 1);
41b44e04 154
c6ccdca9 155 if (as102_dev->streaming++ == 0)
41b44e04 156 ret = as102_start_stream(as102_dev);
41b44e04
PH
157
158 mutex_unlock(&as102_dev->sem);
41b44e04
PH
159 return ret;
160}
161
c6ccdca9
DH
162static int as102_dvb_dmx_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
163{
41b44e04
PH
164 struct dvb_demux *demux = dvbdmxfeed->demux;
165 struct as102_dev_t *as102_dev = demux->priv;
166
41b44e04
PH
167 if (mutex_lock_interruptible(&as102_dev->sem))
168 return -ERESTARTSYS;
169
c6ccdca9 170 if (--as102_dev->streaming == 0)
41b44e04 171 as102_stop_stream(as102_dev);
41b44e04 172
14e0e4bf
SN
173 if (pid_filtering)
174 as10x_pid_filter(as102_dev, dvbdmxfeed->index,
175 dvbdmxfeed->pid, 0);
41b44e04
PH
176
177 mutex_unlock(&as102_dev->sem);
41b44e04
PH
178 return 0;
179}
41b44e04 180
47f79129
MCC
181static int as102_set_tune(void *priv, struct as10x_tune_args *tune_args)
182{
183 struct as10x_bus_adapter_t *bus_adap = priv;
184 int ret;
185
186 /* Set frontend arguments */
187 if (mutex_lock_interruptible(&bus_adap->lock))
188 return -EBUSY;
189
190 ret = as10x_cmd_set_tune(bus_adap, tune_args);
191 if (ret != 0)
192 dev_dbg(&bus_adap->usb_dev->dev,
193 "as10x_cmd_set_tune failed. (err = %d)\n", ret);
194
195 mutex_unlock(&bus_adap->lock);
196
197 return ret;
198}
199
200static int as102_get_tps(void *priv, struct as10x_tps *tps)
201{
202 struct as10x_bus_adapter_t *bus_adap = priv;
203 int ret;
204
205 if (mutex_lock_interruptible(&bus_adap->lock))
206 return -EBUSY;
207
208 /* send abilis command: GET_TPS */
209 ret = as10x_cmd_get_tps(bus_adap, tps);
210
211 mutex_unlock(&bus_adap->lock);
212
213 return ret;
214}
215
216static int as102_get_status(void *priv, struct as10x_tune_status *tstate)
217{
218 struct as10x_bus_adapter_t *bus_adap = priv;
219 int ret;
220
221 if (mutex_lock_interruptible(&bus_adap->lock))
222 return -EBUSY;
223
224 /* send abilis command: GET_TUNE_STATUS */
225 ret = as10x_cmd_get_tune_status(bus_adap, tstate);
226 if (ret < 0) {
227 dev_dbg(&bus_adap->usb_dev->dev,
228 "as10x_cmd_get_tune_status failed (err = %d)\n",
229 ret);
230 }
231
232 mutex_unlock(&bus_adap->lock);
233
234 return ret;
235}
236
237static int as102_get_stats(void *priv, struct as10x_demod_stats *demod_stats)
238{
239 struct as10x_bus_adapter_t *bus_adap = priv;
240 int ret;
241
242 if (mutex_lock_interruptible(&bus_adap->lock))
243 return -EBUSY;
244
245 /* send abilis command: GET_TUNE_STATUS */
246 ret = as10x_cmd_get_demod_stats(bus_adap, demod_stats);
247 if (ret < 0) {
248 dev_dbg(&bus_adap->usb_dev->dev,
249 "as10x_cmd_get_demod_stats failed (probably not tuned)\n");
250 } else {
251 dev_dbg(&bus_adap->usb_dev->dev,
252 "demod status: fc: 0x%08x, bad fc: 0x%08x, bytes corrected: 0x%08x , MER: 0x%04x\n",
253 demod_stats->frame_count,
254 demod_stats->bad_frame_count,
255 demod_stats->bytes_fixed_by_rs,
256 demod_stats->mer);
257 }
258 mutex_unlock(&bus_adap->lock);
259
260 return ret;
261}
262
263static int as102_stream_ctrl(void *priv, int acquire, uint32_t elna_cfg)
264{
265 struct as10x_bus_adapter_t *bus_adap = priv;
266 int ret;
267
268 if (mutex_lock_interruptible(&bus_adap->lock))
269 return -EBUSY;
270
271 if (acquire) {
272 if (elna_enable)
273 as10x_cmd_set_context(bus_adap,
274 CONTEXT_LNA, elna_cfg);
275
276 ret = as10x_cmd_turn_on(bus_adap);
277 } else {
278 ret = as10x_cmd_turn_off(bus_adap);
279 }
280
281 mutex_unlock(&bus_adap->lock);
282
283 return ret;
284}
285
286static const struct as102_fe_ops as102_fe_ops = {
287 .set_tune = as102_set_tune,
288 .get_tps = as102_get_tps,
289 .get_status = as102_get_status,
290 .get_stats = as102_get_stats,
291 .stream_ctrl = as102_stream_ctrl,
292};
293
c6ccdca9
DH
294int as102_dvb_register(struct as102_dev_t *as102_dev)
295{
88010289
SN
296 struct device *dev = &as102_dev->bus_adap.usb_dev->dev;
297 int ret;
41b44e04 298
41b44e04 299 ret = dvb_register_adapter(&as102_dev->dvb_adap,
88010289
SN
300 as102_dev->name, THIS_MODULE,
301 dev, adapter_nr);
41b44e04 302 if (ret < 0) {
88010289
SN
303 dev_err(dev, "%s: dvb_register_adapter() failed: %d\n",
304 __func__, ret);
305 return ret;
41b44e04
PH
306 }
307
308 as102_dev->dvb_dmx.priv = as102_dev;
309 as102_dev->dvb_dmx.filternum = pid_filtering ? 16 : 256;
310 as102_dev->dvb_dmx.feednum = 256;
311 as102_dev->dvb_dmx.start_feed = as102_dvb_dmx_start_feed;
312 as102_dev->dvb_dmx.stop_feed = as102_dvb_dmx_stop_feed;
313
314 as102_dev->dvb_dmx.dmx.capabilities = DMX_TS_FILTERING |
315 DMX_SECTION_FILTERING;
316
317 as102_dev->dvb_dmxdev.filternum = as102_dev->dvb_dmx.filternum;
318 as102_dev->dvb_dmxdev.demux = &as102_dev->dvb_dmx.dmx;
319 as102_dev->dvb_dmxdev.capabilities = 0;
320
c6ccdca9
DH
321 ret = dvb_dmx_init(&as102_dev->dvb_dmx);
322 if (ret < 0) {
1ec9a35e 323 dev_err(dev, "%s: dvb_dmx_init() failed: %d\n", __func__, ret);
88010289 324 goto edmxinit;
41b44e04
PH
325 }
326
327 ret = dvb_dmxdev_init(&as102_dev->dvb_dmxdev, &as102_dev->dvb_adap);
328 if (ret < 0) {
88010289
SN
329 dev_err(dev, "%s: dvb_dmxdev_init() failed: %d\n",
330 __func__, ret);
331 goto edmxdinit;
41b44e04
PH
332 }
333
b601d9a5
MCC
334 /* Attach the frontend */
335 as102_dev->dvb_fe = dvb_attach(as102_attach, as102_dev->name,
47f79129 336 &as102_fe_ops,
b601d9a5
MCC
337 &as102_dev->bus_adap,
338 as102_dev->elna_cfg);
339 if (!as102_dev->dvb_fe) {
bfe40b79 340 ret = -ENODEV;
b601d9a5
MCC
341 dev_err(dev, "%s: as102_attach() failed: %d",
342 __func__, ret);
343 goto efereg;
344 }
345
346 ret = dvb_register_frontend(&as102_dev->dvb_adap, as102_dev->dvb_fe);
41b44e04 347 if (ret < 0) {
88010289 348 dev_err(dev, "%s: as102_dvb_register_frontend() failed: %d",
c6ccdca9 349 __func__, ret);
88010289 350 goto efereg;
41b44e04 351 }
41b44e04
PH
352
353 /* init bus mutex for token locking */
354 mutex_init(&as102_dev->bus_adap.lock);
355
356 /* init start / stop stream mutex */
357 mutex_init(&as102_dev->sem);
358
41b44e04
PH
359 /*
360 * try to load as102 firmware. If firmware upload failed, we'll be
361 * able to upload it later.
362 */
363 if (fw_upload)
364 try_then_request_module(as102_fw_upload(&as102_dev->bus_adap),
365 "firmware_class");
88010289
SN
366
367 pr_info("Registered device %s", as102_dev->name);
368 return 0;
369
370efereg:
371 dvb_dmxdev_release(&as102_dev->dvb_dmxdev);
372edmxdinit:
373 dvb_dmx_release(&as102_dev->dvb_dmx);
374edmxinit:
375 dvb_unregister_adapter(&as102_dev->dvb_adap);
41b44e04
PH
376 return ret;
377}
378
c6ccdca9
DH
379void as102_dvb_unregister(struct as102_dev_t *as102_dev)
380{
41b44e04 381 /* unregister as102 frontend */
b601d9a5
MCC
382 dvb_unregister_frontend(as102_dev->dvb_fe);
383
384 /* detach frontend */
385 dvb_frontend_detach(as102_dev->dvb_fe);
41b44e04
PH
386
387 /* unregister demux device */
388 dvb_dmxdev_release(&as102_dev->dvb_dmxdev);
389 dvb_dmx_release(&as102_dev->dvb_dmx);
390
391 /* unregister dvb adapter */
392 dvb_unregister_adapter(&as102_dev->dvb_adap);
ff7029f5 393
88010289 394 pr_info("Unregistered device %s", as102_dev->name);
41b44e04
PH
395}
396
9451df0e 397module_usb_driver(as102_usb_driver);
41b44e04 398
41b44e04
PH
399/* modinfo details */
400MODULE_DESCRIPTION(DRIVER_FULL_NAME);
401MODULE_LICENSE("GPL");
402MODULE_AUTHOR("Pierrick Hascoet <pierrick.hascoet@abilis.com>");