diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/media/dvb/ttpci/budget-ci.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/media/dvb/ttpci/budget-ci.c')
-rw-r--r-- | drivers/media/dvb/ttpci/budget-ci.c | 995 |
1 files changed, 995 insertions, 0 deletions
diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c new file mode 100644 index 000000000000..521111be3558 --- /dev/null +++ b/drivers/media/dvb/ttpci/budget-ci.c | |||
@@ -0,0 +1,995 @@ | |||
1 | /* | ||
2 | * budget-ci.c: driver for the SAA7146 based Budget DVB cards | ||
3 | * | ||
4 | * Compiled from various sources by Michael Hunold <michael@mihu.de> | ||
5 | * | ||
6 | * msp430 IR support contributed by Jack Thomasson <jkt@Helius.COM> | ||
7 | * partially based on the Siemens DVB driver by Ralph+Marcus Metzler | ||
8 | * | ||
9 | * CI interface support (c) 2004 Andrew de Quincey <adq_dvb@lidskialf.net> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or | ||
12 | * modify it under the terms of the GNU General Public License | ||
13 | * as published by the Free Software Foundation; either version 2 | ||
14 | * of the License, or (at your option) any later version. | ||
15 | * | ||
16 | * | ||
17 | * This program is distributed in the hope that it will be useful, | ||
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
20 | * GNU General Public License for more details. | ||
21 | * | ||
22 | * | ||
23 | * You should have received a copy of the GNU General Public License | ||
24 | * along with this program; if not, write to the Free Software | ||
25 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
26 | * Or, point your browser to http://www.gnu.org/copyleft/gpl.html | ||
27 | * | ||
28 | * | ||
29 | * the project's page is at http://www.linuxtv.org/dvb/ | ||
30 | */ | ||
31 | |||
32 | #include "budget.h" | ||
33 | |||
34 | #include <linux/module.h> | ||
35 | #include <linux/errno.h> | ||
36 | #include <linux/slab.h> | ||
37 | #include <linux/interrupt.h> | ||
38 | #include <linux/input.h> | ||
39 | #include <linux/spinlock.h> | ||
40 | |||
41 | #include "dvb_ca_en50221.h" | ||
42 | #include "stv0299.h" | ||
43 | #include "tda1004x.h" | ||
44 | |||
45 | #define DEBIADDR_IR 0x1234 | ||
46 | #define DEBIADDR_CICONTROL 0x0000 | ||
47 | #define DEBIADDR_CIVERSION 0x4000 | ||
48 | #define DEBIADDR_IO 0x1000 | ||
49 | #define DEBIADDR_ATTR 0x3000 | ||
50 | |||
51 | #define CICONTROL_RESET 0x01 | ||
52 | #define CICONTROL_ENABLETS 0x02 | ||
53 | #define CICONTROL_CAMDETECT 0x08 | ||
54 | |||
55 | #define DEBICICTL 0x00420000 | ||
56 | #define DEBICICAM 0x02420000 | ||
57 | |||
58 | #define SLOTSTATUS_NONE 1 | ||
59 | #define SLOTSTATUS_PRESENT 2 | ||
60 | #define SLOTSTATUS_RESET 4 | ||
61 | #define SLOTSTATUS_READY 8 | ||
62 | #define SLOTSTATUS_OCCUPIED (SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY) | ||
63 | |||
64 | struct budget_ci { | ||
65 | struct budget budget; | ||
66 | struct input_dev input_dev; | ||
67 | struct tasklet_struct msp430_irq_tasklet; | ||
68 | struct tasklet_struct ciintf_irq_tasklet; | ||
69 | int slot_status; | ||
70 | struct dvb_ca_en50221 ca; | ||
71 | char ir_dev_name[50]; | ||
72 | }; | ||
73 | |||
74 | /* from reading the following remotes: | ||
75 | Zenith Universal 7 / TV Mode 807 / VCR Mode 837 | ||
76 | Hauppauge (from NOVA-CI-s box product) | ||
77 | i've taken a "middle of the road" approach and note the differences | ||
78 | */ | ||
79 | static u16 key_map[64] = { | ||
80 | /* 0x0X */ | ||
81 | KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, | ||
82 | KEY_9, | ||
83 | KEY_ENTER, | ||
84 | KEY_RED, | ||
85 | KEY_POWER, /* RADIO on Hauppauge */ | ||
86 | KEY_MUTE, | ||
87 | 0, | ||
88 | KEY_A, /* TV on Hauppauge */ | ||
89 | /* 0x1X */ | ||
90 | KEY_VOLUMEUP, KEY_VOLUMEDOWN, | ||
91 | 0, 0, | ||
92 | KEY_B, | ||
93 | 0, 0, 0, 0, 0, 0, 0, | ||
94 | KEY_UP, KEY_DOWN, | ||
95 | KEY_OPTION, /* RESERVED on Hauppauge */ | ||
96 | KEY_BREAK, | ||
97 | /* 0x2X */ | ||
98 | KEY_CHANNELUP, KEY_CHANNELDOWN, | ||
99 | KEY_PREVIOUS, /* Prev. Ch on Zenith, SOURCE on Hauppauge */ | ||
100 | 0, KEY_RESTART, KEY_OK, | ||
101 | KEY_CYCLEWINDOWS, /* MINIMIZE on Hauppauge */ | ||
102 | 0, | ||
103 | KEY_ENTER, /* VCR mode on Zenith */ | ||
104 | KEY_PAUSE, | ||
105 | 0, | ||
106 | KEY_RIGHT, KEY_LEFT, | ||
107 | 0, | ||
108 | KEY_MENU, /* FULL SCREEN on Hauppauge */ | ||
109 | 0, | ||
110 | /* 0x3X */ | ||
111 | KEY_SLOW, | ||
112 | KEY_PREVIOUS, /* VCR mode on Zenith */ | ||
113 | KEY_REWIND, | ||
114 | 0, | ||
115 | KEY_FASTFORWARD, | ||
116 | KEY_PLAY, KEY_STOP, | ||
117 | KEY_RECORD, | ||
118 | KEY_TUNER, /* TV/VCR on Zenith */ | ||
119 | 0, | ||
120 | KEY_C, | ||
121 | 0, | ||
122 | KEY_EXIT, | ||
123 | KEY_POWER2, | ||
124 | KEY_TUNER, /* VCR mode on Zenith */ | ||
125 | 0, | ||
126 | }; | ||
127 | |||
128 | static void msp430_ir_debounce(unsigned long data) | ||
129 | { | ||
130 | struct input_dev *dev = (struct input_dev *) data; | ||
131 | |||
132 | if (dev->rep[0] == 0 || dev->rep[0] == ~0) { | ||
133 | input_event(dev, EV_KEY, key_map[dev->repeat_key], !!0); | ||
134 | return; | ||
135 | } | ||
136 | |||
137 | dev->rep[0] = 0; | ||
138 | dev->timer.expires = jiffies + HZ * 350 / 1000; | ||
139 | add_timer(&dev->timer); | ||
140 | input_event(dev, EV_KEY, key_map[dev->repeat_key], 2); /* REPEAT */ | ||
141 | } | ||
142 | |||
143 | static void msp430_ir_interrupt(unsigned long data) | ||
144 | { | ||
145 | struct budget_ci *budget_ci = (struct budget_ci *) data; | ||
146 | struct input_dev *dev = &budget_ci->input_dev; | ||
147 | unsigned int code = | ||
148 | ttpci_budget_debiread(&budget_ci->budget, DEBINOSWAP, DEBIADDR_IR, 2, 1, 0) >> 8; | ||
149 | |||
150 | if (code & 0x40) { | ||
151 | code &= 0x3f; | ||
152 | |||
153 | if (timer_pending(&dev->timer)) { | ||
154 | if (code == dev->repeat_key) { | ||
155 | ++dev->rep[0]; | ||
156 | return; | ||
157 | } | ||
158 | del_timer(&dev->timer); | ||
159 | input_event(dev, EV_KEY, key_map[dev->repeat_key], !!0); | ||
160 | } | ||
161 | |||
162 | if (!key_map[code]) { | ||
163 | printk("DVB (%s): no key for %02x!\n", __FUNCTION__, code); | ||
164 | return; | ||
165 | } | ||
166 | |||
167 | /* initialize debounce and repeat */ | ||
168 | dev->repeat_key = code; | ||
169 | /* Zenith remote _always_ sends 2 sequences */ | ||
170 | dev->rep[0] = ~0; | ||
171 | /* 350 milliseconds */ | ||
172 | dev->timer.expires = jiffies + HZ * 350 / 1000; | ||
173 | /* MAKE */ | ||
174 | input_event(dev, EV_KEY, key_map[code], !0); | ||
175 | add_timer(&dev->timer); | ||
176 | } | ||
177 | } | ||
178 | |||
179 | static int msp430_ir_init(struct budget_ci *budget_ci) | ||
180 | { | ||
181 | struct saa7146_dev *saa = budget_ci->budget.dev; | ||
182 | int i; | ||
183 | |||
184 | memset(&budget_ci->input_dev, 0, sizeof(struct input_dev)); | ||
185 | |||
186 | sprintf(budget_ci->ir_dev_name, "Budget-CI dvb ir receiver %s", saa->name); | ||
187 | budget_ci->input_dev.name = budget_ci->ir_dev_name; | ||
188 | |||
189 | set_bit(EV_KEY, budget_ci->input_dev.evbit); | ||
190 | |||
191 | for (i = 0; i < sizeof(key_map) / sizeof(*key_map); i++) | ||
192 | if (key_map[i]) | ||
193 | set_bit(key_map[i], budget_ci->input_dev.keybit); | ||
194 | |||
195 | input_register_device(&budget_ci->input_dev); | ||
196 | |||
197 | budget_ci->input_dev.timer.function = msp430_ir_debounce; | ||
198 | |||
199 | saa7146_write(saa, IER, saa7146_read(saa, IER) | MASK_06); | ||
200 | |||
201 | saa7146_setgpio(saa, 3, SAA7146_GPIO_IRQHI); | ||
202 | |||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | static void msp430_ir_deinit(struct budget_ci *budget_ci) | ||
207 | { | ||
208 | struct saa7146_dev *saa = budget_ci->budget.dev; | ||
209 | struct input_dev *dev = &budget_ci->input_dev; | ||
210 | |||
211 | saa7146_write(saa, IER, saa7146_read(saa, IER) & ~MASK_06); | ||
212 | saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT); | ||
213 | |||
214 | if (del_timer(&dev->timer)) | ||
215 | input_event(dev, EV_KEY, key_map[dev->repeat_key], !!0); | ||
216 | |||
217 | input_unregister_device(dev); | ||
218 | } | ||
219 | |||
220 | static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address) | ||
221 | { | ||
222 | struct budget_ci *budget_ci = (struct budget_ci *) ca->data; | ||
223 | |||
224 | if (slot != 0) | ||
225 | return -EINVAL; | ||
226 | |||
227 | return ttpci_budget_debiread(&budget_ci->budget, DEBICICAM, | ||
228 | DEBIADDR_ATTR | (address & 0xfff), 1, 1, 0); | ||
229 | } | ||
230 | |||
231 | static int ciintf_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address, u8 value) | ||
232 | { | ||
233 | struct budget_ci *budget_ci = (struct budget_ci *) ca->data; | ||
234 | |||
235 | if (slot != 0) | ||
236 | return -EINVAL; | ||
237 | |||
238 | return ttpci_budget_debiwrite(&budget_ci->budget, DEBICICAM, | ||
239 | DEBIADDR_ATTR | (address & 0xfff), 1, value, 1, 0); | ||
240 | } | ||
241 | |||
242 | static int ciintf_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address) | ||
243 | { | ||
244 | struct budget_ci *budget_ci = (struct budget_ci *) ca->data; | ||
245 | |||
246 | if (slot != 0) | ||
247 | return -EINVAL; | ||
248 | |||
249 | return ttpci_budget_debiread(&budget_ci->budget, DEBICICAM, | ||
250 | DEBIADDR_IO | (address & 3), 1, 1, 0); | ||
251 | } | ||
252 | |||
253 | static int ciintf_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address, u8 value) | ||
254 | { | ||
255 | struct budget_ci *budget_ci = (struct budget_ci *) ca->data; | ||
256 | |||
257 | if (slot != 0) | ||
258 | return -EINVAL; | ||
259 | |||
260 | return ttpci_budget_debiwrite(&budget_ci->budget, DEBICICAM, | ||
261 | DEBIADDR_IO | (address & 3), 1, value, 1, 0); | ||
262 | } | ||
263 | |||
264 | static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot) | ||
265 | { | ||
266 | struct budget_ci *budget_ci = (struct budget_ci *) ca->data; | ||
267 | struct saa7146_dev *saa = budget_ci->budget.dev; | ||
268 | |||
269 | if (slot != 0) | ||
270 | return -EINVAL; | ||
271 | |||
272 | // trigger on RISING edge during reset so we know when READY is re-asserted | ||
273 | saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI); | ||
274 | budget_ci->slot_status = SLOTSTATUS_RESET; | ||
275 | ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 0, 1, 0); | ||
276 | msleep(1); | ||
277 | ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, | ||
278 | CICONTROL_RESET, 1, 0); | ||
279 | |||
280 | saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTHI); | ||
281 | ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); | ||
282 | return 0; | ||
283 | } | ||
284 | |||
285 | static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) | ||
286 | { | ||
287 | struct budget_ci *budget_ci = (struct budget_ci *) ca->data; | ||
288 | struct saa7146_dev *saa = budget_ci->budget.dev; | ||
289 | |||
290 | if (slot != 0) | ||
291 | return -EINVAL; | ||
292 | |||
293 | saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTHI); | ||
294 | ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); | ||
295 | return 0; | ||
296 | } | ||
297 | |||
298 | static int ciintf_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) | ||
299 | { | ||
300 | struct budget_ci *budget_ci = (struct budget_ci *) ca->data; | ||
301 | struct saa7146_dev *saa = budget_ci->budget.dev; | ||
302 | int tmp; | ||
303 | |||
304 | if (slot != 0) | ||
305 | return -EINVAL; | ||
306 | |||
307 | saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTLO); | ||
308 | |||
309 | tmp = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0); | ||
310 | ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, | ||
311 | tmp | CICONTROL_ENABLETS, 1, 0); | ||
312 | |||
313 | ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTA); | ||
314 | return 0; | ||
315 | } | ||
316 | |||
317 | static void ciintf_interrupt(unsigned long data) | ||
318 | { | ||
319 | struct budget_ci *budget_ci = (struct budget_ci *) data; | ||
320 | struct saa7146_dev *saa = budget_ci->budget.dev; | ||
321 | unsigned int flags; | ||
322 | |||
323 | // ensure we don't get spurious IRQs during initialisation | ||
324 | if (!budget_ci->budget.ci_present) | ||
325 | return; | ||
326 | |||
327 | // read the CAM status | ||
328 | flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0); | ||
329 | if (flags & CICONTROL_CAMDETECT) { | ||
330 | |||
331 | // GPIO should be set to trigger on falling edge if a CAM is present | ||
332 | saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO); | ||
333 | |||
334 | if (budget_ci->slot_status & SLOTSTATUS_NONE) { | ||
335 | // CAM insertion IRQ | ||
336 | budget_ci->slot_status = SLOTSTATUS_PRESENT; | ||
337 | dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, | ||
338 | DVB_CA_EN50221_CAMCHANGE_INSERTED); | ||
339 | |||
340 | } else if (budget_ci->slot_status & SLOTSTATUS_RESET) { | ||
341 | // CAM ready (reset completed) | ||
342 | budget_ci->slot_status = SLOTSTATUS_READY; | ||
343 | dvb_ca_en50221_camready_irq(&budget_ci->ca, 0); | ||
344 | |||
345 | } else if (budget_ci->slot_status & SLOTSTATUS_READY) { | ||
346 | // FR/DA IRQ | ||
347 | dvb_ca_en50221_frda_irq(&budget_ci->ca, 0); | ||
348 | } | ||
349 | } else { | ||
350 | |||
351 | // trigger on rising edge if a CAM is not present - when a CAM is inserted, we | ||
352 | // only want to get the IRQ when it sets READY. If we trigger on the falling edge, | ||
353 | // the CAM might not actually be ready yet. | ||
354 | saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI); | ||
355 | |||
356 | // generate a CAM removal IRQ if we haven't already | ||
357 | if (budget_ci->slot_status & SLOTSTATUS_OCCUPIED) { | ||
358 | // CAM removal IRQ | ||
359 | budget_ci->slot_status = SLOTSTATUS_NONE; | ||
360 | dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, | ||
361 | DVB_CA_EN50221_CAMCHANGE_REMOVED); | ||
362 | } | ||
363 | } | ||
364 | } | ||
365 | |||
366 | static int ciintf_init(struct budget_ci *budget_ci) | ||
367 | { | ||
368 | struct saa7146_dev *saa = budget_ci->budget.dev; | ||
369 | int flags; | ||
370 | int result; | ||
371 | |||
372 | memset(&budget_ci->ca, 0, sizeof(struct dvb_ca_en50221)); | ||
373 | |||
374 | // enable DEBI pins | ||
375 | saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16) | 0x800); | ||
376 | |||
377 | // test if it is there | ||
378 | if ((ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CIVERSION, 1, 1, 0) & 0xa0) != 0xa0) { | ||
379 | result = -ENODEV; | ||
380 | goto error; | ||
381 | } | ||
382 | // determine whether a CAM is present or not | ||
383 | flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0); | ||
384 | budget_ci->slot_status = SLOTSTATUS_NONE; | ||
385 | if (flags & CICONTROL_CAMDETECT) | ||
386 | budget_ci->slot_status = SLOTSTATUS_PRESENT; | ||
387 | |||
388 | // register CI interface | ||
389 | budget_ci->ca.owner = THIS_MODULE; | ||
390 | budget_ci->ca.read_attribute_mem = ciintf_read_attribute_mem; | ||
391 | budget_ci->ca.write_attribute_mem = ciintf_write_attribute_mem; | ||
392 | budget_ci->ca.read_cam_control = ciintf_read_cam_control; | ||
393 | budget_ci->ca.write_cam_control = ciintf_write_cam_control; | ||
394 | budget_ci->ca.slot_reset = ciintf_slot_reset; | ||
395 | budget_ci->ca.slot_shutdown = ciintf_slot_shutdown; | ||
396 | budget_ci->ca.slot_ts_enable = ciintf_slot_ts_enable; | ||
397 | budget_ci->ca.data = budget_ci; | ||
398 | if ((result = dvb_ca_en50221_init(budget_ci->budget.dvb_adapter, | ||
399 | &budget_ci->ca, | ||
400 | DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE | | ||
401 | DVB_CA_EN50221_FLAG_IRQ_FR | | ||
402 | DVB_CA_EN50221_FLAG_IRQ_DA, 1)) != 0) { | ||
403 | printk("budget_ci: CI interface detected, but initialisation failed.\n"); | ||
404 | goto error; | ||
405 | } | ||
406 | // Setup CI slot IRQ | ||
407 | tasklet_init(&budget_ci->ciintf_irq_tasklet, ciintf_interrupt, (unsigned long) budget_ci); | ||
408 | if (budget_ci->slot_status != SLOTSTATUS_NONE) { | ||
409 | saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO); | ||
410 | } else { | ||
411 | saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI); | ||
412 | } | ||
413 | saa7146_write(saa, IER, saa7146_read(saa, IER) | MASK_03); | ||
414 | ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, | ||
415 | CICONTROL_RESET, 1, 0); | ||
416 | |||
417 | // success! | ||
418 | printk("budget_ci: CI interface initialised\n"); | ||
419 | budget_ci->budget.ci_present = 1; | ||
420 | |||
421 | // forge a fake CI IRQ so the CAM state is setup correctly | ||
422 | flags = DVB_CA_EN50221_CAMCHANGE_REMOVED; | ||
423 | if (budget_ci->slot_status != SLOTSTATUS_NONE) | ||
424 | flags = DVB_CA_EN50221_CAMCHANGE_INSERTED; | ||
425 | dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, flags); | ||
426 | |||
427 | return 0; | ||
428 | |||
429 | error: | ||
430 | saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16)); | ||
431 | return result; | ||
432 | } | ||
433 | |||
434 | static void ciintf_deinit(struct budget_ci *budget_ci) | ||
435 | { | ||
436 | struct saa7146_dev *saa = budget_ci->budget.dev; | ||
437 | |||
438 | // disable CI interrupts | ||
439 | saa7146_write(saa, IER, saa7146_read(saa, IER) & ~MASK_03); | ||
440 | saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT); | ||
441 | tasklet_kill(&budget_ci->ciintf_irq_tasklet); | ||
442 | ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 0, 1, 0); | ||
443 | msleep(1); | ||
444 | ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, | ||
445 | CICONTROL_RESET, 1, 0); | ||
446 | |||
447 | // disable TS data stream to CI interface | ||
448 | saa7146_setgpio(saa, 1, SAA7146_GPIO_INPUT); | ||
449 | |||
450 | // release the CA device | ||
451 | dvb_ca_en50221_release(&budget_ci->ca); | ||
452 | |||
453 | // disable DEBI pins | ||
454 | saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16)); | ||
455 | } | ||
456 | |||
457 | static void budget_ci_irq(struct saa7146_dev *dev, u32 * isr) | ||
458 | { | ||
459 | struct budget_ci *budget_ci = (struct budget_ci *) dev->ext_priv; | ||
460 | |||
461 | dprintk(8, "dev: %p, budget_ci: %p\n", dev, budget_ci); | ||
462 | |||
463 | if (*isr & MASK_06) | ||
464 | tasklet_schedule(&budget_ci->msp430_irq_tasklet); | ||
465 | |||
466 | if (*isr & MASK_10) | ||
467 | ttpci_budget_irq10_handler(dev, isr); | ||
468 | |||
469 | if ((*isr & MASK_03) && (budget_ci->budget.ci_present)) | ||
470 | tasklet_schedule(&budget_ci->ciintf_irq_tasklet); | ||
471 | } | ||
472 | |||
473 | |||
474 | static u8 alps_bsru6_inittab[] = { | ||
475 | 0x01, 0x15, | ||
476 | 0x02, 0x00, | ||
477 | 0x03, 0x00, | ||
478 | 0x04, 0x7d, /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */ | ||
479 | 0x05, 0x35, /* I2CT = 0, SCLT = 1, SDAT = 1 */ | ||
480 | 0x06, 0x40, /* DAC not used, set to high impendance mode */ | ||
481 | 0x07, 0x00, /* DAC LSB */ | ||
482 | 0x08, 0x40, /* DiSEqC off, LNB power on OP2/LOCK pin on */ | ||
483 | 0x09, 0x00, /* FIFO */ | ||
484 | 0x0c, 0x51, /* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */ | ||
485 | 0x0d, 0x82, /* DC offset compensation = ON, beta_agc1 = 2 */ | ||
486 | 0x0e, 0x23, /* alpha_tmg = 2, beta_tmg = 3 */ | ||
487 | 0x10, 0x3f, // AGC2 0x3d | ||
488 | 0x11, 0x84, | ||
489 | 0x12, 0xb5, // Lock detect: -64 Carrier freq detect:on | ||
490 | 0x15, 0xc9, // lock detector threshold | ||
491 | 0x16, 0x00, | ||
492 | 0x17, 0x00, | ||
493 | 0x18, 0x00, | ||
494 | 0x19, 0x00, | ||
495 | 0x1a, 0x00, | ||
496 | 0x1f, 0x50, | ||
497 | 0x20, 0x00, | ||
498 | 0x21, 0x00, | ||
499 | 0x22, 0x00, | ||
500 | 0x23, 0x00, | ||
501 | 0x28, 0x00, // out imp: normal out type: parallel FEC mode:0 | ||
502 | 0x29, 0x1e, // 1/2 threshold | ||
503 | 0x2a, 0x14, // 2/3 threshold | ||
504 | 0x2b, 0x0f, // 3/4 threshold | ||
505 | 0x2c, 0x09, // 5/6 threshold | ||
506 | 0x2d, 0x05, // 7/8 threshold | ||
507 | 0x2e, 0x01, | ||
508 | 0x31, 0x1f, // test all FECs | ||
509 | 0x32, 0x19, // viterbi and synchro search | ||
510 | 0x33, 0xfc, // rs control | ||
511 | 0x34, 0x93, // error control | ||
512 | 0x0f, 0x52, | ||
513 | 0xff, 0xff | ||
514 | }; | ||
515 | |||
516 | static int alps_bsru6_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ratio) | ||
517 | { | ||
518 | u8 aclk = 0; | ||
519 | u8 bclk = 0; | ||
520 | |||
521 | if (srate < 1500000) { | ||
522 | aclk = 0xb7; | ||
523 | bclk = 0x47; | ||
524 | } else if (srate < 3000000) { | ||
525 | aclk = 0xb7; | ||
526 | bclk = 0x4b; | ||
527 | } else if (srate < 7000000) { | ||
528 | aclk = 0xb7; | ||
529 | bclk = 0x4f; | ||
530 | } else if (srate < 14000000) { | ||
531 | aclk = 0xb7; | ||
532 | bclk = 0x53; | ||
533 | } else if (srate < 30000000) { | ||
534 | aclk = 0xb6; | ||
535 | bclk = 0x53; | ||
536 | } else if (srate < 45000000) { | ||
537 | aclk = 0xb4; | ||
538 | bclk = 0x51; | ||
539 | } | ||
540 | |||
541 | stv0299_writereg(fe, 0x13, aclk); | ||
542 | stv0299_writereg(fe, 0x14, bclk); | ||
543 | stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff); | ||
544 | stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff); | ||
545 | stv0299_writereg(fe, 0x21, (ratio) & 0xf0); | ||
546 | |||
547 | return 0; | ||
548 | } | ||
549 | |||
550 | static int alps_bsru6_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) | ||
551 | { | ||
552 | struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; | ||
553 | u8 buf[4]; | ||
554 | u32 div; | ||
555 | struct i2c_msg msg = {.addr = 0x61,.flags = 0,.buf = buf,.len = sizeof(buf) }; | ||
556 | |||
557 | if ((params->frequency < 950000) || (params->frequency > 2150000)) | ||
558 | return -EINVAL; | ||
559 | |||
560 | div = (params->frequency + (125 - 1)) / 125; // round correctly | ||
561 | buf[0] = (div >> 8) & 0x7f; | ||
562 | buf[1] = div & 0xff; | ||
563 | buf[2] = 0x80 | ((div & 0x18000) >> 10) | 4; | ||
564 | buf[3] = 0xC4; | ||
565 | |||
566 | if (params->frequency > 1530000) | ||
567 | buf[3] = 0xc0; | ||
568 | |||
569 | if (i2c_transfer(&budget_ci->budget.i2c_adap, &msg, 1) != 1) | ||
570 | return -EIO; | ||
571 | return 0; | ||
572 | } | ||
573 | |||
574 | static struct stv0299_config alps_bsru6_config = { | ||
575 | |||
576 | .demod_address = 0x68, | ||
577 | .inittab = alps_bsru6_inittab, | ||
578 | .mclk = 88000000UL, | ||
579 | .invert = 1, | ||
580 | .enhanced_tuning = 0, | ||
581 | .skip_reinit = 0, | ||
582 | .lock_output = STV0229_LOCKOUTPUT_1, | ||
583 | .volt13_op0_op1 = STV0299_VOLT13_OP1, | ||
584 | .min_delay_ms = 100, | ||
585 | .set_symbol_rate = alps_bsru6_set_symbol_rate, | ||
586 | .pll_set = alps_bsru6_pll_set, | ||
587 | }; | ||
588 | |||
589 | |||
590 | |||
591 | |||
592 | static u8 philips_su1278_tt_inittab[] = { | ||
593 | 0x01, 0x0f, | ||
594 | 0x02, 0x30, | ||
595 | 0x03, 0x00, | ||
596 | 0x04, 0x5b, | ||
597 | 0x05, 0x85, | ||
598 | 0x06, 0x02, | ||
599 | 0x07, 0x00, | ||
600 | 0x08, 0x02, | ||
601 | 0x09, 0x00, | ||
602 | 0x0C, 0x01, | ||
603 | 0x0D, 0x81, | ||
604 | 0x0E, 0x44, | ||
605 | 0x0f, 0x14, | ||
606 | 0x10, 0x3c, | ||
607 | 0x11, 0x84, | ||
608 | 0x12, 0xda, | ||
609 | 0x13, 0x97, | ||
610 | 0x14, 0x95, | ||
611 | 0x15, 0xc9, | ||
612 | 0x16, 0x19, | ||
613 | 0x17, 0x8c, | ||
614 | 0x18, 0x59, | ||
615 | 0x19, 0xf8, | ||
616 | 0x1a, 0xfe, | ||
617 | 0x1c, 0x7f, | ||
618 | 0x1d, 0x00, | ||
619 | 0x1e, 0x00, | ||
620 | 0x1f, 0x50, | ||
621 | 0x20, 0x00, | ||
622 | 0x21, 0x00, | ||
623 | 0x22, 0x00, | ||
624 | 0x23, 0x00, | ||
625 | 0x28, 0x00, | ||
626 | 0x29, 0x28, | ||
627 | 0x2a, 0x14, | ||
628 | 0x2b, 0x0f, | ||
629 | 0x2c, 0x09, | ||
630 | 0x2d, 0x09, | ||
631 | 0x31, 0x1f, | ||
632 | 0x32, 0x19, | ||
633 | 0x33, 0xfc, | ||
634 | 0x34, 0x93, | ||
635 | 0xff, 0xff | ||
636 | }; | ||
637 | |||
638 | static int philips_su1278_tt_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ratio) | ||
639 | { | ||
640 | stv0299_writereg(fe, 0x0e, 0x44); | ||
641 | if (srate >= 10000000) { | ||
642 | stv0299_writereg(fe, 0x13, 0x97); | ||
643 | stv0299_writereg(fe, 0x14, 0x95); | ||
644 | stv0299_writereg(fe, 0x15, 0xc9); | ||
645 | stv0299_writereg(fe, 0x17, 0x8c); | ||
646 | stv0299_writereg(fe, 0x1a, 0xfe); | ||
647 | stv0299_writereg(fe, 0x1c, 0x7f); | ||
648 | stv0299_writereg(fe, 0x2d, 0x09); | ||
649 | } else { | ||
650 | stv0299_writereg(fe, 0x13, 0x99); | ||
651 | stv0299_writereg(fe, 0x14, 0x8d); | ||
652 | stv0299_writereg(fe, 0x15, 0xce); | ||
653 | stv0299_writereg(fe, 0x17, 0x43); | ||
654 | stv0299_writereg(fe, 0x1a, 0x1d); | ||
655 | stv0299_writereg(fe, 0x1c, 0x12); | ||
656 | stv0299_writereg(fe, 0x2d, 0x05); | ||
657 | } | ||
658 | stv0299_writereg(fe, 0x0e, 0x23); | ||
659 | stv0299_writereg(fe, 0x0f, 0x94); | ||
660 | stv0299_writereg(fe, 0x10, 0x39); | ||
661 | stv0299_writereg(fe, 0x15, 0xc9); | ||
662 | |||
663 | stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff); | ||
664 | stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff); | ||
665 | stv0299_writereg(fe, 0x21, (ratio) & 0xf0); | ||
666 | |||
667 | return 0; | ||
668 | } | ||
669 | |||
670 | static int philips_su1278_tt_pll_set(struct dvb_frontend *fe, | ||
671 | struct dvb_frontend_parameters *params) | ||
672 | { | ||
673 | struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; | ||
674 | u32 div; | ||
675 | u8 buf[4]; | ||
676 | struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = buf,.len = sizeof(buf) }; | ||
677 | |||
678 | if ((params->frequency < 950000) || (params->frequency > 2150000)) | ||
679 | return -EINVAL; | ||
680 | |||
681 | div = (params->frequency + (500 - 1)) / 500; // round correctly | ||
682 | buf[0] = (div >> 8) & 0x7f; | ||
683 | buf[1] = div & 0xff; | ||
684 | buf[2] = 0x80 | ((div & 0x18000) >> 10) | 2; | ||
685 | buf[3] = 0x20; | ||
686 | |||
687 | if (params->u.qpsk.symbol_rate < 4000000) | ||
688 | buf[3] |= 1; | ||
689 | |||
690 | if (params->frequency < 1250000) | ||
691 | buf[3] |= 0; | ||
692 | else if (params->frequency < 1550000) | ||
693 | buf[3] |= 0x40; | ||
694 | else if (params->frequency < 2050000) | ||
695 | buf[3] |= 0x80; | ||
696 | else if (params->frequency < 2150000) | ||
697 | buf[3] |= 0xC0; | ||
698 | |||
699 | if (i2c_transfer(&budget_ci->budget.i2c_adap, &msg, 1) != 1) | ||
700 | return -EIO; | ||
701 | return 0; | ||
702 | } | ||
703 | |||
704 | static struct stv0299_config philips_su1278_tt_config = { | ||
705 | |||
706 | .demod_address = 0x68, | ||
707 | .inittab = philips_su1278_tt_inittab, | ||
708 | .mclk = 64000000UL, | ||
709 | .invert = 0, | ||
710 | .enhanced_tuning = 1, | ||
711 | .skip_reinit = 1, | ||
712 | .lock_output = STV0229_LOCKOUTPUT_1, | ||
713 | .volt13_op0_op1 = STV0299_VOLT13_OP1, | ||
714 | .min_delay_ms = 50, | ||
715 | .set_symbol_rate = philips_su1278_tt_set_symbol_rate, | ||
716 | .pll_set = philips_su1278_tt_pll_set, | ||
717 | }; | ||
718 | |||
719 | |||
720 | |||
721 | static int philips_tdm1316l_pll_init(struct dvb_frontend *fe) | ||
722 | { | ||
723 | struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; | ||
724 | static u8 td1316_init[] = { 0x0b, 0xf5, 0x85, 0xab }; | ||
725 | static u8 disable_mc44BC374c[] = { 0x1d, 0x74, 0xa0, 0x68 }; | ||
726 | struct i2c_msg tuner_msg = {.addr = 0x63,.flags = 0,.buf = td1316_init,.len = | ||
727 | sizeof(td1316_init) }; | ||
728 | |||
729 | // setup PLL configuration | ||
730 | if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) | ||
731 | return -EIO; | ||
732 | msleep(1); | ||
733 | |||
734 | // disable the mc44BC374c (do not check for errors) | ||
735 | tuner_msg.addr = 0x65; | ||
736 | tuner_msg.buf = disable_mc44BC374c; | ||
737 | tuner_msg.len = sizeof(disable_mc44BC374c); | ||
738 | if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) { | ||
739 | i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1); | ||
740 | } | ||
741 | |||
742 | return 0; | ||
743 | } | ||
744 | |||
745 | static int philips_tdm1316l_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) | ||
746 | { | ||
747 | struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; | ||
748 | u8 tuner_buf[4]; | ||
749 | struct i2c_msg tuner_msg = {.addr = 0x63,.flags = 0,.buf = tuner_buf,.len = sizeof(tuner_buf) }; | ||
750 | int tuner_frequency = 0; | ||
751 | u8 band, cp, filter; | ||
752 | |||
753 | // determine charge pump | ||
754 | tuner_frequency = params->frequency + 36130000; | ||
755 | if (tuner_frequency < 87000000) | ||
756 | return -EINVAL; | ||
757 | else if (tuner_frequency < 130000000) | ||
758 | cp = 3; | ||
759 | else if (tuner_frequency < 160000000) | ||
760 | cp = 5; | ||
761 | else if (tuner_frequency < 200000000) | ||
762 | cp = 6; | ||
763 | else if (tuner_frequency < 290000000) | ||
764 | cp = 3; | ||
765 | else if (tuner_frequency < 420000000) | ||
766 | cp = 5; | ||
767 | else if (tuner_frequency < 480000000) | ||
768 | cp = 6; | ||
769 | else if (tuner_frequency < 620000000) | ||
770 | cp = 3; | ||
771 | else if (tuner_frequency < 830000000) | ||
772 | cp = 5; | ||
773 | else if (tuner_frequency < 895000000) | ||
774 | cp = 7; | ||
775 | else | ||
776 | return -EINVAL; | ||
777 | |||
778 | // determine band | ||
779 | if (params->frequency < 49000000) | ||
780 | return -EINVAL; | ||
781 | else if (params->frequency < 159000000) | ||
782 | band = 1; | ||
783 | else if (params->frequency < 444000000) | ||
784 | band = 2; | ||
785 | else if (params->frequency < 861000000) | ||
786 | band = 4; | ||
787 | else | ||
788 | return -EINVAL; | ||
789 | |||
790 | // setup PLL filter and TDA9889 | ||
791 | switch (params->u.ofdm.bandwidth) { | ||
792 | case BANDWIDTH_6_MHZ: | ||
793 | tda1004x_write_byte(fe, 0x0C, 0x14); | ||
794 | filter = 0; | ||
795 | break; | ||
796 | |||
797 | case BANDWIDTH_7_MHZ: | ||
798 | tda1004x_write_byte(fe, 0x0C, 0x80); | ||
799 | filter = 0; | ||
800 | break; | ||
801 | |||
802 | case BANDWIDTH_8_MHZ: | ||
803 | tda1004x_write_byte(fe, 0x0C, 0x14); | ||
804 | filter = 1; | ||
805 | break; | ||
806 | |||
807 | default: | ||
808 | return -EINVAL; | ||
809 | } | ||
810 | |||
811 | // calculate divisor | ||
812 | // ((36130000+((1000000/6)/2)) + Finput)/(1000000/6) | ||
813 | tuner_frequency = (((params->frequency / 1000) * 6) + 217280) / 1000; | ||
814 | |||
815 | // setup tuner buffer | ||
816 | tuner_buf[0] = tuner_frequency >> 8; | ||
817 | tuner_buf[1] = tuner_frequency & 0xff; | ||
818 | tuner_buf[2] = 0xca; | ||
819 | tuner_buf[3] = (cp << 5) | (filter << 3) | band; | ||
820 | |||
821 | if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) | ||
822 | return -EIO; | ||
823 | |||
824 | msleep(1); | ||
825 | return 0; | ||
826 | } | ||
827 | |||
828 | static int philips_tdm1316l_request_firmware(struct dvb_frontend *fe, | ||
829 | const struct firmware **fw, char *name) | ||
830 | { | ||
831 | struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; | ||
832 | |||
833 | return request_firmware(fw, name, &budget_ci->budget.dev->pci->dev); | ||
834 | } | ||
835 | |||
836 | static struct tda1004x_config philips_tdm1316l_config = { | ||
837 | |||
838 | .demod_address = 0x8, | ||
839 | .invert = 0, | ||
840 | .invert_oclk = 0, | ||
841 | .pll_init = philips_tdm1316l_pll_init, | ||
842 | .pll_set = philips_tdm1316l_pll_set, | ||
843 | .request_firmware = philips_tdm1316l_request_firmware, | ||
844 | }; | ||
845 | |||
846 | |||
847 | |||
848 | static void frontend_init(struct budget_ci *budget_ci) | ||
849 | { | ||
850 | switch (budget_ci->budget.dev->pci->subsystem_device) { | ||
851 | case 0x100c: // Hauppauge/TT Nova-CI budget (stv0299/ALPS BSRU6(tsa5059)) | ||
852 | budget_ci->budget.dvb_frontend = | ||
853 | stv0299_attach(&alps_bsru6_config, &budget_ci->budget.i2c_adap); | ||
854 | if (budget_ci->budget.dvb_frontend) { | ||
855 | break; | ||
856 | } | ||
857 | break; | ||
858 | |||
859 | case 0x100f: // Hauppauge/TT Nova-CI budget (stv0299b/Philips su1278(tsa5059)) | ||
860 | budget_ci->budget.dvb_frontend = | ||
861 | stv0299_attach(&philips_su1278_tt_config, &budget_ci->budget.i2c_adap); | ||
862 | if (budget_ci->budget.dvb_frontend) { | ||
863 | break; | ||
864 | } | ||
865 | break; | ||
866 | |||
867 | case 0x1011: // Hauppauge/TT Nova-T budget (tda10045/Philips tdm1316l(tda6651tt) + TDA9889) | ||
868 | budget_ci->budget.dvb_frontend = | ||
869 | tda10045_attach(&philips_tdm1316l_config, &budget_ci->budget.i2c_adap); | ||
870 | if (budget_ci->budget.dvb_frontend) { | ||
871 | break; | ||
872 | } | ||
873 | break; | ||
874 | } | ||
875 | |||
876 | if (budget_ci->budget.dvb_frontend == NULL) { | ||
877 | printk("budget-ci: A frontend driver was not found for device %04x/%04x subsystem %04x/%04x\n", | ||
878 | budget_ci->budget.dev->pci->vendor, | ||
879 | budget_ci->budget.dev->pci->device, | ||
880 | budget_ci->budget.dev->pci->subsystem_vendor, | ||
881 | budget_ci->budget.dev->pci->subsystem_device); | ||
882 | } else { | ||
883 | if (dvb_register_frontend | ||
884 | (budget_ci->budget.dvb_adapter, budget_ci->budget.dvb_frontend)) { | ||
885 | printk("budget-ci: Frontend registration failed!\n"); | ||
886 | if (budget_ci->budget.dvb_frontend->ops->release) | ||
887 | budget_ci->budget.dvb_frontend->ops->release(budget_ci->budget.dvb_frontend); | ||
888 | budget_ci->budget.dvb_frontend = NULL; | ||
889 | } | ||
890 | } | ||
891 | } | ||
892 | |||
893 | static int budget_ci_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info) | ||
894 | { | ||
895 | struct budget_ci *budget_ci; | ||
896 | int err; | ||
897 | |||
898 | if (!(budget_ci = kmalloc(sizeof(struct budget_ci), GFP_KERNEL))) | ||
899 | return -ENOMEM; | ||
900 | |||
901 | dprintk(2, "budget_ci: %p\n", budget_ci); | ||
902 | |||
903 | budget_ci->budget.ci_present = 0; | ||
904 | |||
905 | dev->ext_priv = budget_ci; | ||
906 | |||
907 | if ((err = ttpci_budget_init(&budget_ci->budget, dev, info, THIS_MODULE))) { | ||
908 | kfree(budget_ci); | ||
909 | return err; | ||
910 | } | ||
911 | |||
912 | tasklet_init(&budget_ci->msp430_irq_tasklet, msp430_ir_interrupt, | ||
913 | (unsigned long) budget_ci); | ||
914 | |||
915 | msp430_ir_init(budget_ci); | ||
916 | |||
917 | ciintf_init(budget_ci); | ||
918 | |||
919 | budget_ci->budget.dvb_adapter->priv = budget_ci; | ||
920 | frontend_init(budget_ci); | ||
921 | |||
922 | return 0; | ||
923 | } | ||
924 | |||
925 | static int budget_ci_detach(struct saa7146_dev *dev) | ||
926 | { | ||
927 | struct budget_ci *budget_ci = (struct budget_ci *) dev->ext_priv; | ||
928 | struct saa7146_dev *saa = budget_ci->budget.dev; | ||
929 | int err; | ||
930 | |||
931 | if (budget_ci->budget.ci_present) | ||
932 | ciintf_deinit(budget_ci); | ||
933 | if (budget_ci->budget.dvb_frontend) | ||
934 | dvb_unregister_frontend(budget_ci->budget.dvb_frontend); | ||
935 | err = ttpci_budget_deinit(&budget_ci->budget); | ||
936 | |||
937 | tasklet_kill(&budget_ci->msp430_irq_tasklet); | ||
938 | |||
939 | msp430_ir_deinit(budget_ci); | ||
940 | |||
941 | // disable frontend and CI interface | ||
942 | saa7146_setgpio(saa, 2, SAA7146_GPIO_INPUT); | ||
943 | |||
944 | kfree(budget_ci); | ||
945 | |||
946 | return err; | ||
947 | } | ||
948 | |||
949 | static struct saa7146_extension budget_extension; | ||
950 | |||
951 | MAKE_BUDGET_INFO(ttbci, "TT-Budget/WinTV-NOVA-CI PCI", BUDGET_TT_HW_DISEQC); | ||
952 | MAKE_BUDGET_INFO(ttbt2, "TT-Budget/WinTV-NOVA-T PCI", BUDGET_TT); | ||
953 | |||
954 | static struct pci_device_id pci_tbl[] = { | ||
955 | MAKE_EXTENSION_PCI(ttbci, 0x13c2, 0x100c), | ||
956 | MAKE_EXTENSION_PCI(ttbci, 0x13c2, 0x100f), | ||
957 | MAKE_EXTENSION_PCI(ttbt2, 0x13c2, 0x1011), | ||
958 | { | ||
959 | .vendor = 0, | ||
960 | } | ||
961 | }; | ||
962 | |||
963 | MODULE_DEVICE_TABLE(pci, pci_tbl); | ||
964 | |||
965 | static struct saa7146_extension budget_extension = { | ||
966 | .name = "budget_ci dvb\0", | ||
967 | .flags = 0, | ||
968 | |||
969 | .module = THIS_MODULE, | ||
970 | .pci_tbl = &pci_tbl[0], | ||
971 | .attach = budget_ci_attach, | ||
972 | .detach = budget_ci_detach, | ||
973 | |||
974 | .irq_mask = MASK_03 | MASK_06 | MASK_10, | ||
975 | .irq_func = budget_ci_irq, | ||
976 | }; | ||
977 | |||
978 | static int __init budget_ci_init(void) | ||
979 | { | ||
980 | return saa7146_register_extension(&budget_extension); | ||
981 | } | ||
982 | |||
983 | static void __exit budget_ci_exit(void) | ||
984 | { | ||
985 | saa7146_unregister_extension(&budget_extension); | ||
986 | } | ||
987 | |||
988 | module_init(budget_ci_init); | ||
989 | module_exit(budget_ci_exit); | ||
990 | |||
991 | MODULE_LICENSE("GPL"); | ||
992 | MODULE_AUTHOR("Michael Hunold, Jack Thomasson, Andrew de Quincey, others"); | ||
993 | MODULE_DESCRIPTION("driver for the SAA7146 based so-called " | ||
994 | "budget PCI DVB cards w/ CI-module produced by " | ||
995 | "Siemens, Technotrend, Hauppauge"); | ||