1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2010-2014 Michael Krufky (mkrufky@linuxtv.org) |
4 | * |
5 | * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information |
6 | */ |
7 | |
8 | #include <linux/vmalloc.h> |
9 | #include <linux/i2c.h> |
10 | #include <media/tuner.h> |
11 | |
12 | #include "mxl111sf.h" |
13 | #include "mxl111sf-reg.h" |
14 | #include "mxl111sf-phy.h" |
15 | #include "mxl111sf-i2c.h" |
16 | #include "mxl111sf-gpio.h" |
17 | |
18 | #include "mxl111sf-demod.h" |
19 | #include "mxl111sf-tuner.h" |
20 | |
21 | #include "lgdt3305.h" |
22 | #include "lg2160.h" |
23 | |
24 | int dvb_usb_mxl111sf_debug; |
25 | module_param_named(debug, dvb_usb_mxl111sf_debug, int, 0644); |
26 | MODULE_PARM_DESC(debug, "set debugging level (1=info, 2=xfer, 4=i2c, 8=reg, 16=adv (or-able))." ); |
27 | |
28 | static int dvb_usb_mxl111sf_isoc; |
29 | module_param_named(isoc, dvb_usb_mxl111sf_isoc, int, 0644); |
30 | MODULE_PARM_DESC(isoc, "enable usb isoc xfer (0=bulk, 1=isoc)." ); |
31 | |
32 | static int dvb_usb_mxl111sf_spi; |
33 | module_param_named(spi, dvb_usb_mxl111sf_spi, int, 0644); |
34 | MODULE_PARM_DESC(spi, "use spi rather than tp for data xfer (0=tp, 1=spi)." ); |
35 | |
36 | #define ANT_PATH_AUTO 0 |
37 | #define ANT_PATH_EXTERNAL 1 |
38 | #define ANT_PATH_INTERNAL 2 |
39 | |
40 | static int dvb_usb_mxl111sf_rfswitch = |
41 | #if 0 |
42 | ANT_PATH_AUTO; |
43 | #else |
44 | ANT_PATH_EXTERNAL; |
45 | #endif |
46 | |
47 | module_param_named(rfswitch, dvb_usb_mxl111sf_rfswitch, int, 0644); |
48 | MODULE_PARM_DESC(rfswitch, "force rf switch position (0=auto, 1=ext, 2=int)." ); |
49 | |
50 | DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); |
51 | |
52 | int mxl111sf_ctrl_msg(struct mxl111sf_state *state, |
53 | u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen) |
54 | { |
55 | struct dvb_usb_device *d = state->d; |
56 | int wo = (rbuf == NULL || rlen == 0); /* write-only */ |
57 | int ret; |
58 | |
59 | if (1 + wlen > MXL_MAX_XFER_SIZE) { |
60 | pr_warn("%s: len=%d is too big!\n" , __func__, wlen); |
61 | return -EOPNOTSUPP; |
62 | } |
63 | |
64 | pr_debug("%s(wlen = %d, rlen = %d)\n" , __func__, wlen, rlen); |
65 | |
66 | mutex_lock(&state->msg_lock); |
67 | memset(state->sndbuf, 0, 1+wlen); |
68 | memset(state->rcvbuf, 0, rlen); |
69 | |
70 | state->sndbuf[0] = cmd; |
71 | memcpy(&state->sndbuf[1], wbuf, wlen); |
72 | |
73 | ret = (wo) ? dvb_usbv2_generic_write(d, state->sndbuf, 1+wlen) : |
74 | dvb_usbv2_generic_rw(d, state->sndbuf, 1+wlen, state->rcvbuf, |
75 | rlen); |
76 | |
77 | if (rbuf) |
78 | memcpy(rbuf, state->rcvbuf, rlen); |
79 | |
80 | mutex_unlock(lock: &state->msg_lock); |
81 | |
82 | mxl_fail(ret); |
83 | |
84 | return ret; |
85 | } |
86 | |
87 | /* ------------------------------------------------------------------------ */ |
88 | |
89 | #define MXL_CMD_REG_READ 0xaa |
90 | #define MXL_CMD_REG_WRITE 0x55 |
91 | |
92 | int mxl111sf_read_reg(struct mxl111sf_state *state, u8 addr, u8 *data) |
93 | { |
94 | u8 buf[2]; |
95 | int ret; |
96 | |
97 | ret = mxl111sf_ctrl_msg(state, MXL_CMD_REG_READ, wbuf: &addr, wlen: 1, rbuf: buf, rlen: 2); |
98 | if (mxl_fail(ret)) { |
99 | mxl_debug("error reading reg: 0x%02x" , addr); |
100 | goto fail; |
101 | } |
102 | |
103 | if (buf[0] == addr) |
104 | *data = buf[1]; |
105 | else { |
106 | pr_err("invalid response reading reg: 0x%02x != 0x%02x, 0x%02x" , |
107 | addr, buf[0], buf[1]); |
108 | ret = -EINVAL; |
109 | } |
110 | |
111 | pr_debug("R: (0x%02x, 0x%02x)\n" , addr, buf[1]); |
112 | fail: |
113 | return ret; |
114 | } |
115 | |
116 | int mxl111sf_write_reg(struct mxl111sf_state *state, u8 addr, u8 data) |
117 | { |
118 | u8 buf[] = { addr, data }; |
119 | int ret; |
120 | |
121 | pr_debug("W: (0x%02x, 0x%02x)\n" , addr, data); |
122 | |
123 | ret = mxl111sf_ctrl_msg(state, MXL_CMD_REG_WRITE, wbuf: buf, wlen: 2, NULL, rlen: 0); |
124 | if (mxl_fail(ret)) |
125 | pr_err("error writing reg: 0x%02x, val: 0x%02x" , addr, data); |
126 | return ret; |
127 | } |
128 | |
129 | /* ------------------------------------------------------------------------ */ |
130 | |
131 | int mxl111sf_write_reg_mask(struct mxl111sf_state *state, |
132 | u8 addr, u8 mask, u8 data) |
133 | { |
134 | int ret; |
135 | u8 val = 0; |
136 | |
137 | if (mask != 0xff) { |
138 | ret = mxl111sf_read_reg(state, addr, data: &val); |
139 | #if 1 |
140 | /* don't know why this usually errors out on the first try */ |
141 | if (mxl_fail(ret)) |
142 | pr_err("error writing addr: 0x%02x, mask: 0x%02x, data: 0x%02x, retrying..." , |
143 | addr, mask, data); |
144 | |
145 | ret = mxl111sf_read_reg(state, addr, data: &val); |
146 | #endif |
147 | if (mxl_fail(ret)) |
148 | goto fail; |
149 | } |
150 | val &= ~mask; |
151 | val |= data; |
152 | |
153 | ret = mxl111sf_write_reg(state, addr, data: val); |
154 | mxl_fail(ret); |
155 | fail: |
156 | return ret; |
157 | } |
158 | |
159 | /* ------------------------------------------------------------------------ */ |
160 | |
161 | int mxl111sf_ctrl_program_regs(struct mxl111sf_state *state, |
162 | struct mxl111sf_reg_ctrl_info *ctrl_reg_info) |
163 | { |
164 | int i, ret = 0; |
165 | |
166 | for (i = 0; ctrl_reg_info[i].addr | |
167 | ctrl_reg_info[i].mask | |
168 | ctrl_reg_info[i].data; i++) { |
169 | |
170 | ret = mxl111sf_write_reg_mask(state, |
171 | addr: ctrl_reg_info[i].addr, |
172 | mask: ctrl_reg_info[i].mask, |
173 | data: ctrl_reg_info[i].data); |
174 | if (mxl_fail(ret)) { |
175 | pr_err("failed on reg #%d (0x%02x)" , i, |
176 | ctrl_reg_info[i].addr); |
177 | break; |
178 | } |
179 | } |
180 | return ret; |
181 | } |
182 | |
183 | /* ------------------------------------------------------------------------ */ |
184 | |
185 | static int mxl1x1sf_get_chip_info(struct mxl111sf_state *state) |
186 | { |
187 | int ret; |
188 | u8 id, ver; |
189 | char *mxl_chip, *mxl_rev; |
190 | |
191 | if ((state->chip_id) && (state->chip_ver)) |
192 | return 0; |
193 | |
194 | ret = mxl111sf_read_reg(state, CHIP_ID_REG, data: &id); |
195 | if (mxl_fail(ret)) |
196 | goto fail; |
197 | state->chip_id = id; |
198 | |
199 | ret = mxl111sf_read_reg(state, TOP_CHIP_REV_ID_REG, data: &ver); |
200 | if (mxl_fail(ret)) |
201 | goto fail; |
202 | state->chip_ver = ver; |
203 | |
204 | switch (id) { |
205 | case 0x61: |
206 | mxl_chip = "MxL101SF" ; |
207 | break; |
208 | case 0x63: |
209 | mxl_chip = "MxL111SF" ; |
210 | break; |
211 | default: |
212 | mxl_chip = "UNKNOWN MxL1X1" ; |
213 | break; |
214 | } |
215 | switch (ver) { |
216 | case 0x36: |
217 | state->chip_rev = MXL111SF_V6; |
218 | mxl_rev = "v6" ; |
219 | break; |
220 | case 0x08: |
221 | state->chip_rev = MXL111SF_V8_100; |
222 | mxl_rev = "v8_100" ; |
223 | break; |
224 | case 0x18: |
225 | state->chip_rev = MXL111SF_V8_200; |
226 | mxl_rev = "v8_200" ; |
227 | break; |
228 | default: |
229 | state->chip_rev = 0; |
230 | mxl_rev = "UNKNOWN REVISION" ; |
231 | break; |
232 | } |
233 | pr_info("%s detected, %s (0x%x)" , mxl_chip, mxl_rev, ver); |
234 | fail: |
235 | return ret; |
236 | } |
237 | |
238 | #define get_chip_info(state) \ |
239 | ({ \ |
240 | int ___ret; \ |
241 | ___ret = mxl1x1sf_get_chip_info(state); \ |
242 | if (mxl_fail(___ret)) { \ |
243 | mxl_debug("failed to get chip info" \ |
244 | " on first probe attempt"); \ |
245 | ___ret = mxl1x1sf_get_chip_info(state); \ |
246 | if (mxl_fail(___ret)) \ |
247 | pr_err("failed to get chip info during probe"); \ |
248 | else \ |
249 | mxl_debug("probe needed a retry " \ |
250 | "in order to succeed."); \ |
251 | } \ |
252 | ___ret; \ |
253 | }) |
254 | |
255 | /* ------------------------------------------------------------------------ */ |
256 | #if 0 |
257 | static int mxl111sf_power_ctrl(struct dvb_usb_device *d, int onoff) |
258 | { |
259 | /* power control depends on which adapter is being woken: |
260 | * save this for init, instead, via mxl111sf_adap_fe_init */ |
261 | return 0; |
262 | } |
263 | #endif |
264 | |
265 | static int mxl111sf_adap_fe_init(struct dvb_frontend *fe) |
266 | { |
267 | struct dvb_usb_device *d = fe_to_d(fe); |
268 | struct mxl111sf_state *state = fe_to_priv(fe); |
269 | struct mxl111sf_adap_state *adap_state = &state->adap_state[fe->id]; |
270 | int err; |
271 | |
272 | /* exit if we didn't initialize the driver yet */ |
273 | if (!state->chip_id) { |
274 | mxl_debug("driver not yet initialized, exit." ); |
275 | goto fail; |
276 | } |
277 | |
278 | pr_debug("%s()\n" , __func__); |
279 | |
280 | mutex_lock(&state->fe_lock); |
281 | |
282 | state->alt_mode = adap_state->alt_mode; |
283 | |
284 | if (usb_set_interface(dev: d->udev, ifnum: 0, alternate: state->alt_mode) < 0) |
285 | pr_err("set interface failed" ); |
286 | |
287 | err = mxl1x1sf_soft_reset(state); |
288 | mxl_fail(err); |
289 | err = mxl111sf_init_tuner_demod(state); |
290 | mxl_fail(err); |
291 | err = mxl1x1sf_set_device_mode(state, mode: adap_state->device_mode); |
292 | |
293 | mxl_fail(err); |
294 | err = mxl111sf_enable_usb_output(state); |
295 | mxl_fail(err); |
296 | err = mxl1x1sf_top_master_ctrl(state, onoff: 1); |
297 | mxl_fail(err); |
298 | |
299 | if ((MXL111SF_GPIO_MOD_DVBT != adap_state->gpio_mode) && |
300 | (state->chip_rev > MXL111SF_V6)) { |
301 | mxl111sf_config_pin_mux_modes(state, |
302 | pin_mux_config: PIN_MUX_TS_SPI_IN_MODE_1); |
303 | mxl_fail(err); |
304 | } |
305 | err = mxl111sf_init_port_expander(state); |
306 | if (!mxl_fail(err)) { |
307 | state->gpio_mode = adap_state->gpio_mode; |
308 | err = mxl111sf_gpio_mode_switch(state, mode: state->gpio_mode); |
309 | mxl_fail(err); |
310 | #if 0 |
311 | err = fe->ops.init(fe); |
312 | #endif |
313 | msleep(msecs: 100); /* add short delay after enabling |
314 | * the demod before touching it */ |
315 | } |
316 | |
317 | return (adap_state->fe_init) ? adap_state->fe_init(fe) : 0; |
318 | fail: |
319 | return -ENODEV; |
320 | } |
321 | |
322 | static int mxl111sf_adap_fe_sleep(struct dvb_frontend *fe) |
323 | { |
324 | struct mxl111sf_state *state = fe_to_priv(fe); |
325 | struct mxl111sf_adap_state *adap_state = &state->adap_state[fe->id]; |
326 | int err; |
327 | |
328 | /* exit if we didn't initialize the driver yet */ |
329 | if (!state->chip_id) { |
330 | mxl_debug("driver not yet initialized, exit." ); |
331 | goto fail; |
332 | } |
333 | |
334 | pr_debug("%s()\n" , __func__); |
335 | |
336 | err = (adap_state->fe_sleep) ? adap_state->fe_sleep(fe) : 0; |
337 | |
338 | mutex_unlock(lock: &state->fe_lock); |
339 | |
340 | return err; |
341 | fail: |
342 | return -ENODEV; |
343 | } |
344 | |
345 | |
346 | static int mxl111sf_ep6_streaming_ctrl(struct dvb_frontend *fe, int onoff) |
347 | { |
348 | struct mxl111sf_state *state = fe_to_priv(fe); |
349 | struct mxl111sf_adap_state *adap_state = &state->adap_state[fe->id]; |
350 | int ret = 0; |
351 | |
352 | pr_debug("%s(%d)\n" , __func__, onoff); |
353 | |
354 | if (onoff) { |
355 | ret = mxl111sf_enable_usb_output(state); |
356 | mxl_fail(ret); |
357 | ret = mxl111sf_config_mpeg_in(state, parallel_serial: 1, msb_lsb_1st: 1, |
358 | clock_phase: adap_state->ep6_clockphase, |
359 | mpeg_valid_pol: 0, mpeg_sync_pol: 0); |
360 | mxl_fail(ret); |
361 | #if 0 |
362 | } else { |
363 | ret = mxl111sf_disable_656_port(state); |
364 | mxl_fail(ret); |
365 | #endif |
366 | } |
367 | |
368 | return ret; |
369 | } |
370 | |
371 | static int mxl111sf_ep5_streaming_ctrl(struct dvb_frontend *fe, int onoff) |
372 | { |
373 | struct mxl111sf_state *state = fe_to_priv(fe); |
374 | int ret = 0; |
375 | |
376 | pr_debug("%s(%d)\n" , __func__, onoff); |
377 | |
378 | if (onoff) { |
379 | ret = mxl111sf_enable_usb_output(state); |
380 | mxl_fail(ret); |
381 | |
382 | ret = mxl111sf_init_i2s_port(state, sample_size: 200); |
383 | mxl_fail(ret); |
384 | ret = mxl111sf_config_i2s(state, msb_start_pos: 0, data_width: 15); |
385 | mxl_fail(ret); |
386 | } else { |
387 | ret = mxl111sf_disable_i2s_port(state); |
388 | mxl_fail(ret); |
389 | } |
390 | if (state->chip_rev > MXL111SF_V6) |
391 | ret = mxl111sf_config_spi(state, onoff); |
392 | mxl_fail(ret); |
393 | |
394 | return ret; |
395 | } |
396 | |
397 | static int mxl111sf_ep4_streaming_ctrl(struct dvb_frontend *fe, int onoff) |
398 | { |
399 | struct mxl111sf_state *state = fe_to_priv(fe); |
400 | int ret = 0; |
401 | |
402 | pr_debug("%s(%d)\n" , __func__, onoff); |
403 | |
404 | if (onoff) { |
405 | ret = mxl111sf_enable_usb_output(state); |
406 | mxl_fail(ret); |
407 | } |
408 | |
409 | return ret; |
410 | } |
411 | |
412 | /* ------------------------------------------------------------------------ */ |
413 | |
414 | static struct lgdt3305_config hauppauge_lgdt3305_config = { |
415 | .i2c_addr = 0xb2 >> 1, |
416 | .mpeg_mode = LGDT3305_MPEG_SERIAL, |
417 | .tpclk_edge = LGDT3305_TPCLK_RISING_EDGE, |
418 | .tpvalid_polarity = LGDT3305_TP_VALID_HIGH, |
419 | .deny_i2c_rptr = 1, |
420 | .spectral_inversion = 0, |
421 | .qam_if_khz = 6000, |
422 | .vsb_if_khz = 6000, |
423 | }; |
424 | |
425 | static int mxl111sf_lgdt3305_frontend_attach(struct dvb_usb_adapter *adap, u8 fe_id) |
426 | { |
427 | struct dvb_usb_device *d = adap_to_d(adap); |
428 | struct mxl111sf_state *state = d_to_priv(d); |
429 | struct mxl111sf_adap_state *adap_state = &state->adap_state[fe_id]; |
430 | int ret; |
431 | |
432 | pr_debug("%s()\n" , __func__); |
433 | |
434 | /* save a pointer to the dvb_usb_device in device state */ |
435 | state->d = d; |
436 | adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 2 : 1; |
437 | state->alt_mode = adap_state->alt_mode; |
438 | |
439 | if (usb_set_interface(dev: d->udev, ifnum: 0, alternate: state->alt_mode) < 0) |
440 | pr_err("set interface failed" ); |
441 | |
442 | state->gpio_mode = MXL111SF_GPIO_MOD_ATSC; |
443 | adap_state->gpio_mode = state->gpio_mode; |
444 | adap_state->device_mode = MXL_TUNER_MODE; |
445 | adap_state->ep6_clockphase = 1; |
446 | |
447 | ret = mxl1x1sf_soft_reset(state); |
448 | if (mxl_fail(ret)) |
449 | goto fail; |
450 | ret = mxl111sf_init_tuner_demod(state); |
451 | if (mxl_fail(ret)) |
452 | goto fail; |
453 | |
454 | ret = mxl1x1sf_set_device_mode(state, mode: adap_state->device_mode); |
455 | if (mxl_fail(ret)) |
456 | goto fail; |
457 | |
458 | ret = mxl111sf_enable_usb_output(state); |
459 | if (mxl_fail(ret)) |
460 | goto fail; |
461 | ret = mxl1x1sf_top_master_ctrl(state, onoff: 1); |
462 | if (mxl_fail(ret)) |
463 | goto fail; |
464 | |
465 | ret = mxl111sf_init_port_expander(state); |
466 | if (mxl_fail(ret)) |
467 | goto fail; |
468 | ret = mxl111sf_gpio_mode_switch(state, mode: state->gpio_mode); |
469 | if (mxl_fail(ret)) |
470 | goto fail; |
471 | |
472 | adap->fe[fe_id] = dvb_attach(lgdt3305_attach, |
473 | &hauppauge_lgdt3305_config, |
474 | &d->i2c_adap); |
475 | if (adap->fe[fe_id]) { |
476 | state->num_frontends++; |
477 | adap_state->fe_init = adap->fe[fe_id]->ops.init; |
478 | adap->fe[fe_id]->ops.init = mxl111sf_adap_fe_init; |
479 | adap_state->fe_sleep = adap->fe[fe_id]->ops.sleep; |
480 | adap->fe[fe_id]->ops.sleep = mxl111sf_adap_fe_sleep; |
481 | return 0; |
482 | } |
483 | ret = -EIO; |
484 | fail: |
485 | return ret; |
486 | } |
487 | |
488 | static struct lg2160_config hauppauge_lg2160_config = { |
489 | .lg_chip = LG2160, |
490 | .i2c_addr = 0x1c >> 1, |
491 | .deny_i2c_rptr = 1, |
492 | .spectral_inversion = 0, |
493 | .if_khz = 6000, |
494 | }; |
495 | |
496 | static int mxl111sf_lg2160_frontend_attach(struct dvb_usb_adapter *adap, u8 fe_id) |
497 | { |
498 | struct dvb_usb_device *d = adap_to_d(adap); |
499 | struct mxl111sf_state *state = d_to_priv(d); |
500 | struct mxl111sf_adap_state *adap_state = &state->adap_state[fe_id]; |
501 | int ret; |
502 | |
503 | pr_debug("%s()\n" , __func__); |
504 | |
505 | /* save a pointer to the dvb_usb_device in device state */ |
506 | state->d = d; |
507 | adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 2 : 1; |
508 | state->alt_mode = adap_state->alt_mode; |
509 | |
510 | if (usb_set_interface(dev: d->udev, ifnum: 0, alternate: state->alt_mode) < 0) |
511 | pr_err("set interface failed" ); |
512 | |
513 | state->gpio_mode = MXL111SF_GPIO_MOD_MH; |
514 | adap_state->gpio_mode = state->gpio_mode; |
515 | adap_state->device_mode = MXL_TUNER_MODE; |
516 | adap_state->ep6_clockphase = 1; |
517 | |
518 | ret = mxl1x1sf_soft_reset(state); |
519 | if (mxl_fail(ret)) |
520 | goto fail; |
521 | ret = mxl111sf_init_tuner_demod(state); |
522 | if (mxl_fail(ret)) |
523 | goto fail; |
524 | |
525 | ret = mxl1x1sf_set_device_mode(state, mode: adap_state->device_mode); |
526 | if (mxl_fail(ret)) |
527 | goto fail; |
528 | |
529 | ret = mxl111sf_enable_usb_output(state); |
530 | if (mxl_fail(ret)) |
531 | goto fail; |
532 | ret = mxl1x1sf_top_master_ctrl(state, onoff: 1); |
533 | if (mxl_fail(ret)) |
534 | goto fail; |
535 | |
536 | ret = mxl111sf_init_port_expander(state); |
537 | if (mxl_fail(ret)) |
538 | goto fail; |
539 | ret = mxl111sf_gpio_mode_switch(state, mode: state->gpio_mode); |
540 | if (mxl_fail(ret)) |
541 | goto fail; |
542 | |
543 | ret = get_chip_info(state); |
544 | if (mxl_fail(ret)) |
545 | goto fail; |
546 | |
547 | adap->fe[fe_id] = dvb_attach(lg2160_attach, |
548 | &hauppauge_lg2160_config, |
549 | &d->i2c_adap); |
550 | if (adap->fe[fe_id]) { |
551 | state->num_frontends++; |
552 | adap_state->fe_init = adap->fe[fe_id]->ops.init; |
553 | adap->fe[fe_id]->ops.init = mxl111sf_adap_fe_init; |
554 | adap_state->fe_sleep = adap->fe[fe_id]->ops.sleep; |
555 | adap->fe[fe_id]->ops.sleep = mxl111sf_adap_fe_sleep; |
556 | return 0; |
557 | } |
558 | ret = -EIO; |
559 | fail: |
560 | return ret; |
561 | } |
562 | |
563 | static struct lg2160_config hauppauge_lg2161_1019_config = { |
564 | .lg_chip = LG2161_1019, |
565 | .i2c_addr = 0x1c >> 1, |
566 | .deny_i2c_rptr = 1, |
567 | .spectral_inversion = 0, |
568 | .if_khz = 6000, |
569 | .output_if = 2, /* LG2161_OIF_SPI_MAS */ |
570 | }; |
571 | |
572 | static struct lg2160_config hauppauge_lg2161_1040_config = { |
573 | .lg_chip = LG2161_1040, |
574 | .i2c_addr = 0x1c >> 1, |
575 | .deny_i2c_rptr = 1, |
576 | .spectral_inversion = 0, |
577 | .if_khz = 6000, |
578 | .output_if = 4, /* LG2161_OIF_SPI_MAS */ |
579 | }; |
580 | |
581 | static int mxl111sf_lg2161_frontend_attach(struct dvb_usb_adapter *adap, u8 fe_id) |
582 | { |
583 | struct dvb_usb_device *d = adap_to_d(adap); |
584 | struct mxl111sf_state *state = d_to_priv(d); |
585 | struct mxl111sf_adap_state *adap_state = &state->adap_state[fe_id]; |
586 | int ret; |
587 | |
588 | pr_debug("%s()\n" , __func__); |
589 | |
590 | /* save a pointer to the dvb_usb_device in device state */ |
591 | state->d = d; |
592 | adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 2 : 1; |
593 | state->alt_mode = adap_state->alt_mode; |
594 | |
595 | if (usb_set_interface(dev: d->udev, ifnum: 0, alternate: state->alt_mode) < 0) |
596 | pr_err("set interface failed" ); |
597 | |
598 | state->gpio_mode = MXL111SF_GPIO_MOD_MH; |
599 | adap_state->gpio_mode = state->gpio_mode; |
600 | adap_state->device_mode = MXL_TUNER_MODE; |
601 | adap_state->ep6_clockphase = 1; |
602 | |
603 | ret = mxl1x1sf_soft_reset(state); |
604 | if (mxl_fail(ret)) |
605 | goto fail; |
606 | ret = mxl111sf_init_tuner_demod(state); |
607 | if (mxl_fail(ret)) |
608 | goto fail; |
609 | |
610 | ret = mxl1x1sf_set_device_mode(state, mode: adap_state->device_mode); |
611 | if (mxl_fail(ret)) |
612 | goto fail; |
613 | |
614 | ret = mxl111sf_enable_usb_output(state); |
615 | if (mxl_fail(ret)) |
616 | goto fail; |
617 | ret = mxl1x1sf_top_master_ctrl(state, onoff: 1); |
618 | if (mxl_fail(ret)) |
619 | goto fail; |
620 | |
621 | ret = mxl111sf_init_port_expander(state); |
622 | if (mxl_fail(ret)) |
623 | goto fail; |
624 | ret = mxl111sf_gpio_mode_switch(state, mode: state->gpio_mode); |
625 | if (mxl_fail(ret)) |
626 | goto fail; |
627 | |
628 | ret = get_chip_info(state); |
629 | if (mxl_fail(ret)) |
630 | goto fail; |
631 | |
632 | adap->fe[fe_id] = dvb_attach(lg2160_attach, |
633 | (MXL111SF_V8_200 == state->chip_rev) ? |
634 | &hauppauge_lg2161_1040_config : |
635 | &hauppauge_lg2161_1019_config, |
636 | &d->i2c_adap); |
637 | if (adap->fe[fe_id]) { |
638 | state->num_frontends++; |
639 | adap_state->fe_init = adap->fe[fe_id]->ops.init; |
640 | adap->fe[fe_id]->ops.init = mxl111sf_adap_fe_init; |
641 | adap_state->fe_sleep = adap->fe[fe_id]->ops.sleep; |
642 | adap->fe[fe_id]->ops.sleep = mxl111sf_adap_fe_sleep; |
643 | return 0; |
644 | } |
645 | ret = -EIO; |
646 | fail: |
647 | return ret; |
648 | } |
649 | |
650 | static struct lg2160_config hauppauge_lg2161_1019_ep6_config = { |
651 | .lg_chip = LG2161_1019, |
652 | .i2c_addr = 0x1c >> 1, |
653 | .deny_i2c_rptr = 1, |
654 | .spectral_inversion = 0, |
655 | .if_khz = 6000, |
656 | .output_if = 1, /* LG2161_OIF_SERIAL_TS */ |
657 | }; |
658 | |
659 | static struct lg2160_config hauppauge_lg2161_1040_ep6_config = { |
660 | .lg_chip = LG2161_1040, |
661 | .i2c_addr = 0x1c >> 1, |
662 | .deny_i2c_rptr = 1, |
663 | .spectral_inversion = 0, |
664 | .if_khz = 6000, |
665 | .output_if = 7, /* LG2161_OIF_SERIAL_TS */ |
666 | }; |
667 | |
668 | static int mxl111sf_lg2161_ep6_frontend_attach(struct dvb_usb_adapter *adap, u8 fe_id) |
669 | { |
670 | struct dvb_usb_device *d = adap_to_d(adap); |
671 | struct mxl111sf_state *state = d_to_priv(d); |
672 | struct mxl111sf_adap_state *adap_state = &state->adap_state[fe_id]; |
673 | int ret; |
674 | |
675 | pr_debug("%s()\n" , __func__); |
676 | |
677 | /* save a pointer to the dvb_usb_device in device state */ |
678 | state->d = d; |
679 | adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 2 : 1; |
680 | state->alt_mode = adap_state->alt_mode; |
681 | |
682 | if (usb_set_interface(dev: d->udev, ifnum: 0, alternate: state->alt_mode) < 0) |
683 | pr_err("set interface failed" ); |
684 | |
685 | state->gpio_mode = MXL111SF_GPIO_MOD_MH; |
686 | adap_state->gpio_mode = state->gpio_mode; |
687 | adap_state->device_mode = MXL_TUNER_MODE; |
688 | adap_state->ep6_clockphase = 0; |
689 | |
690 | ret = mxl1x1sf_soft_reset(state); |
691 | if (mxl_fail(ret)) |
692 | goto fail; |
693 | ret = mxl111sf_init_tuner_demod(state); |
694 | if (mxl_fail(ret)) |
695 | goto fail; |
696 | |
697 | ret = mxl1x1sf_set_device_mode(state, mode: adap_state->device_mode); |
698 | if (mxl_fail(ret)) |
699 | goto fail; |
700 | |
701 | ret = mxl111sf_enable_usb_output(state); |
702 | if (mxl_fail(ret)) |
703 | goto fail; |
704 | ret = mxl1x1sf_top_master_ctrl(state, onoff: 1); |
705 | if (mxl_fail(ret)) |
706 | goto fail; |
707 | |
708 | ret = mxl111sf_init_port_expander(state); |
709 | if (mxl_fail(ret)) |
710 | goto fail; |
711 | ret = mxl111sf_gpio_mode_switch(state, mode: state->gpio_mode); |
712 | if (mxl_fail(ret)) |
713 | goto fail; |
714 | |
715 | ret = get_chip_info(state); |
716 | if (mxl_fail(ret)) |
717 | goto fail; |
718 | |
719 | adap->fe[fe_id] = dvb_attach(lg2160_attach, |
720 | (MXL111SF_V8_200 == state->chip_rev) ? |
721 | &hauppauge_lg2161_1040_ep6_config : |
722 | &hauppauge_lg2161_1019_ep6_config, |
723 | &d->i2c_adap); |
724 | if (adap->fe[fe_id]) { |
725 | state->num_frontends++; |
726 | adap_state->fe_init = adap->fe[fe_id]->ops.init; |
727 | adap->fe[fe_id]->ops.init = mxl111sf_adap_fe_init; |
728 | adap_state->fe_sleep = adap->fe[fe_id]->ops.sleep; |
729 | adap->fe[fe_id]->ops.sleep = mxl111sf_adap_fe_sleep; |
730 | return 0; |
731 | } |
732 | ret = -EIO; |
733 | fail: |
734 | return ret; |
735 | } |
736 | |
737 | static const struct mxl111sf_demod_config mxl_demod_config = { |
738 | .read_reg = mxl111sf_read_reg, |
739 | .write_reg = mxl111sf_write_reg, |
740 | .program_regs = mxl111sf_ctrl_program_regs, |
741 | }; |
742 | |
743 | static int mxl111sf_attach_demod(struct dvb_usb_adapter *adap, u8 fe_id) |
744 | { |
745 | struct dvb_usb_device *d = adap_to_d(adap); |
746 | struct mxl111sf_state *state = d_to_priv(d); |
747 | struct mxl111sf_adap_state *adap_state = &state->adap_state[fe_id]; |
748 | int ret; |
749 | |
750 | pr_debug("%s()\n" , __func__); |
751 | |
752 | /* save a pointer to the dvb_usb_device in device state */ |
753 | state->d = d; |
754 | adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 1 : 2; |
755 | state->alt_mode = adap_state->alt_mode; |
756 | |
757 | if (usb_set_interface(dev: d->udev, ifnum: 0, alternate: state->alt_mode) < 0) |
758 | pr_err("set interface failed" ); |
759 | |
760 | state->gpio_mode = MXL111SF_GPIO_MOD_DVBT; |
761 | adap_state->gpio_mode = state->gpio_mode; |
762 | adap_state->device_mode = MXL_SOC_MODE; |
763 | adap_state->ep6_clockphase = 1; |
764 | |
765 | ret = mxl1x1sf_soft_reset(state); |
766 | if (mxl_fail(ret)) |
767 | goto fail; |
768 | ret = mxl111sf_init_tuner_demod(state); |
769 | if (mxl_fail(ret)) |
770 | goto fail; |
771 | |
772 | ret = mxl1x1sf_set_device_mode(state, mode: adap_state->device_mode); |
773 | if (mxl_fail(ret)) |
774 | goto fail; |
775 | |
776 | ret = mxl111sf_enable_usb_output(state); |
777 | if (mxl_fail(ret)) |
778 | goto fail; |
779 | ret = mxl1x1sf_top_master_ctrl(state, onoff: 1); |
780 | if (mxl_fail(ret)) |
781 | goto fail; |
782 | |
783 | /* don't care if this fails */ |
784 | mxl111sf_init_port_expander(state); |
785 | |
786 | adap->fe[fe_id] = dvb_attach(mxl111sf_demod_attach, state, |
787 | &mxl_demod_config); |
788 | if (adap->fe[fe_id]) { |
789 | state->num_frontends++; |
790 | adap_state->fe_init = adap->fe[fe_id]->ops.init; |
791 | adap->fe[fe_id]->ops.init = mxl111sf_adap_fe_init; |
792 | adap_state->fe_sleep = adap->fe[fe_id]->ops.sleep; |
793 | adap->fe[fe_id]->ops.sleep = mxl111sf_adap_fe_sleep; |
794 | return 0; |
795 | } |
796 | ret = -EIO; |
797 | fail: |
798 | return ret; |
799 | } |
800 | |
801 | static inline int mxl111sf_set_ant_path(struct mxl111sf_state *state, |
802 | int antpath) |
803 | { |
804 | return mxl111sf_idac_config(state, control_mode: 1, current_setting: 1, |
805 | current_value: (antpath == ANT_PATH_INTERNAL) ? |
806 | 0x3f : 0x00, hysteresis_value: 0); |
807 | } |
808 | |
809 | #define DbgAntHunt(x, pwr0, pwr1, pwr2, pwr3) \ |
810 | pr_err("%s(%d) FINAL input set to %s rxPwr:%d|%d|%d|%d\n", \ |
811 | __func__, __LINE__, \ |
812 | (ANT_PATH_EXTERNAL == x) ? "EXTERNAL" : "INTERNAL", \ |
813 | pwr0, pwr1, pwr2, pwr3) |
814 | |
815 | #define ANT_HUNT_SLEEP 90 |
816 | #define ANT_EXT_TWEAK 0 |
817 | |
818 | static int mxl111sf_ant_hunt(struct dvb_frontend *fe) |
819 | { |
820 | struct mxl111sf_state *state = fe_to_priv(fe); |
821 | int antctrl = dvb_usb_mxl111sf_rfswitch; |
822 | |
823 | u16 rxPwrA, rxPwr0, rxPwr1, rxPwr2; |
824 | |
825 | /* FIXME: must force EXTERNAL for QAM - done elsewhere */ |
826 | mxl111sf_set_ant_path(state, antpath: antctrl == ANT_PATH_AUTO ? |
827 | ANT_PATH_EXTERNAL : antctrl); |
828 | |
829 | if (antctrl == ANT_PATH_AUTO) { |
830 | #if 0 |
831 | msleep(ANT_HUNT_SLEEP); |
832 | #endif |
833 | fe->ops.tuner_ops.get_rf_strength(fe, &rxPwrA); |
834 | |
835 | mxl111sf_set_ant_path(state, ANT_PATH_EXTERNAL); |
836 | msleep(ANT_HUNT_SLEEP); |
837 | fe->ops.tuner_ops.get_rf_strength(fe, &rxPwr0); |
838 | |
839 | mxl111sf_set_ant_path(state, ANT_PATH_EXTERNAL); |
840 | msleep(ANT_HUNT_SLEEP); |
841 | fe->ops.tuner_ops.get_rf_strength(fe, &rxPwr1); |
842 | |
843 | mxl111sf_set_ant_path(state, ANT_PATH_INTERNAL); |
844 | msleep(ANT_HUNT_SLEEP); |
845 | fe->ops.tuner_ops.get_rf_strength(fe, &rxPwr2); |
846 | |
847 | if (rxPwr1+ANT_EXT_TWEAK >= rxPwr2) { |
848 | /* return with EXTERNAL enabled */ |
849 | mxl111sf_set_ant_path(state, ANT_PATH_EXTERNAL); |
850 | DbgAntHunt(ANT_PATH_EXTERNAL, rxPwrA, |
851 | rxPwr0, rxPwr1, rxPwr2); |
852 | } else { |
853 | /* return with INTERNAL enabled */ |
854 | DbgAntHunt(ANT_PATH_INTERNAL, rxPwrA, |
855 | rxPwr0, rxPwr1, rxPwr2); |
856 | } |
857 | } |
858 | return 0; |
859 | } |
860 | |
861 | static const struct mxl111sf_tuner_config mxl_tuner_config = { |
862 | .if_freq = MXL_IF_6_0, /* applies to external IF output, only */ |
863 | .invert_spectrum = 0, |
864 | .read_reg = mxl111sf_read_reg, |
865 | .write_reg = mxl111sf_write_reg, |
866 | .program_regs = mxl111sf_ctrl_program_regs, |
867 | .top_master_ctrl = mxl1x1sf_top_master_ctrl, |
868 | .ant_hunt = mxl111sf_ant_hunt, |
869 | }; |
870 | |
871 | static int mxl111sf_attach_tuner(struct dvb_usb_adapter *adap) |
872 | { |
873 | struct mxl111sf_state *state = adap_to_priv(adap); |
874 | #ifdef CONFIG_MEDIA_CONTROLLER_DVB |
875 | struct media_device *mdev = dvb_get_media_controller(adap: &adap->dvb_adap); |
876 | int ret; |
877 | #endif |
878 | int i; |
879 | |
880 | pr_debug("%s()\n" , __func__); |
881 | |
882 | for (i = 0; i < state->num_frontends; i++) { |
883 | if (dvb_attach(mxl111sf_tuner_attach, adap->fe[i], state, |
884 | &mxl_tuner_config) == NULL) |
885 | return -EIO; |
886 | adap->fe[i]->ops.read_signal_strength = adap->fe[i]->ops.tuner_ops.get_rf_strength; |
887 | } |
888 | |
889 | #ifdef CONFIG_MEDIA_CONTROLLER_DVB |
890 | state->tuner.function = MEDIA_ENT_F_TUNER; |
891 | state->tuner.name = "mxl111sf tuner" ; |
892 | state->tuner_pads[MXL111SF_PAD_RF_INPUT].flags = MEDIA_PAD_FL_SINK; |
893 | state->tuner_pads[MXL111SF_PAD_RF_INPUT].sig_type = PAD_SIGNAL_ANALOG; |
894 | state->tuner_pads[MXL111SF_PAD_OUTPUT].flags = MEDIA_PAD_FL_SOURCE; |
895 | state->tuner_pads[MXL111SF_PAD_OUTPUT].sig_type = PAD_SIGNAL_ANALOG; |
896 | |
897 | ret = media_entity_pads_init(entity: &state->tuner, |
898 | num_pads: MXL111SF_NUM_PADS, pads: state->tuner_pads); |
899 | if (ret) |
900 | return ret; |
901 | |
902 | ret = media_device_register_entity(mdev, entity: &state->tuner); |
903 | if (ret) |
904 | return ret; |
905 | #endif |
906 | return 0; |
907 | } |
908 | |
909 | static u32 mxl111sf_i2c_func(struct i2c_adapter *adapter) |
910 | { |
911 | return I2C_FUNC_I2C; |
912 | } |
913 | |
914 | static struct i2c_algorithm mxl111sf_i2c_algo = { |
915 | .master_xfer = mxl111sf_i2c_xfer, |
916 | .functionality = mxl111sf_i2c_func, |
917 | #ifdef NEED_ALGO_CONTROL |
918 | .algo_control = dummy_algo_control, |
919 | #endif |
920 | }; |
921 | |
922 | static int mxl111sf_init(struct dvb_usb_device *d) |
923 | { |
924 | struct mxl111sf_state *state = d_to_priv(d); |
925 | int ret; |
926 | static u8 eeprom[256]; |
927 | u8 reg = 0; |
928 | struct i2c_msg msg[2] = { |
929 | { .addr = 0xa0 >> 1, .len = 1, .buf = ® }, |
930 | { .addr = 0xa0 >> 1, .flags = I2C_M_RD, |
931 | .len = sizeof(eeprom), .buf = eeprom }, |
932 | }; |
933 | |
934 | ret = get_chip_info(state); |
935 | if (mxl_fail(ret)) |
936 | pr_err("failed to get chip info during probe" ); |
937 | |
938 | mutex_init(&state->fe_lock); |
939 | |
940 | if (state->chip_rev > MXL111SF_V6) |
941 | mxl111sf_config_pin_mux_modes(state, pin_mux_config: PIN_MUX_TS_SPI_IN_MODE_1); |
942 | |
943 | ret = i2c_transfer(adap: &d->i2c_adap, msgs: msg, num: 2); |
944 | if (mxl_fail(ret)) |
945 | return 0; |
946 | tveeprom_hauppauge_analog(tvee: &state->tv, eeprom_data: (0x84 == eeprom[0xa0]) ? |
947 | eeprom + 0xa0 : eeprom + 0x80); |
948 | #if 0 |
949 | switch (state->tv.model) { |
950 | case 117001: |
951 | case 126001: |
952 | case 138001: |
953 | break; |
954 | default: |
955 | printk(KERN_WARNING "%s: warning: unknown hauppauge model #%d\n" , |
956 | __func__, state->tv.model); |
957 | } |
958 | #endif |
959 | return 0; |
960 | } |
961 | |
962 | static int mxl111sf_frontend_attach_dvbt(struct dvb_usb_adapter *adap) |
963 | { |
964 | return mxl111sf_attach_demod(adap, fe_id: 0); |
965 | } |
966 | |
967 | static int mxl111sf_frontend_attach_atsc(struct dvb_usb_adapter *adap) |
968 | { |
969 | return mxl111sf_lgdt3305_frontend_attach(adap, fe_id: 0); |
970 | } |
971 | |
972 | static int mxl111sf_frontend_attach_mh(struct dvb_usb_adapter *adap) |
973 | { |
974 | return mxl111sf_lg2160_frontend_attach(adap, fe_id: 0); |
975 | } |
976 | |
977 | static int mxl111sf_frontend_attach_atsc_mh(struct dvb_usb_adapter *adap) |
978 | { |
979 | int ret; |
980 | pr_debug("%s\n" , __func__); |
981 | |
982 | ret = mxl111sf_lgdt3305_frontend_attach(adap, fe_id: 0); |
983 | if (ret < 0) |
984 | return ret; |
985 | |
986 | ret = mxl111sf_attach_demod(adap, fe_id: 1); |
987 | if (ret < 0) |
988 | return ret; |
989 | |
990 | ret = mxl111sf_lg2160_frontend_attach(adap, fe_id: 2); |
991 | if (ret < 0) |
992 | return ret; |
993 | |
994 | return ret; |
995 | } |
996 | |
997 | static int mxl111sf_frontend_attach_mercury(struct dvb_usb_adapter *adap) |
998 | { |
999 | int ret; |
1000 | pr_debug("%s\n" , __func__); |
1001 | |
1002 | ret = mxl111sf_lgdt3305_frontend_attach(adap, fe_id: 0); |
1003 | if (ret < 0) |
1004 | return ret; |
1005 | |
1006 | ret = mxl111sf_attach_demod(adap, fe_id: 1); |
1007 | if (ret < 0) |
1008 | return ret; |
1009 | |
1010 | ret = mxl111sf_lg2161_ep6_frontend_attach(adap, fe_id: 2); |
1011 | if (ret < 0) |
1012 | return ret; |
1013 | |
1014 | return ret; |
1015 | } |
1016 | |
1017 | static int mxl111sf_frontend_attach_mercury_mh(struct dvb_usb_adapter *adap) |
1018 | { |
1019 | int ret; |
1020 | pr_debug("%s\n" , __func__); |
1021 | |
1022 | ret = mxl111sf_attach_demod(adap, fe_id: 0); |
1023 | if (ret < 0) |
1024 | return ret; |
1025 | |
1026 | if (dvb_usb_mxl111sf_spi) |
1027 | ret = mxl111sf_lg2161_frontend_attach(adap, fe_id: 1); |
1028 | else |
1029 | ret = mxl111sf_lg2161_ep6_frontend_attach(adap, fe_id: 1); |
1030 | |
1031 | return ret; |
1032 | } |
1033 | |
1034 | static void mxl111sf_stream_config_bulk(struct usb_data_stream_properties *stream, u8 endpoint) |
1035 | { |
1036 | pr_debug("%s: endpoint=%d size=8192\n" , __func__, endpoint); |
1037 | stream->type = USB_BULK; |
1038 | stream->count = 5; |
1039 | stream->endpoint = endpoint; |
1040 | stream->u.bulk.buffersize = 8192; |
1041 | } |
1042 | |
1043 | static void mxl111sf_stream_config_isoc(struct usb_data_stream_properties *stream, |
1044 | u8 endpoint, int framesperurb, int framesize) |
1045 | { |
1046 | pr_debug("%s: endpoint=%d size=%d\n" , __func__, endpoint, |
1047 | framesperurb * framesize); |
1048 | stream->type = USB_ISOC; |
1049 | stream->count = 5; |
1050 | stream->endpoint = endpoint; |
1051 | stream->u.isoc.framesperurb = framesperurb; |
1052 | stream->u.isoc.framesize = framesize; |
1053 | stream->u.isoc.interval = 1; |
1054 | } |
1055 | |
1056 | /* DVB USB Driver stuff */ |
1057 | |
1058 | /* dvbt mxl111sf |
1059 | * bulk EP4/BULK/5/8192 |
1060 | * isoc EP4/ISOC/5/96/564 |
1061 | */ |
1062 | static int mxl111sf_get_stream_config_dvbt(struct dvb_frontend *fe, |
1063 | u8 *ts_type, struct usb_data_stream_properties *stream) |
1064 | { |
1065 | pr_debug("%s: fe=%d\n" , __func__, fe->id); |
1066 | |
1067 | *ts_type = DVB_USB_FE_TS_TYPE_188; |
1068 | if (dvb_usb_mxl111sf_isoc) |
1069 | mxl111sf_stream_config_isoc(stream, endpoint: 4, framesperurb: 96, framesize: 564); |
1070 | else |
1071 | mxl111sf_stream_config_bulk(stream, endpoint: 4); |
1072 | return 0; |
1073 | } |
1074 | |
1075 | static int mxl111sf_probe(struct dvb_usb_device *dev) |
1076 | { |
1077 | struct mxl111sf_state *state = d_to_priv(dev); |
1078 | |
1079 | mutex_init(&state->msg_lock); |
1080 | return 0; |
1081 | } |
1082 | |
1083 | static struct dvb_usb_device_properties mxl111sf_props_dvbt = { |
1084 | .driver_name = KBUILD_MODNAME, |
1085 | .owner = THIS_MODULE, |
1086 | .adapter_nr = adapter_nr, |
1087 | .size_of_priv = sizeof(struct mxl111sf_state), |
1088 | |
1089 | .generic_bulk_ctrl_endpoint = 0x02, |
1090 | .generic_bulk_ctrl_endpoint_response = 0x81, |
1091 | |
1092 | .probe = mxl111sf_probe, |
1093 | .i2c_algo = &mxl111sf_i2c_algo, |
1094 | .frontend_attach = mxl111sf_frontend_attach_dvbt, |
1095 | .tuner_attach = mxl111sf_attach_tuner, |
1096 | .init = mxl111sf_init, |
1097 | .streaming_ctrl = mxl111sf_ep4_streaming_ctrl, |
1098 | .get_stream_config = mxl111sf_get_stream_config_dvbt, |
1099 | |
1100 | .num_adapters = 1, |
1101 | .adapter = { |
1102 | { |
1103 | .stream = DVB_USB_STREAM_ISOC(6, 5, 24, 3072, 1), |
1104 | } |
1105 | } |
1106 | }; |
1107 | |
1108 | /* atsc lgdt3305 |
1109 | * bulk EP6/BULK/5/8192 |
1110 | * isoc EP6/ISOC/5/24/3072 |
1111 | */ |
1112 | static int mxl111sf_get_stream_config_atsc(struct dvb_frontend *fe, |
1113 | u8 *ts_type, struct usb_data_stream_properties *stream) |
1114 | { |
1115 | pr_debug("%s: fe=%d\n" , __func__, fe->id); |
1116 | |
1117 | *ts_type = DVB_USB_FE_TS_TYPE_188; |
1118 | if (dvb_usb_mxl111sf_isoc) |
1119 | mxl111sf_stream_config_isoc(stream, endpoint: 6, framesperurb: 24, framesize: 3072); |
1120 | else |
1121 | mxl111sf_stream_config_bulk(stream, endpoint: 6); |
1122 | return 0; |
1123 | } |
1124 | |
1125 | static struct dvb_usb_device_properties mxl111sf_props_atsc = { |
1126 | .driver_name = KBUILD_MODNAME, |
1127 | .owner = THIS_MODULE, |
1128 | .adapter_nr = adapter_nr, |
1129 | .size_of_priv = sizeof(struct mxl111sf_state), |
1130 | |
1131 | .generic_bulk_ctrl_endpoint = 0x02, |
1132 | .generic_bulk_ctrl_endpoint_response = 0x81, |
1133 | |
1134 | .probe = mxl111sf_probe, |
1135 | .i2c_algo = &mxl111sf_i2c_algo, |
1136 | .frontend_attach = mxl111sf_frontend_attach_atsc, |
1137 | .tuner_attach = mxl111sf_attach_tuner, |
1138 | .init = mxl111sf_init, |
1139 | .streaming_ctrl = mxl111sf_ep6_streaming_ctrl, |
1140 | .get_stream_config = mxl111sf_get_stream_config_atsc, |
1141 | |
1142 | .num_adapters = 1, |
1143 | .adapter = { |
1144 | { |
1145 | .stream = DVB_USB_STREAM_ISOC(6, 5, 24, 3072, 1), |
1146 | } |
1147 | } |
1148 | }; |
1149 | |
1150 | /* mh lg2160 |
1151 | * bulk EP5/BULK/5/8192/RAW |
1152 | * isoc EP5/ISOC/5/96/200/RAW |
1153 | */ |
1154 | static int mxl111sf_get_stream_config_mh(struct dvb_frontend *fe, |
1155 | u8 *ts_type, struct usb_data_stream_properties *stream) |
1156 | { |
1157 | pr_debug("%s: fe=%d\n" , __func__, fe->id); |
1158 | |
1159 | *ts_type = DVB_USB_FE_TS_TYPE_RAW; |
1160 | if (dvb_usb_mxl111sf_isoc) |
1161 | mxl111sf_stream_config_isoc(stream, endpoint: 5, framesperurb: 96, framesize: 200); |
1162 | else |
1163 | mxl111sf_stream_config_bulk(stream, endpoint: 5); |
1164 | return 0; |
1165 | } |
1166 | |
1167 | static struct dvb_usb_device_properties mxl111sf_props_mh = { |
1168 | .driver_name = KBUILD_MODNAME, |
1169 | .owner = THIS_MODULE, |
1170 | .adapter_nr = adapter_nr, |
1171 | .size_of_priv = sizeof(struct mxl111sf_state), |
1172 | |
1173 | .generic_bulk_ctrl_endpoint = 0x02, |
1174 | .generic_bulk_ctrl_endpoint_response = 0x81, |
1175 | |
1176 | .probe = mxl111sf_probe, |
1177 | .i2c_algo = &mxl111sf_i2c_algo, |
1178 | .frontend_attach = mxl111sf_frontend_attach_mh, |
1179 | .tuner_attach = mxl111sf_attach_tuner, |
1180 | .init = mxl111sf_init, |
1181 | .streaming_ctrl = mxl111sf_ep5_streaming_ctrl, |
1182 | .get_stream_config = mxl111sf_get_stream_config_mh, |
1183 | |
1184 | .num_adapters = 1, |
1185 | .adapter = { |
1186 | { |
1187 | .stream = DVB_USB_STREAM_ISOC(6, 5, 24, 3072, 1), |
1188 | } |
1189 | } |
1190 | }; |
1191 | |
1192 | /* atsc mh lgdt3305 mxl111sf lg2160 |
1193 | * bulk EP6/BULK/5/8192 EP4/BULK/5/8192 EP5/BULK/5/8192/RAW |
1194 | * isoc EP6/ISOC/5/24/3072 EP4/ISOC/5/96/564 EP5/ISOC/5/96/200/RAW |
1195 | */ |
1196 | static int mxl111sf_get_stream_config_atsc_mh(struct dvb_frontend *fe, |
1197 | u8 *ts_type, struct usb_data_stream_properties *stream) |
1198 | { |
1199 | pr_debug("%s: fe=%d\n" , __func__, fe->id); |
1200 | |
1201 | if (fe->id == 0) { |
1202 | *ts_type = DVB_USB_FE_TS_TYPE_188; |
1203 | if (dvb_usb_mxl111sf_isoc) |
1204 | mxl111sf_stream_config_isoc(stream, endpoint: 6, framesperurb: 24, framesize: 3072); |
1205 | else |
1206 | mxl111sf_stream_config_bulk(stream, endpoint: 6); |
1207 | } else if (fe->id == 1) { |
1208 | *ts_type = DVB_USB_FE_TS_TYPE_188; |
1209 | if (dvb_usb_mxl111sf_isoc) |
1210 | mxl111sf_stream_config_isoc(stream, endpoint: 4, framesperurb: 96, framesize: 564); |
1211 | else |
1212 | mxl111sf_stream_config_bulk(stream, endpoint: 4); |
1213 | } else if (fe->id == 2) { |
1214 | *ts_type = DVB_USB_FE_TS_TYPE_RAW; |
1215 | if (dvb_usb_mxl111sf_isoc) |
1216 | mxl111sf_stream_config_isoc(stream, endpoint: 5, framesperurb: 96, framesize: 200); |
1217 | else |
1218 | mxl111sf_stream_config_bulk(stream, endpoint: 5); |
1219 | } |
1220 | return 0; |
1221 | } |
1222 | |
1223 | static int mxl111sf_streaming_ctrl_atsc_mh(struct dvb_frontend *fe, int onoff) |
1224 | { |
1225 | pr_debug("%s: fe=%d onoff=%d\n" , __func__, fe->id, onoff); |
1226 | |
1227 | if (fe->id == 0) |
1228 | return mxl111sf_ep6_streaming_ctrl(fe, onoff); |
1229 | else if (fe->id == 1) |
1230 | return mxl111sf_ep4_streaming_ctrl(fe, onoff); |
1231 | else if (fe->id == 2) |
1232 | return mxl111sf_ep5_streaming_ctrl(fe, onoff); |
1233 | return 0; |
1234 | } |
1235 | |
1236 | static struct dvb_usb_device_properties mxl111sf_props_atsc_mh = { |
1237 | .driver_name = KBUILD_MODNAME, |
1238 | .owner = THIS_MODULE, |
1239 | .adapter_nr = adapter_nr, |
1240 | .size_of_priv = sizeof(struct mxl111sf_state), |
1241 | |
1242 | .generic_bulk_ctrl_endpoint = 0x02, |
1243 | .generic_bulk_ctrl_endpoint_response = 0x81, |
1244 | |
1245 | .probe = mxl111sf_probe, |
1246 | .i2c_algo = &mxl111sf_i2c_algo, |
1247 | .frontend_attach = mxl111sf_frontend_attach_atsc_mh, |
1248 | .tuner_attach = mxl111sf_attach_tuner, |
1249 | .init = mxl111sf_init, |
1250 | .streaming_ctrl = mxl111sf_streaming_ctrl_atsc_mh, |
1251 | .get_stream_config = mxl111sf_get_stream_config_atsc_mh, |
1252 | |
1253 | .num_adapters = 1, |
1254 | .adapter = { |
1255 | { |
1256 | .stream = DVB_USB_STREAM_ISOC(6, 5, 24, 3072, 1), |
1257 | } |
1258 | } |
1259 | }; |
1260 | |
1261 | /* mercury lgdt3305 mxl111sf lg2161 |
1262 | * tp bulk EP6/BULK/5/8192 EP4/BULK/5/8192 EP6/BULK/5/8192/RAW |
1263 | * tp isoc EP6/ISOC/5/24/3072 EP4/ISOC/5/96/564 EP6/ISOC/5/24/3072/RAW |
1264 | * spi bulk EP6/BULK/5/8192 EP4/BULK/5/8192 EP5/BULK/5/8192/RAW |
1265 | * spi isoc EP6/ISOC/5/24/3072 EP4/ISOC/5/96/564 EP5/ISOC/5/96/200/RAW |
1266 | */ |
1267 | static int mxl111sf_get_stream_config_mercury(struct dvb_frontend *fe, |
1268 | u8 *ts_type, struct usb_data_stream_properties *stream) |
1269 | { |
1270 | pr_debug("%s: fe=%d\n" , __func__, fe->id); |
1271 | |
1272 | if (fe->id == 0) { |
1273 | *ts_type = DVB_USB_FE_TS_TYPE_188; |
1274 | if (dvb_usb_mxl111sf_isoc) |
1275 | mxl111sf_stream_config_isoc(stream, endpoint: 6, framesperurb: 24, framesize: 3072); |
1276 | else |
1277 | mxl111sf_stream_config_bulk(stream, endpoint: 6); |
1278 | } else if (fe->id == 1) { |
1279 | *ts_type = DVB_USB_FE_TS_TYPE_188; |
1280 | if (dvb_usb_mxl111sf_isoc) |
1281 | mxl111sf_stream_config_isoc(stream, endpoint: 4, framesperurb: 96, framesize: 564); |
1282 | else |
1283 | mxl111sf_stream_config_bulk(stream, endpoint: 4); |
1284 | } else if (fe->id == 2 && dvb_usb_mxl111sf_spi) { |
1285 | *ts_type = DVB_USB_FE_TS_TYPE_RAW; |
1286 | if (dvb_usb_mxl111sf_isoc) |
1287 | mxl111sf_stream_config_isoc(stream, endpoint: 5, framesperurb: 96, framesize: 200); |
1288 | else |
1289 | mxl111sf_stream_config_bulk(stream, endpoint: 5); |
1290 | } else if (fe->id == 2 && !dvb_usb_mxl111sf_spi) { |
1291 | *ts_type = DVB_USB_FE_TS_TYPE_RAW; |
1292 | if (dvb_usb_mxl111sf_isoc) |
1293 | mxl111sf_stream_config_isoc(stream, endpoint: 6, framesperurb: 24, framesize: 3072); |
1294 | else |
1295 | mxl111sf_stream_config_bulk(stream, endpoint: 6); |
1296 | } |
1297 | return 0; |
1298 | } |
1299 | |
1300 | static int mxl111sf_streaming_ctrl_mercury(struct dvb_frontend *fe, int onoff) |
1301 | { |
1302 | pr_debug("%s: fe=%d onoff=%d\n" , __func__, fe->id, onoff); |
1303 | |
1304 | if (fe->id == 0) |
1305 | return mxl111sf_ep6_streaming_ctrl(fe, onoff); |
1306 | else if (fe->id == 1) |
1307 | return mxl111sf_ep4_streaming_ctrl(fe, onoff); |
1308 | else if (fe->id == 2 && dvb_usb_mxl111sf_spi) |
1309 | return mxl111sf_ep5_streaming_ctrl(fe, onoff); |
1310 | else if (fe->id == 2 && !dvb_usb_mxl111sf_spi) |
1311 | return mxl111sf_ep6_streaming_ctrl(fe, onoff); |
1312 | return 0; |
1313 | } |
1314 | |
1315 | static struct dvb_usb_device_properties mxl111sf_props_mercury = { |
1316 | .driver_name = KBUILD_MODNAME, |
1317 | .owner = THIS_MODULE, |
1318 | .adapter_nr = adapter_nr, |
1319 | .size_of_priv = sizeof(struct mxl111sf_state), |
1320 | |
1321 | .generic_bulk_ctrl_endpoint = 0x02, |
1322 | .generic_bulk_ctrl_endpoint_response = 0x81, |
1323 | |
1324 | .probe = mxl111sf_probe, |
1325 | .i2c_algo = &mxl111sf_i2c_algo, |
1326 | .frontend_attach = mxl111sf_frontend_attach_mercury, |
1327 | .tuner_attach = mxl111sf_attach_tuner, |
1328 | .init = mxl111sf_init, |
1329 | .streaming_ctrl = mxl111sf_streaming_ctrl_mercury, |
1330 | .get_stream_config = mxl111sf_get_stream_config_mercury, |
1331 | |
1332 | .num_adapters = 1, |
1333 | .adapter = { |
1334 | { |
1335 | .stream = DVB_USB_STREAM_ISOC(6, 5, 24, 3072, 1), |
1336 | } |
1337 | } |
1338 | }; |
1339 | |
1340 | /* mercury mh mxl111sf lg2161 |
1341 | * tp bulk EP4/BULK/5/8192 EP6/BULK/5/8192/RAW |
1342 | * tp isoc EP4/ISOC/5/96/564 EP6/ISOC/5/24/3072/RAW |
1343 | * spi bulk EP4/BULK/5/8192 EP5/BULK/5/8192/RAW |
1344 | * spi isoc EP4/ISOC/5/96/564 EP5/ISOC/5/96/200/RAW |
1345 | */ |
1346 | static int mxl111sf_get_stream_config_mercury_mh(struct dvb_frontend *fe, |
1347 | u8 *ts_type, struct usb_data_stream_properties *stream) |
1348 | { |
1349 | pr_debug("%s: fe=%d\n" , __func__, fe->id); |
1350 | |
1351 | if (fe->id == 0) { |
1352 | *ts_type = DVB_USB_FE_TS_TYPE_188; |
1353 | if (dvb_usb_mxl111sf_isoc) |
1354 | mxl111sf_stream_config_isoc(stream, endpoint: 4, framesperurb: 96, framesize: 564); |
1355 | else |
1356 | mxl111sf_stream_config_bulk(stream, endpoint: 4); |
1357 | } else if (fe->id == 1 && dvb_usb_mxl111sf_spi) { |
1358 | *ts_type = DVB_USB_FE_TS_TYPE_RAW; |
1359 | if (dvb_usb_mxl111sf_isoc) |
1360 | mxl111sf_stream_config_isoc(stream, endpoint: 5, framesperurb: 96, framesize: 200); |
1361 | else |
1362 | mxl111sf_stream_config_bulk(stream, endpoint: 5); |
1363 | } else if (fe->id == 1 && !dvb_usb_mxl111sf_spi) { |
1364 | *ts_type = DVB_USB_FE_TS_TYPE_RAW; |
1365 | if (dvb_usb_mxl111sf_isoc) |
1366 | mxl111sf_stream_config_isoc(stream, endpoint: 6, framesperurb: 24, framesize: 3072); |
1367 | else |
1368 | mxl111sf_stream_config_bulk(stream, endpoint: 6); |
1369 | } |
1370 | return 0; |
1371 | } |
1372 | |
1373 | static int mxl111sf_streaming_ctrl_mercury_mh(struct dvb_frontend *fe, int onoff) |
1374 | { |
1375 | pr_debug("%s: fe=%d onoff=%d\n" , __func__, fe->id, onoff); |
1376 | |
1377 | if (fe->id == 0) |
1378 | return mxl111sf_ep4_streaming_ctrl(fe, onoff); |
1379 | else if (fe->id == 1 && dvb_usb_mxl111sf_spi) |
1380 | return mxl111sf_ep5_streaming_ctrl(fe, onoff); |
1381 | else if (fe->id == 1 && !dvb_usb_mxl111sf_spi) |
1382 | return mxl111sf_ep6_streaming_ctrl(fe, onoff); |
1383 | return 0; |
1384 | } |
1385 | |
1386 | static struct dvb_usb_device_properties mxl111sf_props_mercury_mh = { |
1387 | .driver_name = KBUILD_MODNAME, |
1388 | .owner = THIS_MODULE, |
1389 | .adapter_nr = adapter_nr, |
1390 | .size_of_priv = sizeof(struct mxl111sf_state), |
1391 | |
1392 | .generic_bulk_ctrl_endpoint = 0x02, |
1393 | .generic_bulk_ctrl_endpoint_response = 0x81, |
1394 | |
1395 | .probe = mxl111sf_probe, |
1396 | .i2c_algo = &mxl111sf_i2c_algo, |
1397 | .frontend_attach = mxl111sf_frontend_attach_mercury_mh, |
1398 | .tuner_attach = mxl111sf_attach_tuner, |
1399 | .init = mxl111sf_init, |
1400 | .streaming_ctrl = mxl111sf_streaming_ctrl_mercury_mh, |
1401 | .get_stream_config = mxl111sf_get_stream_config_mercury_mh, |
1402 | |
1403 | .num_adapters = 1, |
1404 | .adapter = { |
1405 | { |
1406 | .stream = DVB_USB_STREAM_ISOC(6, 5, 24, 3072, 1), |
1407 | } |
1408 | } |
1409 | }; |
1410 | |
1411 | static const struct usb_device_id mxl111sf_id_table[] = { |
1412 | { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc600, &mxl111sf_props_atsc_mh, "Hauppauge 126xxx ATSC+" , NULL) }, |
1413 | { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc601, &mxl111sf_props_atsc, "Hauppauge 126xxx ATSC" , NULL) }, |
1414 | { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc602, &mxl111sf_props_mh, "HCW 126xxx" , NULL) }, |
1415 | { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc603, &mxl111sf_props_atsc_mh, "Hauppauge 126xxx ATSC+" , NULL) }, |
1416 | { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc604, &mxl111sf_props_dvbt, "Hauppauge 126xxx DVBT" , NULL) }, |
1417 | { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc609, &mxl111sf_props_atsc, "Hauppauge 126xxx ATSC" , NULL) }, |
1418 | { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc60a, &mxl111sf_props_mh, "HCW 126xxx" , NULL) }, |
1419 | { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc60b, &mxl111sf_props_atsc_mh, "Hauppauge 126xxx ATSC+" , NULL) }, |
1420 | { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc60c, &mxl111sf_props_dvbt, "Hauppauge 126xxx DVBT" , NULL) }, |
1421 | { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc653, &mxl111sf_props_atsc_mh, "Hauppauge 126xxx ATSC+" , NULL) }, |
1422 | { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc65b, &mxl111sf_props_atsc_mh, "Hauppauge 126xxx ATSC+" , NULL) }, |
1423 | { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xb700, &mxl111sf_props_atsc_mh, "Hauppauge 117xxx ATSC+" , NULL) }, |
1424 | { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xb701, &mxl111sf_props_atsc, "Hauppauge 126xxx ATSC" , NULL) }, |
1425 | { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xb702, &mxl111sf_props_mh, "HCW 117xxx" , NULL) }, |
1426 | { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xb703, &mxl111sf_props_atsc_mh, "Hauppauge 117xxx ATSC+" , NULL) }, |
1427 | { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xb704, &mxl111sf_props_dvbt, "Hauppauge 117xxx DVBT" , NULL) }, |
1428 | { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xb753, &mxl111sf_props_atsc_mh, "Hauppauge 117xxx ATSC+" , NULL) }, |
1429 | { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xb763, &mxl111sf_props_atsc_mh, "Hauppauge 117xxx ATSC+" , NULL) }, |
1430 | { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xb764, &mxl111sf_props_dvbt, "Hauppauge 117xxx DVBT" , NULL) }, |
1431 | { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xd853, &mxl111sf_props_mercury, "Hauppauge Mercury" , NULL) }, |
1432 | { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xd854, &mxl111sf_props_dvbt, "Hauppauge 138xxx DVBT" , NULL) }, |
1433 | { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xd863, &mxl111sf_props_mercury, "Hauppauge Mercury" , NULL) }, |
1434 | { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xd864, &mxl111sf_props_dvbt, "Hauppauge 138xxx DVBT" , NULL) }, |
1435 | { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8d3, &mxl111sf_props_mercury, "Hauppauge Mercury" , NULL) }, |
1436 | { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8d4, &mxl111sf_props_dvbt, "Hauppauge 138xxx DVBT" , NULL) }, |
1437 | { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8e3, &mxl111sf_props_mercury, "Hauppauge Mercury" , NULL) }, |
1438 | { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8e4, &mxl111sf_props_dvbt, "Hauppauge 138xxx DVBT" , NULL) }, |
1439 | { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8ff, &mxl111sf_props_mercury, "Hauppauge Mercury" , NULL) }, |
1440 | { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc612, &mxl111sf_props_mercury_mh, "Hauppauge 126xxx" , NULL) }, |
1441 | { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc613, &mxl111sf_props_mercury, "Hauppauge WinTV-Aero-M" , NULL) }, |
1442 | { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc61a, &mxl111sf_props_mercury_mh, "Hauppauge 126xxx" , NULL) }, |
1443 | { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc61b, &mxl111sf_props_mercury, "Hauppauge WinTV-Aero-M" , NULL) }, |
1444 | { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xb757, &mxl111sf_props_atsc_mh, "Hauppauge 117xxx ATSC+" , NULL) }, |
1445 | { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xb767, &mxl111sf_props_atsc_mh, "Hauppauge 117xxx ATSC+" , NULL) }, |
1446 | { } |
1447 | }; |
1448 | MODULE_DEVICE_TABLE(usb, mxl111sf_id_table); |
1449 | |
1450 | static struct usb_driver mxl111sf_usb_driver = { |
1451 | .name = KBUILD_MODNAME, |
1452 | .id_table = mxl111sf_id_table, |
1453 | .probe = dvb_usbv2_probe, |
1454 | .disconnect = dvb_usbv2_disconnect, |
1455 | .suspend = dvb_usbv2_suspend, |
1456 | .resume = dvb_usbv2_resume, |
1457 | .no_dynamic_id = 1, |
1458 | .soft_unbind = 1, |
1459 | }; |
1460 | |
1461 | module_usb_driver(mxl111sf_usb_driver); |
1462 | |
1463 | MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>" ); |
1464 | MODULE_DESCRIPTION("Driver for MaxLinear MxL111SF" ); |
1465 | MODULE_VERSION("1.0" ); |
1466 | MODULE_LICENSE("GPL" ); |
1467 | |