diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/media/video/cx23885/cimax2.c | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'drivers/media/video/cx23885/cimax2.c')
-rw-r--r-- | drivers/media/video/cx23885/cimax2.c | 536 |
1 files changed, 536 insertions, 0 deletions
diff --git a/drivers/media/video/cx23885/cimax2.c b/drivers/media/video/cx23885/cimax2.c new file mode 100644 index 00000000000..c9f15d6dec4 --- /dev/null +++ b/drivers/media/video/cx23885/cimax2.c | |||
@@ -0,0 +1,536 @@ | |||
1 | /* | ||
2 | * cimax2.c | ||
3 | * | ||
4 | * CIMax2(R) SP2 driver in conjunction with NetUp Dual DVB-S2 CI card | ||
5 | * | ||
6 | * Copyright (C) 2009 NetUP Inc. | ||
7 | * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru> | ||
8 | * Copyright (C) 2009 Abylay Ospan <aospan@netup.ru> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * | ||
19 | * GNU General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
24 | */ | ||
25 | |||
26 | #include "cx23885.h" | ||
27 | #include "dvb_ca_en50221.h" | ||
28 | /**** Bit definitions for MC417_RWD and MC417_OEN registers *** | ||
29 | bits 31-16 | ||
30 | +-----------+ | ||
31 | | Reserved | | ||
32 | +-----------+ | ||
33 | bit 15 bit 14 bit 13 bit 12 bit 11 bit 10 bit 9 bit 8 | ||
34 | +-------+-------+-------+-------+-------+-------+-------+-------+ | ||
35 | | WR# | RD# | | ACK# | ADHI | ADLO | CS1# | CS0# | | ||
36 | +-------+-------+-------+-------+-------+-------+-------+-------+ | ||
37 | bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 | ||
38 | +-------+-------+-------+-------+-------+-------+-------+-------+ | ||
39 | | DATA7| DATA6| DATA5| DATA4| DATA3| DATA2| DATA1| DATA0| | ||
40 | +-------+-------+-------+-------+-------+-------+-------+-------+ | ||
41 | ***/ | ||
42 | /* MC417 */ | ||
43 | #define NETUP_DATA 0x000000ff | ||
44 | #define NETUP_WR 0x00008000 | ||
45 | #define NETUP_RD 0x00004000 | ||
46 | #define NETUP_ACK 0x00001000 | ||
47 | #define NETUP_ADHI 0x00000800 | ||
48 | #define NETUP_ADLO 0x00000400 | ||
49 | #define NETUP_CS1 0x00000200 | ||
50 | #define NETUP_CS0 0x00000100 | ||
51 | #define NETUP_EN_ALL 0x00001000 | ||
52 | #define NETUP_CTRL_OFF (NETUP_CS1 | NETUP_CS0 | NETUP_WR | NETUP_RD) | ||
53 | #define NETUP_CI_CTL 0x04 | ||
54 | #define NETUP_CI_RD 1 | ||
55 | |||
56 | #define NETUP_IRQ_DETAM 0x1 | ||
57 | #define NETUP_IRQ_IRQAM 0x4 | ||
58 | |||
59 | static unsigned int ci_dbg; | ||
60 | module_param(ci_dbg, int, 0644); | ||
61 | MODULE_PARM_DESC(ci_dbg, "Enable CI debugging"); | ||
62 | |||
63 | static unsigned int ci_irq_enable; | ||
64 | module_param(ci_irq_enable, int, 0644); | ||
65 | MODULE_PARM_DESC(ci_irq_enable, "Enable IRQ from CAM"); | ||
66 | |||
67 | #define ci_dbg_print(args...) \ | ||
68 | do { \ | ||
69 | if (ci_dbg) \ | ||
70 | printk(KERN_DEBUG args); \ | ||
71 | } while (0) | ||
72 | |||
73 | #define ci_irq_flags() (ci_irq_enable ? NETUP_IRQ_IRQAM : 0) | ||
74 | |||
75 | /* stores all private variables for communication with CI */ | ||
76 | struct netup_ci_state { | ||
77 | struct dvb_ca_en50221 ca; | ||
78 | struct mutex ca_mutex; | ||
79 | struct i2c_adapter *i2c_adap; | ||
80 | u8 ci_i2c_addr; | ||
81 | int status; | ||
82 | struct work_struct work; | ||
83 | void *priv; | ||
84 | u8 current_irq_mode; | ||
85 | int current_ci_flag; | ||
86 | unsigned long next_status_checked_time; | ||
87 | }; | ||
88 | |||
89 | |||
90 | int netup_read_i2c(struct i2c_adapter *i2c_adap, u8 addr, u8 reg, | ||
91 | u8 *buf, int len) | ||
92 | { | ||
93 | int ret; | ||
94 | struct i2c_msg msg[] = { | ||
95 | { | ||
96 | .addr = addr, | ||
97 | .flags = 0, | ||
98 | .buf = ®, | ||
99 | .len = 1 | ||
100 | }, { | ||
101 | .addr = addr, | ||
102 | .flags = I2C_M_RD, | ||
103 | .buf = buf, | ||
104 | .len = len | ||
105 | } | ||
106 | }; | ||
107 | |||
108 | ret = i2c_transfer(i2c_adap, msg, 2); | ||
109 | |||
110 | if (ret != 2) { | ||
111 | ci_dbg_print("%s: i2c read error, Reg = 0x%02x, Status = %d\n", | ||
112 | __func__, reg, ret); | ||
113 | |||
114 | return -1; | ||
115 | } | ||
116 | |||
117 | ci_dbg_print("%s: i2c read Addr=0x%04x, Reg = 0x%02x, data = %02x\n", | ||
118 | __func__, addr, reg, buf[0]); | ||
119 | |||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | int netup_write_i2c(struct i2c_adapter *i2c_adap, u8 addr, u8 reg, | ||
124 | u8 *buf, int len) | ||
125 | { | ||
126 | int ret; | ||
127 | u8 buffer[len + 1]; | ||
128 | |||
129 | struct i2c_msg msg = { | ||
130 | .addr = addr, | ||
131 | .flags = 0, | ||
132 | .buf = &buffer[0], | ||
133 | .len = len + 1 | ||
134 | }; | ||
135 | |||
136 | buffer[0] = reg; | ||
137 | memcpy(&buffer[1], buf, len); | ||
138 | |||
139 | ret = i2c_transfer(i2c_adap, &msg, 1); | ||
140 | |||
141 | if (ret != 1) { | ||
142 | ci_dbg_print("%s: i2c write error, Reg=[0x%02x], Status=%d\n", | ||
143 | __func__, reg, ret); | ||
144 | return -1; | ||
145 | } | ||
146 | |||
147 | return 0; | ||
148 | } | ||
149 | |||
150 | int netup_ci_get_mem(struct cx23885_dev *dev) | ||
151 | { | ||
152 | int mem; | ||
153 | unsigned long timeout = jiffies + msecs_to_jiffies(1); | ||
154 | |||
155 | for (;;) { | ||
156 | mem = cx_read(MC417_RWD); | ||
157 | if ((mem & NETUP_ACK) == 0) | ||
158 | break; | ||
159 | if (time_after(jiffies, timeout)) | ||
160 | break; | ||
161 | udelay(1); | ||
162 | } | ||
163 | |||
164 | cx_set(MC417_RWD, NETUP_CTRL_OFF); | ||
165 | |||
166 | return mem & 0xff; | ||
167 | } | ||
168 | |||
169 | int netup_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot, | ||
170 | u8 flag, u8 read, int addr, u8 data) | ||
171 | { | ||
172 | struct netup_ci_state *state = en50221->data; | ||
173 | struct cx23885_tsport *port = state->priv; | ||
174 | struct cx23885_dev *dev = port->dev; | ||
175 | |||
176 | u8 store; | ||
177 | int mem; | ||
178 | int ret; | ||
179 | |||
180 | if (0 != slot) | ||
181 | return -EINVAL; | ||
182 | |||
183 | if (state->current_ci_flag != flag) { | ||
184 | ret = netup_read_i2c(state->i2c_adap, state->ci_i2c_addr, | ||
185 | 0, &store, 1); | ||
186 | if (ret != 0) | ||
187 | return ret; | ||
188 | |||
189 | store &= ~0x0c; | ||
190 | store |= flag; | ||
191 | |||
192 | ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr, | ||
193 | 0, &store, 1); | ||
194 | if (ret != 0) | ||
195 | return ret; | ||
196 | }; | ||
197 | state->current_ci_flag = flag; | ||
198 | |||
199 | mutex_lock(&dev->gpio_lock); | ||
200 | |||
201 | /* write addr */ | ||
202 | cx_write(MC417_OEN, NETUP_EN_ALL); | ||
203 | cx_write(MC417_RWD, NETUP_CTRL_OFF | | ||
204 | NETUP_ADLO | (0xff & addr)); | ||
205 | cx_clear(MC417_RWD, NETUP_ADLO); | ||
206 | cx_write(MC417_RWD, NETUP_CTRL_OFF | | ||
207 | NETUP_ADHI | (0xff & (addr >> 8))); | ||
208 | cx_clear(MC417_RWD, NETUP_ADHI); | ||
209 | |||
210 | if (read) { /* data in */ | ||
211 | cx_write(MC417_OEN, NETUP_EN_ALL | NETUP_DATA); | ||
212 | } else /* data out */ | ||
213 | cx_write(MC417_RWD, NETUP_CTRL_OFF | data); | ||
214 | |||
215 | /* choose chip */ | ||
216 | cx_clear(MC417_RWD, | ||
217 | (state->ci_i2c_addr == 0x40) ? NETUP_CS0 : NETUP_CS1); | ||
218 | /* read/write */ | ||
219 | cx_clear(MC417_RWD, (read) ? NETUP_RD : NETUP_WR); | ||
220 | mem = netup_ci_get_mem(dev); | ||
221 | |||
222 | mutex_unlock(&dev->gpio_lock); | ||
223 | |||
224 | if (!read) | ||
225 | if (mem < 0) | ||
226 | return -EREMOTEIO; | ||
227 | |||
228 | ci_dbg_print("%s: %s: chipaddr=[0x%x] addr=[0x%02x], %s=%x\n", __func__, | ||
229 | (read) ? "read" : "write", state->ci_i2c_addr, addr, | ||
230 | (flag == NETUP_CI_CTL) ? "ctl" : "mem", | ||
231 | (read) ? mem : data); | ||
232 | |||
233 | if (read) | ||
234 | return mem; | ||
235 | |||
236 | return 0; | ||
237 | } | ||
238 | |||
239 | int netup_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221, | ||
240 | int slot, int addr) | ||
241 | { | ||
242 | return netup_ci_op_cam(en50221, slot, 0, NETUP_CI_RD, addr, 0); | ||
243 | } | ||
244 | |||
245 | int netup_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221, | ||
246 | int slot, int addr, u8 data) | ||
247 | { | ||
248 | return netup_ci_op_cam(en50221, slot, 0, 0, addr, data); | ||
249 | } | ||
250 | |||
251 | int netup_ci_read_cam_ctl(struct dvb_ca_en50221 *en50221, int slot, u8 addr) | ||
252 | { | ||
253 | return netup_ci_op_cam(en50221, slot, NETUP_CI_CTL, | ||
254 | NETUP_CI_RD, addr, 0); | ||
255 | } | ||
256 | |||
257 | int netup_ci_write_cam_ctl(struct dvb_ca_en50221 *en50221, int slot, | ||
258 | u8 addr, u8 data) | ||
259 | { | ||
260 | return netup_ci_op_cam(en50221, slot, NETUP_CI_CTL, 0, addr, data); | ||
261 | } | ||
262 | |||
263 | int netup_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot) | ||
264 | { | ||
265 | struct netup_ci_state *state = en50221->data; | ||
266 | u8 buf = 0x80; | ||
267 | int ret; | ||
268 | |||
269 | if (0 != slot) | ||
270 | return -EINVAL; | ||
271 | |||
272 | udelay(500); | ||
273 | ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr, | ||
274 | 0, &buf, 1); | ||
275 | |||
276 | if (ret != 0) | ||
277 | return ret; | ||
278 | |||
279 | udelay(500); | ||
280 | |||
281 | buf = 0x00; | ||
282 | ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr, | ||
283 | 0, &buf, 1); | ||
284 | |||
285 | msleep(1000); | ||
286 | dvb_ca_en50221_camready_irq(&state->ca, 0); | ||
287 | |||
288 | return 0; | ||
289 | |||
290 | } | ||
291 | |||
292 | int netup_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot) | ||
293 | { | ||
294 | /* not implemented */ | ||
295 | return 0; | ||
296 | } | ||
297 | |||
298 | int netup_ci_set_irq(struct dvb_ca_en50221 *en50221, u8 irq_mode) | ||
299 | { | ||
300 | struct netup_ci_state *state = en50221->data; | ||
301 | int ret; | ||
302 | |||
303 | if (irq_mode == state->current_irq_mode) | ||
304 | return 0; | ||
305 | |||
306 | ci_dbg_print("%s: chipaddr=[0x%x] setting ci IRQ to [0x%x] \n", | ||
307 | __func__, state->ci_i2c_addr, irq_mode); | ||
308 | ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr, | ||
309 | 0x1b, &irq_mode, 1); | ||
310 | |||
311 | if (ret != 0) | ||
312 | return ret; | ||
313 | |||
314 | state->current_irq_mode = irq_mode; | ||
315 | |||
316 | return 0; | ||
317 | } | ||
318 | |||
319 | int netup_ci_slot_ts_ctl(struct dvb_ca_en50221 *en50221, int slot) | ||
320 | { | ||
321 | struct netup_ci_state *state = en50221->data; | ||
322 | u8 buf; | ||
323 | |||
324 | if (0 != slot) | ||
325 | return -EINVAL; | ||
326 | |||
327 | netup_read_i2c(state->i2c_adap, state->ci_i2c_addr, | ||
328 | 0, &buf, 1); | ||
329 | buf |= 0x60; | ||
330 | |||
331 | return netup_write_i2c(state->i2c_adap, state->ci_i2c_addr, | ||
332 | 0, &buf, 1); | ||
333 | } | ||
334 | |||
335 | /* work handler */ | ||
336 | static void netup_read_ci_status(struct work_struct *work) | ||
337 | { | ||
338 | struct netup_ci_state *state = | ||
339 | container_of(work, struct netup_ci_state, work); | ||
340 | u8 buf[33]; | ||
341 | int ret; | ||
342 | |||
343 | /* CAM module IRQ processing. fast operation */ | ||
344 | dvb_ca_en50221_frda_irq(&state->ca, 0); | ||
345 | |||
346 | /* CAM module INSERT/REMOVE processing. slow operation because of i2c | ||
347 | * transfers */ | ||
348 | if (time_after(jiffies, state->next_status_checked_time) | ||
349 | || !state->status) { | ||
350 | ret = netup_read_i2c(state->i2c_adap, state->ci_i2c_addr, | ||
351 | 0, &buf[0], 33); | ||
352 | |||
353 | state->next_status_checked_time = jiffies | ||
354 | + msecs_to_jiffies(1000); | ||
355 | |||
356 | if (ret != 0) | ||
357 | return; | ||
358 | |||
359 | ci_dbg_print("%s: Slot Status Addr=[0x%04x], " | ||
360 | "Reg=[0x%02x], data=%02x, " | ||
361 | "TS config = %02x\n", __func__, | ||
362 | state->ci_i2c_addr, 0, buf[0], | ||
363 | buf[0]); | ||
364 | |||
365 | |||
366 | if (buf[0] & 1) | ||
367 | state->status = DVB_CA_EN50221_POLL_CAM_PRESENT | | ||
368 | DVB_CA_EN50221_POLL_CAM_READY; | ||
369 | else | ||
370 | state->status = 0; | ||
371 | } | ||
372 | } | ||
373 | |||
374 | /* CI irq handler */ | ||
375 | int netup_ci_slot_status(struct cx23885_dev *dev, u32 pci_status) | ||
376 | { | ||
377 | struct cx23885_tsport *port = NULL; | ||
378 | struct netup_ci_state *state = NULL; | ||
379 | |||
380 | ci_dbg_print("%s:\n", __func__); | ||
381 | |||
382 | if (0 == (pci_status & (PCI_MSK_GPIO0 | PCI_MSK_GPIO1))) | ||
383 | return 0; | ||
384 | |||
385 | if (pci_status & PCI_MSK_GPIO0) { | ||
386 | port = &dev->ts1; | ||
387 | state = port->port_priv; | ||
388 | schedule_work(&state->work); | ||
389 | ci_dbg_print("%s: Wakeup CI0\n", __func__); | ||
390 | } | ||
391 | |||
392 | if (pci_status & PCI_MSK_GPIO1) { | ||
393 | port = &dev->ts2; | ||
394 | state = port->port_priv; | ||
395 | schedule_work(&state->work); | ||
396 | ci_dbg_print("%s: Wakeup CI1\n", __func__); | ||
397 | } | ||
398 | |||
399 | return 1; | ||
400 | } | ||
401 | |||
402 | int netup_poll_ci_slot_status(struct dvb_ca_en50221 *en50221, int slot, int open) | ||
403 | { | ||
404 | struct netup_ci_state *state = en50221->data; | ||
405 | |||
406 | if (0 != slot) | ||
407 | return -EINVAL; | ||
408 | |||
409 | netup_ci_set_irq(en50221, open ? (NETUP_IRQ_DETAM | ci_irq_flags()) | ||
410 | : NETUP_IRQ_DETAM); | ||
411 | |||
412 | return state->status; | ||
413 | } | ||
414 | |||
415 | int netup_ci_init(struct cx23885_tsport *port) | ||
416 | { | ||
417 | struct netup_ci_state *state; | ||
418 | u8 cimax_init[34] = { | ||
419 | 0x00, /* module A control*/ | ||
420 | 0x00, /* auto select mask high A */ | ||
421 | 0x00, /* auto select mask low A */ | ||
422 | 0x00, /* auto select pattern high A */ | ||
423 | 0x00, /* auto select pattern low A */ | ||
424 | 0x44, /* memory access time A */ | ||
425 | 0x00, /* invert input A */ | ||
426 | 0x00, /* RFU */ | ||
427 | 0x00, /* RFU */ | ||
428 | 0x00, /* module B control*/ | ||
429 | 0x00, /* auto select mask high B */ | ||
430 | 0x00, /* auto select mask low B */ | ||
431 | 0x00, /* auto select pattern high B */ | ||
432 | 0x00, /* auto select pattern low B */ | ||
433 | 0x44, /* memory access time B */ | ||
434 | 0x00, /* invert input B */ | ||
435 | 0x00, /* RFU */ | ||
436 | 0x00, /* RFU */ | ||
437 | 0x00, /* auto select mask high Ext */ | ||
438 | 0x00, /* auto select mask low Ext */ | ||
439 | 0x00, /* auto select pattern high Ext */ | ||
440 | 0x00, /* auto select pattern low Ext */ | ||
441 | 0x00, /* RFU */ | ||
442 | 0x02, /* destination - module A */ | ||
443 | 0x01, /* power on (use it like store place) */ | ||
444 | 0x00, /* RFU */ | ||
445 | 0x00, /* int status read only */ | ||
446 | ci_irq_flags() | NETUP_IRQ_DETAM, /* DETAM, IRQAM unmasked */ | ||
447 | 0x05, /* EXTINT=active-high, INT=push-pull */ | ||
448 | 0x00, /* USCG1 */ | ||
449 | 0x04, /* ack active low */ | ||
450 | 0x00, /* LOCK = 0 */ | ||
451 | 0x33, /* serial mode, rising in, rising out, MSB first*/ | ||
452 | 0x31, /* synchronization */ | ||
453 | }; | ||
454 | int ret; | ||
455 | |||
456 | ci_dbg_print("%s\n", __func__); | ||
457 | state = kzalloc(sizeof(struct netup_ci_state), GFP_KERNEL); | ||
458 | if (!state) { | ||
459 | ci_dbg_print("%s: Unable create CI structure!\n", __func__); | ||
460 | ret = -ENOMEM; | ||
461 | goto err; | ||
462 | } | ||
463 | |||
464 | port->port_priv = state; | ||
465 | |||
466 | switch (port->nr) { | ||
467 | case 1: | ||
468 | state->ci_i2c_addr = 0x40; | ||
469 | break; | ||
470 | case 2: | ||
471 | state->ci_i2c_addr = 0x41; | ||
472 | break; | ||
473 | } | ||
474 | |||
475 | state->i2c_adap = &port->dev->i2c_bus[0].i2c_adap; | ||
476 | state->ca.owner = THIS_MODULE; | ||
477 | state->ca.read_attribute_mem = netup_ci_read_attribute_mem; | ||
478 | state->ca.write_attribute_mem = netup_ci_write_attribute_mem; | ||
479 | state->ca.read_cam_control = netup_ci_read_cam_ctl; | ||
480 | state->ca.write_cam_control = netup_ci_write_cam_ctl; | ||
481 | state->ca.slot_reset = netup_ci_slot_reset; | ||
482 | state->ca.slot_shutdown = netup_ci_slot_shutdown; | ||
483 | state->ca.slot_ts_enable = netup_ci_slot_ts_ctl; | ||
484 | state->ca.poll_slot_status = netup_poll_ci_slot_status; | ||
485 | state->ca.data = state; | ||
486 | state->priv = port; | ||
487 | state->current_irq_mode = ci_irq_flags() | NETUP_IRQ_DETAM; | ||
488 | |||
489 | ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr, | ||
490 | 0, &cimax_init[0], 34); | ||
491 | /* lock registers */ | ||
492 | ret |= netup_write_i2c(state->i2c_adap, state->ci_i2c_addr, | ||
493 | 0x1f, &cimax_init[0x18], 1); | ||
494 | /* power on slots */ | ||
495 | ret |= netup_write_i2c(state->i2c_adap, state->ci_i2c_addr, | ||
496 | 0x18, &cimax_init[0x18], 1); | ||
497 | |||
498 | if (0 != ret) | ||
499 | goto err; | ||
500 | |||
501 | ret = dvb_ca_en50221_init(&port->frontends.adapter, | ||
502 | &state->ca, | ||
503 | /* flags */ 0, | ||
504 | /* n_slots */ 1); | ||
505 | if (0 != ret) | ||
506 | goto err; | ||
507 | |||
508 | INIT_WORK(&state->work, netup_read_ci_status); | ||
509 | schedule_work(&state->work); | ||
510 | |||
511 | ci_dbg_print("%s: CI initialized!\n", __func__); | ||
512 | |||
513 | return 0; | ||
514 | err: | ||
515 | ci_dbg_print("%s: Cannot initialize CI: Error %d.\n", __func__, ret); | ||
516 | kfree(state); | ||
517 | return ret; | ||
518 | } | ||
519 | |||
520 | void netup_ci_exit(struct cx23885_tsport *port) | ||
521 | { | ||
522 | struct netup_ci_state *state; | ||
523 | |||
524 | if (NULL == port) | ||
525 | return; | ||
526 | |||
527 | state = (struct netup_ci_state *)port->port_priv; | ||
528 | if (NULL == state) | ||
529 | return; | ||
530 | |||
531 | if (NULL == state->ca.data) | ||
532 | return; | ||
533 | |||
534 | dvb_ca_en50221_release(&state->ca); | ||
535 | kfree(state); | ||
536 | } | ||