blob: 3ff67523cce2dbd505310c011cfd993f789cbaf6 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * budget-av.c: driver for the SAA7146 based Budget DVB cards
3 * with analog video in
4 *
5 * Compiled from various sources by Michael Hunold <michael@mihu.de>
6 *
7 * CI interface support (c) 2004 Olivier Gournet <ogournet@anevia.com> &
8 * Andrew de Quincey <adq_dvb@lidskialf.net>
9 *
10 * Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de>
11 *
12 * Copyright (C) 1999-2002 Ralph Metzler
13 * & Marcus Metzler for convergence integrated media GmbH
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
19 *
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
30 * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
31 *
32 *
33 * the project's page is at http://www.linuxtv.org/dvb/
34 */
35
36#include "budget.h"
37#include "stv0299.h"
38#include "tda10021.h"
39#include "tda1004x.h"
Regis Prevotf8bf1342006-01-11 23:31:53 -020040#include "dvb-pll.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070041#include <media/saa7146_vv.h>
42#include <linux/module.h>
43#include <linux/errno.h>
44#include <linux/slab.h>
45#include <linux/interrupt.h>
46#include <linux/input.h>
47#include <linux/spinlock.h>
48
49#include "dvb_ca_en50221.h"
50
51#define DEBICICAM 0x02420000
52
Andrew de Quincey5c1208b2006-05-22 10:32:02 -030053#define SLOTSTATUS_NONE 1
54#define SLOTSTATUS_PRESENT 2
55#define SLOTSTATUS_RESET 4
56#define SLOTSTATUS_READY 8
57#define SLOTSTATUS_OCCUPIED (SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY)
58
Linus Torvalds1da177e2005-04-16 15:20:36 -070059struct budget_av {
60 struct budget budget;
61 struct video_device *vd;
62 int cur_input;
63 int has_saa7113;
64 struct tasklet_struct ciintf_irq_tasklet;
65 int slot_status;
66 struct dvb_ca_en50221 ca;
Andrew de Quincey5c1208b2006-05-22 10:32:02 -030067 u8 reinitialise_demod:1;
68 u8 tda10021_poclkp:1;
69 u8 tda10021_ts_enabled;
70 int (*tda10021_set_frontend)(struct dvb_frontend *fe, struct dvb_frontend_parameters *p);
Linus Torvalds1da177e2005-04-16 15:20:36 -070071};
72
Andrew de Quincey5c1208b2006-05-22 10:32:02 -030073static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot);
74
75
Andrew de Quincey86f40cc2006-03-30 15:53:35 -030076/* GPIO Connections:
77 * 0 - Vcc/Reset (Reset is controlled by capacitor). Resets the frontend *AS WELL*!
78 * 1 - CI memory select 0=>IO memory, 1=>Attribute Memory
79 * 2 - CI Card Enable (Active Low)
80 * 3 - CI Card Detect
Johannes Stezenbachb82a96a2005-05-16 21:54:49 -070081 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070082
83/****************************************************************************
84 * INITIALIZATION
85 ****************************************************************************/
86
87static u8 i2c_readreg(struct i2c_adapter *i2c, u8 id, u8 reg)
88{
89 u8 mm1[] = { 0x00 };
90 u8 mm2[] = { 0x00 };
91 struct i2c_msg msgs[2];
92
93 msgs[0].flags = 0;
94 msgs[1].flags = I2C_M_RD;
95 msgs[0].addr = msgs[1].addr = id / 2;
96 mm1[0] = reg;
97 msgs[0].len = 1;
98 msgs[1].len = 1;
99 msgs[0].buf = mm1;
100 msgs[1].buf = mm2;
101
102 i2c_transfer(i2c, msgs, 2);
103
104 return mm2[0];
105}
106
107static int i2c_readregs(struct i2c_adapter *i2c, u8 id, u8 reg, u8 * buf, u8 len)
108{
109 u8 mm1[] = { reg };
110 struct i2c_msg msgs[2] = {
111 {.addr = id / 2,.flags = 0,.buf = mm1,.len = 1},
112 {.addr = id / 2,.flags = I2C_M_RD,.buf = buf,.len = len}
113 };
114
115 if (i2c_transfer(i2c, msgs, 2) != 2)
116 return -EIO;
117
118 return 0;
119}
120
121static int i2c_writereg(struct i2c_adapter *i2c, u8 id, u8 reg, u8 val)
122{
123 u8 msg[2] = { reg, val };
124 struct i2c_msg msgs;
125
126 msgs.flags = 0;
127 msgs.addr = id / 2;
128 msgs.len = 2;
129 msgs.buf = msg;
130 return i2c_transfer(i2c, &msgs, 1);
131}
132
133static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address)
134{
135 struct budget_av *budget_av = (struct budget_av *) ca->data;
136 int result;
137
138 if (slot != 0)
139 return -EINVAL;
140
141 saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTHI);
142 udelay(1);
143
Andrew de Quincey2d0235d2006-01-09 15:25:05 -0200144 result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, address & 0xfff, 1, 0, 1);
Andrew de Quincey5c1208b2006-05-22 10:32:02 -0300145 if (result == -ETIMEDOUT) {
146 ciintf_slot_shutdown(ca, slot);
147 printk(KERN_INFO "budget-av: cam ejected 1\n");
148 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149 return result;
150}
151
152static int ciintf_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address, u8 value)
153{
154 struct budget_av *budget_av = (struct budget_av *) ca->data;
155 int result;
156
157 if (slot != 0)
158 return -EINVAL;
159
160 saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTHI);
161 udelay(1);
162
Andrew de Quincey2d0235d2006-01-09 15:25:05 -0200163 result = ttpci_budget_debiwrite(&budget_av->budget, DEBICICAM, address & 0xfff, 1, value, 0, 1);
Andrew de Quincey5c1208b2006-05-22 10:32:02 -0300164 if (result == -ETIMEDOUT) {
165 ciintf_slot_shutdown(ca, slot);
166 printk(KERN_INFO "budget-av: cam ejected 2\n");
167 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168 return result;
169}
170
171static int ciintf_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address)
172{
173 struct budget_av *budget_av = (struct budget_av *) ca->data;
174 int result;
175
176 if (slot != 0)
177 return -EINVAL;
178
179 saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO);
180 udelay(1);
181
182 result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, address & 3, 1, 0, 0);
Andrew de Quincey5c1208b2006-05-22 10:32:02 -0300183 if ((result == -ETIMEDOUT) || ((result == 0xff) && ((address & 3) < 2))) {
184 ciintf_slot_shutdown(ca, slot);
185 printk(KERN_INFO "budget-av: cam ejected 3\n");
186 return -ETIMEDOUT;
187 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188 return result;
189}
190
191static int ciintf_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address, u8 value)
192{
193 struct budget_av *budget_av = (struct budget_av *) ca->data;
194 int result;
195
196 if (slot != 0)
197 return -EINVAL;
198
199 saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO);
200 udelay(1);
201
202 result = ttpci_budget_debiwrite(&budget_av->budget, DEBICICAM, address & 3, 1, value, 0, 0);
Andrew de Quincey5c1208b2006-05-22 10:32:02 -0300203 if (result == -ETIMEDOUT) {
204 ciintf_slot_shutdown(ca, slot);
205 printk(KERN_INFO "budget-av: cam ejected 5\n");
206 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207 return result;
208}
209
210static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot)
211{
212 struct budget_av *budget_av = (struct budget_av *) ca->data;
213 struct saa7146_dev *saa = budget_av->budget.dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214
215 if (slot != 0)
216 return -EINVAL;
217
218 dprintk(1, "ciintf_slot_reset\n");
Andrew de Quincey5c1208b2006-05-22 10:32:02 -0300219 budget_av->slot_status = SLOTSTATUS_RESET;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220
Johannes Stezenbachb82a96a2005-05-16 21:54:49 -0700221 saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTHI); /* disable card */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222
Johannes Stezenbachb82a96a2005-05-16 21:54:49 -0700223 saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTHI); /* Vcc off */
224 msleep(2);
225 saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO); /* Vcc on */
226 msleep(20); /* 20 ms Vcc settling time */
227
228 saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTLO); /* enable card */
Andrew de Quincey5c1208b2006-05-22 10:32:02 -0300229 ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB);
230 msleep(20);
Johannes Stezenbachb82a96a2005-05-16 21:54:49 -0700231
Andrew de Quincey5c1208b2006-05-22 10:32:02 -0300232 /* reinitialise the frontend if necessary */
233 if (budget_av->reinitialise_demod)
234 dvb_frontend_reinitialise(budget_av->budget.dvb_frontend);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235
Andrew de Quincey5c1208b2006-05-22 10:32:02 -0300236 /* set tda10021 back to original clock configuration on reset */
237 if (budget_av->tda10021_poclkp) {
238 tda10021_write_byte(budget_av->budget.dvb_frontend, 0x12, 0xa0);
239 budget_av->tda10021_ts_enabled = 0;
Johannes Stezenbachb82a96a2005-05-16 21:54:49 -0700240 }
241
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242 return 0;
243}
244
245static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
246{
247 struct budget_av *budget_av = (struct budget_av *) ca->data;
248 struct saa7146_dev *saa = budget_av->budget.dev;
249
250 if (slot != 0)
251 return -EINVAL;
252
253 dprintk(1, "ciintf_slot_shutdown\n");
254
255 ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB);
Andrew de Quincey5c1208b2006-05-22 10:32:02 -0300256 budget_av->slot_status = SLOTSTATUS_NONE;
257
258 /* set tda10021 back to original clock configuration when cam removed */
259 if (budget_av->tda10021_poclkp) {
260 tda10021_write_byte(budget_av->budget.dvb_frontend, 0x12, 0xa0);
261 budget_av->tda10021_ts_enabled = 0;
262 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263 return 0;
264}
265
266static int ciintf_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
267{
268 struct budget_av *budget_av = (struct budget_av *) ca->data;
269 struct saa7146_dev *saa = budget_av->budget.dev;
270
271 if (slot != 0)
272 return -EINVAL;
273
274 dprintk(1, "ciintf_slot_ts_enable: %d\n", budget_av->slot_status);
275
276 ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTA);
Andrew de Quincey5c1208b2006-05-22 10:32:02 -0300277
278 /* tda10021 seems to need a different TS clock config when data is routed to the CAM */
279 if (budget_av->tda10021_poclkp) {
280 tda10021_write_byte(budget_av->budget.dvb_frontend, 0x12, 0xa1);
281 budget_av->tda10021_ts_enabled = 1;
282 }
283
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 return 0;
285}
286
287static int ciintf_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open)
288{
289 struct budget_av *budget_av = (struct budget_av *) ca->data;
290 struct saa7146_dev *saa = budget_av->budget.dev;
Andrew de Quincey5c1208b2006-05-22 10:32:02 -0300291 int result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292
293 if (slot != 0)
294 return -EINVAL;
295
Andrew de Quincey5c1208b2006-05-22 10:32:02 -0300296 /* test the card detect line - needs to be done carefully
297 * since it never goes high for some CAMs on this interface (e.g. topuptv) */
298 if (budget_av->slot_status == SLOTSTATUS_NONE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299 saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT);
300 udelay(1);
Andrew de Quincey5c1208b2006-05-22 10:32:02 -0300301 if (saa7146_read(saa, PSR) & MASK_06) {
302 if (budget_av->slot_status == SLOTSTATUS_NONE) {
303 budget_av->slot_status = SLOTSTATUS_PRESENT;
304 printk(KERN_INFO "budget-av: cam inserted A\n");
Andrew de Quincey2d0235d2006-01-09 15:25:05 -0200305 }
306 }
Andrew de Quincey5c1208b2006-05-22 10:32:02 -0300307 saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO);
308 }
Andrew de Quincey2d0235d2006-01-09 15:25:05 -0200309
Andrew de Quincey5c1208b2006-05-22 10:32:02 -0300310 /* We also try and read from IO memory to work round the above detection bug. If
311 * there is no CAM, we will get a timeout. Only done if there is no cam
312 * present, since this test actually breaks some cams :(
313 *
314 * if the CI interface is not open, we also do the above test since we
315 * don't care if the cam has problems - we'll be resetting it on open() anyway */
316 if ((budget_av->slot_status == SLOTSTATUS_NONE) || (!open)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317 saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO);
Andrew de Quincey5c1208b2006-05-22 10:32:02 -0300318 result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, 0, 1, 0, 1);
319 if ((result >= 0) && (budget_av->slot_status == SLOTSTATUS_NONE)) {
320 budget_av->slot_status = SLOTSTATUS_PRESENT;
321 printk(KERN_INFO "budget-av: cam inserted B\n");
322 } else if (result < 0) {
323 if (budget_av->slot_status != SLOTSTATUS_NONE) {
324 ciintf_slot_shutdown(ca, slot);
325 printk(KERN_INFO "budget-av: cam ejected 5\n");
326 return 0;
327 }
Johannes Stezenbachb82a96a2005-05-16 21:54:49 -0700328 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329 }
330
Andrew de Quincey5c1208b2006-05-22 10:32:02 -0300331 /* read from attribute memory in reset/ready state to know when the CAM is ready */
332 if (budget_av->slot_status == SLOTSTATUS_RESET) {
333 result = ciintf_read_attribute_mem(ca, slot, 0);
334 if (result == 0x1d) {
335 budget_av->slot_status = SLOTSTATUS_READY;
336 }
337 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338
Andrew de Quincey5c1208b2006-05-22 10:32:02 -0300339 /* work out correct return code */
340 if (budget_av->slot_status != SLOTSTATUS_NONE) {
341 if (budget_av->slot_status & SLOTSTATUS_READY) {
342 return DVB_CA_EN50221_POLL_CAM_PRESENT | DVB_CA_EN50221_POLL_CAM_READY;
343 }
344 return DVB_CA_EN50221_POLL_CAM_PRESENT;
345 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346 return 0;
347}
348
349static int ciintf_init(struct budget_av *budget_av)
350{
351 struct saa7146_dev *saa = budget_av->budget.dev;
352 int result;
353
354 memset(&budget_av->ca, 0, sizeof(struct dvb_ca_en50221));
355
Johannes Stezenbachb82a96a2005-05-16 21:54:49 -0700356 saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO);
357 saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTLO);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358 saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTLO);
359 saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO);
360
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361 /* Enable DEBI pins */
362 saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16) | 0x800);
363
364 /* register CI interface */
365 budget_av->ca.owner = THIS_MODULE;
366 budget_av->ca.read_attribute_mem = ciintf_read_attribute_mem;
367 budget_av->ca.write_attribute_mem = ciintf_write_attribute_mem;
368 budget_av->ca.read_cam_control = ciintf_read_cam_control;
369 budget_av->ca.write_cam_control = ciintf_write_cam_control;
370 budget_av->ca.slot_reset = ciintf_slot_reset;
371 budget_av->ca.slot_shutdown = ciintf_slot_shutdown;
372 budget_av->ca.slot_ts_enable = ciintf_slot_ts_enable;
373 budget_av->ca.poll_slot_status = ciintf_poll_slot_status;
374 budget_av->ca.data = budget_av;
Andrew de Quincey5c1208b2006-05-22 10:32:02 -0300375 budget_av->budget.ci_present = 1;
376 budget_av->slot_status = SLOTSTATUS_NONE;
Johannes Stezenbachb82a96a2005-05-16 21:54:49 -0700377
Johannes Stezenbachfdc53a62005-05-16 21:54:39 -0700378 if ((result = dvb_ca_en50221_init(&budget_av->budget.dvb_adapter,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379 &budget_av->ca, 0, 1)) != 0) {
Johannes Stezenbachb82a96a2005-05-16 21:54:49 -0700380 printk(KERN_ERR "budget-av: ci initialisation failed.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381 goto error;
382 }
Johannes Stezenbachb82a96a2005-05-16 21:54:49 -0700383
384 printk(KERN_INFO "budget-av: ci interface initialised.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 return 0;
386
387error:
388 saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16));
389 return result;
390}
391
392static void ciintf_deinit(struct budget_av *budget_av)
393{
394 struct saa7146_dev *saa = budget_av->budget.dev;
395
396 saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT);
397 saa7146_setgpio(saa, 1, SAA7146_GPIO_INPUT);
398 saa7146_setgpio(saa, 2, SAA7146_GPIO_INPUT);
399 saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT);
400
401 /* release the CA device */
402 dvb_ca_en50221_release(&budget_av->ca);
403
404 /* disable DEBI pins */
405 saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16));
406}
407
408
409static const u8 saa7113_tab[] = {
410 0x01, 0x08,
411 0x02, 0xc0,
412 0x03, 0x33,
413 0x04, 0x00,
414 0x05, 0x00,
415 0x06, 0xeb,
416 0x07, 0xe0,
417 0x08, 0x28,
418 0x09, 0x00,
419 0x0a, 0x80,
420 0x0b, 0x47,
421 0x0c, 0x40,
422 0x0d, 0x00,
423 0x0e, 0x01,
424 0x0f, 0x44,
425
426 0x10, 0x08,
427 0x11, 0x0c,
428 0x12, 0x7b,
429 0x13, 0x00,
430 0x15, 0x00, 0x16, 0x00, 0x17, 0x00,
431
432 0x57, 0xff,
433 0x40, 0x82, 0x58, 0x00, 0x59, 0x54, 0x5a, 0x07,
434 0x5b, 0x83, 0x5e, 0x00,
435 0xff
436};
437
438static int saa7113_init(struct budget_av *budget_av)
439{
440 struct budget *budget = &budget_av->budget;
Johannes Stezenbachb82a96a2005-05-16 21:54:49 -0700441 struct saa7146_dev *saa = budget->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442 const u8 *data = saa7113_tab;
443
Johannes Stezenbachb82a96a2005-05-16 21:54:49 -0700444 saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTHI);
445 msleep(200);
446
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447 if (i2c_writereg(&budget->i2c_adap, 0x4a, 0x01, 0x08) != 1) {
448 dprintk(1, "saa7113 not found on KNC card\n");
449 return -ENODEV;
450 }
451
452 dprintk(1, "saa7113 detected and initializing\n");
453
454 while (*data != 0xff) {
455 i2c_writereg(&budget->i2c_adap, 0x4a, *data, *(data + 1));
456 data += 2;
457 }
458
459 dprintk(1, "saa7113 status=%02x\n", i2c_readreg(&budget->i2c_adap, 0x4a, 0x1f));
460
461 return 0;
462}
463
464static int saa7113_setinput(struct budget_av *budget_av, int input)
465{
466 struct budget *budget = &budget_av->budget;
467
468 if (1 != budget_av->has_saa7113)
469 return -ENODEV;
470
471 if (input == 1) {
472 i2c_writereg(&budget->i2c_adap, 0x4a, 0x02, 0xc7);
473 i2c_writereg(&budget->i2c_adap, 0x4a, 0x09, 0x80);
474 } else if (input == 0) {
475 i2c_writereg(&budget->i2c_adap, 0x4a, 0x02, 0xc0);
476 i2c_writereg(&budget->i2c_adap, 0x4a, 0x09, 0x00);
477 } else
478 return -EINVAL;
479
480 budget_av->cur_input = input;
481 return 0;
482}
483
484
485static int philips_su1278_ty_ci_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ratio)
486{
487 u8 aclk = 0;
488 u8 bclk = 0;
489 u8 m1;
490
491 aclk = 0xb5;
492 if (srate < 2000000)
493 bclk = 0x86;
494 else if (srate < 5000000)
495 bclk = 0x89;
496 else if (srate < 15000000)
497 bclk = 0x8f;
498 else if (srate < 45000000)
499 bclk = 0x95;
500
501 m1 = 0x14;
502 if (srate < 4000000)
503 m1 = 0x10;
504
505 stv0299_writereg(fe, 0x13, aclk);
506 stv0299_writereg(fe, 0x14, bclk);
507 stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff);
508 stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff);
509 stv0299_writereg(fe, 0x21, (ratio) & 0xf0);
510 stv0299_writereg(fe, 0x0f, 0x80 | m1);
511
512 return 0;
513}
514
Andrew de Quinceye87d41c2006-04-18 17:47:11 -0300515static int philips_su1278_ty_ci_tuner_set_params(struct dvb_frontend *fe,
516 struct dvb_frontend_parameters *params)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 u32 div;
519 u8 buf[4];
Andrew de Quinceye87d41c2006-04-18 17:47:11 -0300520 struct budget *budget = (struct budget *) fe->dvb->priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 struct i2c_msg msg = {.addr = 0x61,.flags = 0,.buf = buf,.len = sizeof(buf) };
522
523 if ((params->frequency < 950000) || (params->frequency > 2150000))
524 return -EINVAL;
525
526 div = (params->frequency + (125 - 1)) / 125; // round correctly
527 buf[0] = (div >> 8) & 0x7f;
528 buf[1] = div & 0xff;
529 buf[2] = 0x80 | ((div & 0x18000) >> 10) | 4;
530 buf[3] = 0x20;
531
532 if (params->u.qpsk.symbol_rate < 4000000)
533 buf[3] |= 1;
534
535 if (params->frequency < 1250000)
536 buf[3] |= 0;
537 else if (params->frequency < 1550000)
538 buf[3] |= 0x40;
539 else if (params->frequency < 2050000)
540 buf[3] |= 0x80;
541 else if (params->frequency < 2150000)
542 buf[3] |= 0xC0;
543
Andrew de Quinceye87d41c2006-04-18 17:47:11 -0300544 if (fe->ops->i2c_gate_ctrl)
545 fe->ops->i2c_gate_ctrl(fe, 1);
546 if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 return -EIO;
548 return 0;
549}
550
Andrew de Quinceyeffa7912006-01-09 15:25:09 -0200551#define MIN2(a,b) ((a) < (b) ? (a) : (b))
552#define MIN3(a,b,c) MIN2(MIN2(a,b),c)
553
Andrew de Quinceye87d41c2006-04-18 17:47:11 -0300554static int philips_su1278sh2_tua6100_tuner_set_params(struct dvb_frontend *fe,
555 struct dvb_frontend_parameters *params)
Andrew de Quinceyeffa7912006-01-09 15:25:09 -0200556{
557 u8 reg0 [2] = { 0x00, 0x00 };
558 u8 reg1 [4] = { 0x01, 0x00, 0x00, 0x00 };
559 u8 reg2 [3] = { 0x02, 0x00, 0x00 };
560 int _fband;
561 int first_ZF;
562 int R, A, N, P, M;
563 struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = NULL,.len = 0 };
564 int freq = params->frequency;
Andrew de Quinceye87d41c2006-04-18 17:47:11 -0300565 struct budget *budget = (struct budget *) fe->dvb->priv;
Andrew de Quinceyeffa7912006-01-09 15:25:09 -0200566
567 first_ZF = (freq) / 1000;
568
569 if (abs(MIN2(abs(first_ZF-1190),abs(first_ZF-1790))) <
570 abs(MIN3(abs(first_ZF-1202),abs(first_ZF-1542),abs(first_ZF-1890))))
571 _fband = 2;
572 else
573 _fband = 3;
574
575 if (_fband == 2) {
576 if (((first_ZF >= 950) && (first_ZF < 1350)) ||
577 ((first_ZF >= 1430) && (first_ZF < 1950)))
578 reg0[1] = 0x07;
579 else if (((first_ZF >= 1350) && (first_ZF < 1430)) ||
580 ((first_ZF >= 1950) && (first_ZF < 2150)))
581 reg0[1] = 0x0B;
582 }
583
584 if(_fband == 3) {
585 if (((first_ZF >= 950) && (first_ZF < 1350)) ||
586 ((first_ZF >= 1455) && (first_ZF < 1950)))
587 reg0[1] = 0x07;
588 else if (((first_ZF >= 1350) && (first_ZF < 1420)) ||
589 ((first_ZF >= 1950) && (first_ZF < 2150)))
590 reg0[1] = 0x0B;
591 else if ((first_ZF >= 1420) && (first_ZF < 1455))
592 reg0[1] = 0x0F;
593 }
594
595 if (first_ZF > 1525)
596 reg1[1] |= 0x80;
597 else
598 reg1[1] &= 0x7F;
599
600 if (_fband == 2) {
601 if (first_ZF > 1430) { /* 1430MHZ */
602 reg1[1] &= 0xCF; /* N2 */
603 reg2[1] &= 0xCF; /* R2 */
604 reg2[1] |= 0x10;
605 } else {
606 reg1[1] &= 0xCF; /* N2 */
607 reg1[1] |= 0x20;
608 reg2[1] &= 0xCF; /* R2 */
609 reg2[1] |= 0x10;
610 }
611 }
612
613 if (_fband == 3) {
614 if ((first_ZF >= 1455) &&
615 (first_ZF < 1630)) {
616 reg1[1] &= 0xCF; /* N2 */
617 reg1[1] |= 0x20;
618 reg2[1] &= 0xCF; /* R2 */
619 } else {
620 if (first_ZF < 1455) {
621 reg1[1] &= 0xCF; /* N2 */
622 reg1[1] |= 0x20;
623 reg2[1] &= 0xCF; /* R2 */
624 reg2[1] |= 0x10;
625 } else {
626 if (first_ZF >= 1630) {
627 reg1[1] &= 0xCF; /* N2 */
628 reg2[1] &= 0xCF; /* R2 */
629 reg2[1] |= 0x10;
630 }
631 }
632 }
633 }
634
635 /* set ports, enable P0 for symbol rates > 4Ms/s */
636 if (params->u.qpsk.symbol_rate >= 4000000)
637 reg1[1] |= 0x0c;
638 else
639 reg1[1] |= 0x04;
640
641 reg2[1] |= 0x0c;
642
643 R = 64;
644 A = 64;
645 P = 64; //32
646
647 M = (freq * R) / 4; /* in Mhz */
648 N = (M - A * 1000) / (P * 1000);
649
650 reg1[1] |= (N >> 9) & 0x03;
651 reg1[2] = (N >> 1) & 0xff;
652 reg1[3] = (N << 7) & 0x80;
653
654 reg2[1] |= (R >> 8) & 0x03;
655 reg2[2] = R & 0xFF; /* R */
656
657 reg1[3] |= A & 0x7f; /* A */
658
659 if (P == 64)
660 reg1[1] |= 0x40; /* Prescaler 64/65 */
661
662 reg0[1] |= 0x03;
663
664 /* already enabled - do not reenable i2c repeater or TX fails */
Andrew de Quinceye87d41c2006-04-18 17:47:11 -0300665 if (fe->ops->i2c_gate_ctrl)
666 fe->ops->i2c_gate_ctrl(fe, 1);
Andrew de Quinceyeffa7912006-01-09 15:25:09 -0200667 msg.buf = reg0;
668 msg.len = sizeof(reg0);
Andrew de Quinceye87d41c2006-04-18 17:47:11 -0300669 if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1)
Andrew de Quinceyeffa7912006-01-09 15:25:09 -0200670 return -EIO;
671
Andrew de Quinceye87d41c2006-04-18 17:47:11 -0300672 if (fe->ops->i2c_gate_ctrl)
673 fe->ops->i2c_gate_ctrl(fe, 1);
Andrew de Quinceyeffa7912006-01-09 15:25:09 -0200674 msg.buf = reg1;
675 msg.len = sizeof(reg1);
Andrew de Quinceye87d41c2006-04-18 17:47:11 -0300676 if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1)
Andrew de Quinceyeffa7912006-01-09 15:25:09 -0200677 return -EIO;
678
Andrew de Quinceye87d41c2006-04-18 17:47:11 -0300679 if (fe->ops->i2c_gate_ctrl)
680 fe->ops->i2c_gate_ctrl(fe, 1);
Andrew de Quinceyeffa7912006-01-09 15:25:09 -0200681 msg.buf = reg2;
682 msg.len = sizeof(reg2);
Andrew de Quinceye87d41c2006-04-18 17:47:11 -0300683 if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1)
Andrew de Quinceyeffa7912006-01-09 15:25:09 -0200684 return -EIO;
685
686 return 0;
687}
688
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689static u8 typhoon_cinergy1200s_inittab[] = {
690 0x01, 0x15,
691 0x02, 0x30,
692 0x03, 0x00,
693 0x04, 0x7d, /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */
694 0x05, 0x35, /* I2CT = 0, SCLT = 1, SDAT = 1 */
695 0x06, 0x40, /* DAC not used, set to high impendance mode */
696 0x07, 0x00, /* DAC LSB */
697 0x08, 0x40, /* DiSEqC off */
698 0x09, 0x00, /* FIFO */
699 0x0c, 0x51, /* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */
700 0x0d, 0x82, /* DC offset compensation = ON, beta_agc1 = 2 */
701 0x0e, 0x23, /* alpha_tmg = 2, beta_tmg = 3 */
702 0x10, 0x3f, // AGC2 0x3d
703 0x11, 0x84,
Oliver Endrissff29d062005-11-08 21:35:43 -0800704 0x12, 0xb9,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705 0x15, 0xc9, // lock detector threshold
706 0x16, 0x00,
707 0x17, 0x00,
708 0x18, 0x00,
709 0x19, 0x00,
710 0x1a, 0x00,
711 0x1f, 0x50,
712 0x20, 0x00,
713 0x21, 0x00,
714 0x22, 0x00,
715 0x23, 0x00,
716 0x28, 0x00, // out imp: normal out type: parallel FEC mode:0
717 0x29, 0x1e, // 1/2 threshold
718 0x2a, 0x14, // 2/3 threshold
719 0x2b, 0x0f, // 3/4 threshold
720 0x2c, 0x09, // 5/6 threshold
721 0x2d, 0x05, // 7/8 threshold
722 0x2e, 0x01,
723 0x31, 0x1f, // test all FECs
724 0x32, 0x19, // viterbi and synchro search
725 0x33, 0xfc, // rs control
726 0x34, 0x93, // error control
727 0x0f, 0x92,
728 0xff, 0xff
729};
730
731static struct stv0299_config typhoon_config = {
732 .demod_address = 0x68,
733 .inittab = typhoon_cinergy1200s_inittab,
734 .mclk = 88000000UL,
735 .invert = 0,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736 .skip_reinit = 0,
737 .lock_output = STV0229_LOCKOUTPUT_1,
738 .volt13_op0_op1 = STV0299_VOLT13_OP0,
739 .min_delay_ms = 100,
740 .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741};
742
743
744static struct stv0299_config cinergy_1200s_config = {
745 .demod_address = 0x68,
746 .inittab = typhoon_cinergy1200s_inittab,
747 .mclk = 88000000UL,
748 .invert = 0,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749 .skip_reinit = 0,
750 .lock_output = STV0229_LOCKOUTPUT_0,
751 .volt13_op0_op1 = STV0299_VOLT13_OP0,
752 .min_delay_ms = 100,
753 .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754};
755
Andrew de Quinceyeffa7912006-01-09 15:25:09 -0200756static struct stv0299_config cinergy_1200s_1894_0010_config = {
757 .demod_address = 0x68,
758 .inittab = typhoon_cinergy1200s_inittab,
759 .mclk = 88000000UL,
760 .invert = 1,
761 .skip_reinit = 0,
762 .lock_output = STV0229_LOCKOUTPUT_1,
763 .volt13_op0_op1 = STV0299_VOLT13_OP0,
764 .min_delay_ms = 100,
765 .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate,
Andrew de Quinceyeffa7912006-01-09 15:25:09 -0200766};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767
Andrew de Quinceye87d41c2006-04-18 17:47:11 -0300768static int philips_cu1216_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769{
770 struct budget *budget = (struct budget *) fe->dvb->priv;
771 u8 buf[4];
772 struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = buf,.len = sizeof(buf) };
773
774#define TUNER_MUL 62500
775
776 u32 div = (params->frequency + 36125000 + TUNER_MUL / 2) / TUNER_MUL;
777
778 buf[0] = (div >> 8) & 0x7f;
779 buf[1] = div & 0xff;
Johannes Stezenbacheef57642005-07-07 17:57:58 -0700780 buf[2] = 0x86;
781 buf[3] = (params->frequency < 150000000 ? 0x01 :
782 params->frequency < 445000000 ? 0x02 : 0x04);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783
Andrew de Quinceye87d41c2006-04-18 17:47:11 -0300784 if (fe->ops->i2c_gate_ctrl)
785 fe->ops->i2c_gate_ctrl(fe, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786 if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1)
787 return -EIO;
788 return 0;
789}
790
791static struct tda10021_config philips_cu1216_config = {
792 .demod_address = 0x0c,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793};
794
795
796
797
Andrew de Quinceye87d41c2006-04-18 17:47:11 -0300798static int philips_tu1216_tuner_init(struct dvb_frontend *fe)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799{
800 struct budget *budget = (struct budget *) fe->dvb->priv;
801 static u8 tu1216_init[] = { 0x0b, 0xf5, 0x85, 0xab };
802 struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tu1216_init,.len = sizeof(tu1216_init) };
803
804 // setup PLL configuration
Andrew de Quinceye87d41c2006-04-18 17:47:11 -0300805 if (fe->ops->i2c_gate_ctrl)
806 fe->ops->i2c_gate_ctrl(fe, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807 if (i2c_transfer(&budget->i2c_adap, &tuner_msg, 1) != 1)
808 return -EIO;
809 msleep(1);
810
811 return 0;
812}
813
Andrew de Quinceye87d41c2006-04-18 17:47:11 -0300814static int philips_tu1216_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815{
816 struct budget *budget = (struct budget *) fe->dvb->priv;
817 u8 tuner_buf[4];
818 struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tuner_buf,.len =
819 sizeof(tuner_buf) };
820 int tuner_frequency = 0;
821 u8 band, cp, filter;
822
823 // determine charge pump
824 tuner_frequency = params->frequency + 36166000;
825 if (tuner_frequency < 87000000)
826 return -EINVAL;
827 else if (tuner_frequency < 130000000)
828 cp = 3;
829 else if (tuner_frequency < 160000000)
830 cp = 5;
831 else if (tuner_frequency < 200000000)
832 cp = 6;
833 else if (tuner_frequency < 290000000)
834 cp = 3;
835 else if (tuner_frequency < 420000000)
836 cp = 5;
837 else if (tuner_frequency < 480000000)
838 cp = 6;
839 else if (tuner_frequency < 620000000)
840 cp = 3;
841 else if (tuner_frequency < 830000000)
842 cp = 5;
843 else if (tuner_frequency < 895000000)
844 cp = 7;
845 else
846 return -EINVAL;
847
848 // determine band
849 if (params->frequency < 49000000)
850 return -EINVAL;
851 else if (params->frequency < 161000000)
852 band = 1;
853 else if (params->frequency < 444000000)
854 band = 2;
855 else if (params->frequency < 861000000)
856 band = 4;
857 else
858 return -EINVAL;
859
860 // setup PLL filter
861 switch (params->u.ofdm.bandwidth) {
862 case BANDWIDTH_6_MHZ:
863 filter = 0;
864 break;
865
866 case BANDWIDTH_7_MHZ:
867 filter = 0;
868 break;
869
870 case BANDWIDTH_8_MHZ:
871 filter = 1;
872 break;
873
874 default:
875 return -EINVAL;
876 }
877
878 // calculate divisor
879 // ((36166000+((1000000/6)/2)) + Finput)/(1000000/6)
880 tuner_frequency = (((params->frequency / 1000) * 6) + 217496) / 1000;
881
882 // setup tuner buffer
883 tuner_buf[0] = (tuner_frequency >> 8) & 0x7f;
884 tuner_buf[1] = tuner_frequency & 0xff;
885 tuner_buf[2] = 0xca;
886 tuner_buf[3] = (cp << 5) | (filter << 3) | band;
887
Andrew de Quinceye87d41c2006-04-18 17:47:11 -0300888 if (fe->ops->i2c_gate_ctrl)
889 fe->ops->i2c_gate_ctrl(fe, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890 if (i2c_transfer(&budget->i2c_adap, &tuner_msg, 1) != 1)
891 return -EIO;
892
893 msleep(1);
894 return 0;
895}
896
897static int philips_tu1216_request_firmware(struct dvb_frontend *fe,
898 const struct firmware **fw, char *name)
899{
900 struct budget *budget = (struct budget *) fe->dvb->priv;
901
902 return request_firmware(fw, name, &budget->dev->pci->dev);
903}
904
905static struct tda1004x_config philips_tu1216_config = {
906
907 .demod_address = 0x8,
908 .invert = 1,
909 .invert_oclk = 1,
Hartmut Hackmannecb60de2005-07-07 17:57:40 -0700910 .xtal_freq = TDA10046_XTAL_4M,
911 .agc_config = TDA10046_AGC_DEFAULT,
912 .if_freq = TDA10046_FREQ_3617,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913 .request_firmware = philips_tu1216_request_firmware,
914};
915
Regis Prevotf8bf1342006-01-11 23:31:53 -0200916static u8 philips_sd1878_inittab[] = {
917 0x01, 0x15,
918 0x02, 0x30,
919 0x03, 0x00,
920 0x04, 0x7d,
921 0x05, 0x35,
922 0x06, 0x40,
923 0x07, 0x00,
924 0x08, 0x43,
925 0x09, 0x02,
926 0x0C, 0x51,
927 0x0D, 0x82,
928 0x0E, 0x23,
929 0x10, 0x3f,
930 0x11, 0x84,
931 0x12, 0xb9,
932 0x15, 0xc9,
933 0x16, 0x19,
934 0x17, 0x8c,
935 0x18, 0x59,
936 0x19, 0xf8,
937 0x1a, 0xfe,
938 0x1c, 0x7f,
939 0x1d, 0x00,
940 0x1e, 0x00,
941 0x1f, 0x50,
942 0x20, 0x00,
943 0x21, 0x00,
944 0x22, 0x00,
945 0x23, 0x00,
946 0x28, 0x00,
947 0x29, 0x28,
948 0x2a, 0x14,
949 0x2b, 0x0f,
950 0x2c, 0x09,
951 0x2d, 0x09,
952 0x31, 0x1f,
953 0x32, 0x19,
954 0x33, 0xfc,
955 0x34, 0x93,
956 0xff, 0xff
957};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958
Andrew de Quinceye87d41c2006-04-18 17:47:11 -0300959static int philips_sd1878_tda8261_tuner_set_params(struct dvb_frontend *fe,
960 struct dvb_frontend_parameters *params)
Regis Prevotf8bf1342006-01-11 23:31:53 -0200961{
962 u8 buf[4];
963 int rc;
964 struct i2c_msg tuner_msg = {.addr=0x60,.flags=0,.buf=buf,.len=sizeof(buf)};
Andrew de Quinceye87d41c2006-04-18 17:47:11 -0300965 struct budget *budget = (struct budget *) fe->dvb->priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966
Regis Prevotf8bf1342006-01-11 23:31:53 -0200967 if((params->frequency < 950000) || (params->frequency > 2150000))
968 return -EINVAL;
969
970 rc=dvb_pll_configure(&dvb_pll_philips_sd1878_tda8261, buf,
971 params->frequency, 0);
972 if(rc < 0) return rc;
973
Andrew de Quinceye87d41c2006-04-18 17:47:11 -0300974 if (fe->ops->i2c_gate_ctrl)
975 fe->ops->i2c_gate_ctrl(fe, 1);
976 if(i2c_transfer(&budget->i2c_adap, &tuner_msg, 1) != 1)
Regis Prevotf8bf1342006-01-11 23:31:53 -0200977 return -EIO;
978
979 return 0;
980}
981
982static int philips_sd1878_ci_set_symbol_rate(struct dvb_frontend *fe,
983 u32 srate, u32 ratio)
984{
985 u8 aclk = 0;
986 u8 bclk = 0;
987 u8 m1;
988
989 aclk = 0xb5;
990 if (srate < 2000000)
991 bclk = 0x86;
992 else if (srate < 5000000)
993 bclk = 0x89;
994 else if (srate < 15000000)
995 bclk = 0x8f;
996 else if (srate < 45000000)
997 bclk = 0x95;
998
999 m1 = 0x14;
1000 if (srate < 4000000)
1001 m1 = 0x10;
1002
1003 stv0299_writereg(fe, 0x0e, 0x23);
1004 stv0299_writereg(fe, 0x0f, 0x94);
1005 stv0299_writereg(fe, 0x10, 0x39);
1006 stv0299_writereg(fe, 0x13, aclk);
1007 stv0299_writereg(fe, 0x14, bclk);
1008 stv0299_writereg(fe, 0x15, 0xc9);
1009 stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff);
1010 stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff);
1011 stv0299_writereg(fe, 0x21, (ratio) & 0xf0);
1012 stv0299_writereg(fe, 0x0f, 0x80 | m1);
1013
1014 return 0;
1015}
1016
1017static struct stv0299_config philips_sd1878_config = {
1018 .demod_address = 0x68,
Andrew de Quincey5c1208b2006-05-22 10:32:02 -03001019 .inittab = philips_sd1878_inittab,
Regis Prevotf8bf1342006-01-11 23:31:53 -02001020 .mclk = 88000000UL,
1021 .invert = 0,
1022 .skip_reinit = 0,
1023 .lock_output = STV0229_LOCKOUTPUT_1,
1024 .volt13_op0_op1 = STV0299_VOLT13_OP0,
1025 .min_delay_ms = 100,
1026 .set_symbol_rate = philips_sd1878_ci_set_symbol_rate,
Regis Prevotf8bf1342006-01-11 23:31:53 -02001027};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028
1029static u8 read_pwm(struct budget_av *budget_av)
1030{
1031 u8 b = 0xff;
1032 u8 pwm;
1033 struct i2c_msg msg[] = { {.addr = 0x50,.flags = 0,.buf = &b,.len = 1},
1034 {.addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1}
1035 };
1036
1037 if ((i2c_transfer(&budget_av->budget.i2c_adap, msg, 2) != 2)
1038 || (pwm == 0xff))
1039 pwm = 0x48;
1040
1041 return pwm;
1042}
1043
Johannes Stezenbachb82a96a2005-05-16 21:54:49 -07001044#define SUBID_DVBS_KNC1 0x0010
1045#define SUBID_DVBS_KNC1_PLUS 0x0011
1046#define SUBID_DVBS_TYPHOON 0x4f56
1047#define SUBID_DVBS_CINERGY1200 0x1154
Regis Prevotf8bf1342006-01-11 23:31:53 -02001048#define SUBID_DVBS_CYNERGY1200N 0x1155
Johannes Stezenbachb82a96a2005-05-16 21:54:49 -07001049
Regis Prevotf8bf1342006-01-11 23:31:53 -02001050#define SUBID_DVBS_TV_STAR 0x0014
1051#define SUBID_DVBS_TV_STAR_CI 0x0016
Thilo Berger36f4f332006-02-27 00:09:08 -03001052#define SUBID_DVBS_EASYWATCH 0x001e
Johannes Stezenbachb82a96a2005-05-16 21:54:49 -07001053#define SUBID_DVBC_KNC1 0x0020
1054#define SUBID_DVBC_KNC1_PLUS 0x0021
1055#define SUBID_DVBC_CINERGY1200 0x1156
1056
1057#define SUBID_DVBT_KNC1_PLUS 0x0031
1058#define SUBID_DVBT_KNC1 0x0030
1059#define SUBID_DVBT_CINERGY1200 0x1157
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060
Andrew de Quincey5c1208b2006-05-22 10:32:02 -03001061
1062static int tda10021_set_frontend(struct dvb_frontend *fe,
1063 struct dvb_frontend_parameters *p)
1064{
1065 struct budget_av* budget_av = fe->dvb->priv;
1066 int result;
1067
1068 result = budget_av->tda10021_set_frontend(fe, p);
1069 if (budget_av->tda10021_ts_enabled) {
1070 tda10021_write_byte(budget_av->budget.dvb_frontend, 0x12, 0xa1);
1071 } else {
1072 tda10021_write_byte(budget_av->budget.dvb_frontend, 0x12, 0xa0);
1073 }
1074
1075 return result;
1076}
1077
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078static void frontend_init(struct budget_av *budget_av)
1079{
Johannes Stezenbachb82a96a2005-05-16 21:54:49 -07001080 struct saa7146_dev * saa = budget_av->budget.dev;
1081 struct dvb_frontend * fe = NULL;
1082
Andrew de Quincey473f5422006-04-13 17:29:07 -03001083 /* Enable / PowerON Frontend */
1084 saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO);
1085
1086 /* additional setup necessary for the PLUS cards */
Johannes Stezenbachb82a96a2005-05-16 21:54:49 -07001087 switch (saa->pci->subsystem_device) {
1088 case SUBID_DVBS_KNC1_PLUS:
1089 case SUBID_DVBC_KNC1_PLUS:
1090 case SUBID_DVBT_KNC1_PLUS:
Johannes Stezenbachb82a96a2005-05-16 21:54:49 -07001091 saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTHI);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092 break;
Johannes Stezenbachb82a96a2005-05-16 21:54:49 -07001093 }
1094
1095 switch (saa->pci->subsystem_device) {
1096
1097 case SUBID_DVBS_KNC1:
Andrew de Quinceyeffa7912006-01-09 15:25:09 -02001098 if (saa->pci->subsystem_vendor == 0x1894) {
1099 fe = stv0299_attach(&cinergy_1200s_1894_0010_config,
1100 &budget_av->budget.i2c_adap);
Andrew de Quinceye87d41c2006-04-18 17:47:11 -03001101 if (fe) {
1102 fe->ops->tuner_ops.set_params = philips_su1278sh2_tua6100_tuner_set_params;
1103 }
Andrew de Quinceyeffa7912006-01-09 15:25:09 -02001104 } else {
1105 fe = stv0299_attach(&typhoon_config,
1106 &budget_av->budget.i2c_adap);
Andrew de Quinceye87d41c2006-04-18 17:47:11 -03001107 if (fe) {
1108 fe->ops->tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params;
1109 }
Andrew de Quinceyeffa7912006-01-09 15:25:09 -02001110 }
1111 break;
1112
Regis Prevotf8bf1342006-01-11 23:31:53 -02001113 case SUBID_DVBS_TV_STAR:
1114 case SUBID_DVBS_TV_STAR_CI:
1115 case SUBID_DVBS_CYNERGY1200N:
Thilo Berger36f4f332006-02-27 00:09:08 -03001116 case SUBID_DVBS_EASYWATCH:
Regis Prevotf8bf1342006-01-11 23:31:53 -02001117 fe = stv0299_attach(&philips_sd1878_config,
1118 &budget_av->budget.i2c_adap);
Andrew de Quinceye87d41c2006-04-18 17:47:11 -03001119 if (fe) {
1120 fe->ops->tuner_ops.set_params = philips_sd1878_tda8261_tuner_set_params;
1121 }
Regis Prevotf8bf1342006-01-11 23:31:53 -02001122 break;
1123
Johannes Stezenbachb82a96a2005-05-16 21:54:49 -07001124 case SUBID_DVBS_KNC1_PLUS:
1125 case SUBID_DVBS_TYPHOON:
1126 fe = stv0299_attach(&typhoon_config,
1127 &budget_av->budget.i2c_adap);
Andrew de Quinceye87d41c2006-04-18 17:47:11 -03001128 if (fe) {
1129 fe->ops->tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params;
1130 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131 break;
1132
Johannes Stezenbachb82a96a2005-05-16 21:54:49 -07001133 case SUBID_DVBS_CINERGY1200:
1134 fe = stv0299_attach(&cinergy_1200s_config,
1135 &budget_av->budget.i2c_adap);
Andrew de Quinceye87d41c2006-04-18 17:47:11 -03001136 if (fe) {
1137 fe->ops->tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params;
1138 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139 break;
1140
Johannes Stezenbachb82a96a2005-05-16 21:54:49 -07001141 case SUBID_DVBC_KNC1:
1142 case SUBID_DVBC_KNC1_PLUS:
Andrew de Quincey5c1208b2006-05-22 10:32:02 -03001143 case SUBID_DVBC_CINERGY1200:
1144 budget_av->reinitialise_demod = 1;
Johannes Stezenbachb82a96a2005-05-16 21:54:49 -07001145 fe = tda10021_attach(&philips_cu1216_config,
1146 &budget_av->budget.i2c_adap,
1147 read_pwm(budget_av));
Andrew de Quinceye87d41c2006-04-18 17:47:11 -03001148 if (fe) {
Andrew de Quincey5c1208b2006-05-22 10:32:02 -03001149 budget_av->tda10021_poclkp = 1;
1150 budget_av->tda10021_set_frontend = fe->ops->set_frontend;
1151 fe->ops->set_frontend = tda10021_set_frontend;
Andrew de Quinceye87d41c2006-04-18 17:47:11 -03001152 fe->ops->tuner_ops.set_params = philips_cu1216_tuner_set_params;
1153 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 break;
1155
Johannes Stezenbachb82a96a2005-05-16 21:54:49 -07001156 case SUBID_DVBT_KNC1:
1157 case SUBID_DVBT_KNC1_PLUS:
Johannes Stezenbachb82a96a2005-05-16 21:54:49 -07001158 case SUBID_DVBT_CINERGY1200:
Andrew de Quincey5c1208b2006-05-22 10:32:02 -03001159 budget_av->reinitialise_demod = 1;
Johannes Stezenbachb82a96a2005-05-16 21:54:49 -07001160 fe = tda10046_attach(&philips_tu1216_config,
1161 &budget_av->budget.i2c_adap);
Andrew de Quincey6b3ccab2006-04-20 12:01:47 -03001162 if (fe) {
1163 fe->ops->tuner_ops.init = philips_tu1216_tuner_init;
1164 fe->ops->tuner_ops.set_params = philips_tu1216_tuner_set_params;
1165 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166 break;
1167 }
1168
Johannes Stezenbachb82a96a2005-05-16 21:54:49 -07001169 if (fe == NULL) {
1170 printk(KERN_ERR "budget-av: A frontend driver was not found "
1171 "for device %04x/%04x subsystem %04x/%04x\n",
1172 saa->pci->vendor,
1173 saa->pci->device,
1174 saa->pci->subsystem_vendor,
1175 saa->pci->subsystem_device);
1176 return;
1177 }
1178
1179 budget_av->budget.dvb_frontend = fe;
1180
1181 if (dvb_register_frontend(&budget_av->budget.dvb_adapter,
1182 budget_av->budget.dvb_frontend)) {
1183 printk(KERN_ERR "budget-av: Frontend registration failed!\n");
1184 if (budget_av->budget.dvb_frontend->ops->release)
1185 budget_av->budget.dvb_frontend->ops->release(budget_av->budget.dvb_frontend);
1186 budget_av->budget.dvb_frontend = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187 }
1188}
1189
1190
1191static void budget_av_irq(struct saa7146_dev *dev, u32 * isr)
1192{
1193 struct budget_av *budget_av = (struct budget_av *) dev->ext_priv;
1194
1195 dprintk(8, "dev: %p, budget_av: %p\n", dev, budget_av);
1196
1197 if (*isr & MASK_10)
1198 ttpci_budget_irq10_handler(dev, isr);
1199}
1200
1201static int budget_av_detach(struct saa7146_dev *dev)
1202{
1203 struct budget_av *budget_av = (struct budget_av *) dev->ext_priv;
1204 int err;
1205
1206 dprintk(2, "dev: %p\n", dev);
1207
1208 if (1 == budget_av->has_saa7113) {
1209 saa7146_setgpio(dev, 0, SAA7146_GPIO_OUTLO);
1210
1211 msleep(200);
1212
1213 saa7146_unregister_device(&budget_av->vd, dev);
1214 }
1215
1216 if (budget_av->budget.ci_present)
1217 ciintf_deinit(budget_av);
1218
1219 if (budget_av->budget.dvb_frontend != NULL)
1220 dvb_unregister_frontend(budget_av->budget.dvb_frontend);
1221 err = ttpci_budget_deinit(&budget_av->budget);
1222
1223 kfree(budget_av);
1224
1225 return err;
1226}
1227
1228static struct saa7146_ext_vv vv_data;
1229
1230static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
1231{
1232 struct budget_av *budget_av;
1233 u8 *mac;
1234 int err;
1235
1236 dprintk(2, "dev: %p\n", dev);
1237
Panagiotis Issaris74081872006-01-11 19:40:56 -02001238 if (!(budget_av = kzalloc(sizeof(struct budget_av), GFP_KERNEL)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001239 return -ENOMEM;
1240
Johannes Stezenbachb82a96a2005-05-16 21:54:49 -07001241 budget_av->has_saa7113 = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001242 budget_av->budget.ci_present = 0;
1243
1244 dev->ext_priv = budget_av;
1245
1246 if ((err = ttpci_budget_init(&budget_av->budget, dev, info, THIS_MODULE))) {
1247 kfree(budget_av);
1248 return err;
1249 }
1250
1251 /* knc1 initialization */
1252 saa7146_write(dev, DD1_STREAM_B, 0x04000000);
1253 saa7146_write(dev, DD1_INIT, 0x07000600);
1254 saa7146_write(dev, MC2, MASK_09 | MASK_25 | MASK_10 | MASK_26);
1255
Johannes Stezenbachb82a96a2005-05-16 21:54:49 -07001256 if (saa7113_init(budget_av) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001257 budget_av->has_saa7113 = 1;
1258
1259 if (0 != saa7146_vv_init(dev, &vv_data)) {
1260 /* fixme: proper cleanup here */
1261 ERR(("cannot init vv subsystem.\n"));
1262 return err;
1263 }
1264
1265 if ((err = saa7146_register_device(&budget_av->vd, dev, "knc1", VFL_TYPE_GRABBER))) {
1266 /* fixme: proper cleanup here */
1267 ERR(("cannot register capture v4l2 device.\n"));
1268 return err;
1269 }
1270
1271 /* beware: this modifies dev->vv ... */
1272 saa7146_set_hps_source_and_sync(dev, SAA7146_HPS_SOURCE_PORT_A,
1273 SAA7146_HPS_SYNC_PORT_A);
1274
1275 saa7113_setinput(budget_av, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001276 }
1277
1278 /* fixme: find some sane values here... */
1279 saa7146_write(dev, PCI_BT_V1, 0x1c00101f);
1280
Johannes Stezenbachfdc53a62005-05-16 21:54:39 -07001281 mac = budget_av->budget.dvb_adapter.proposed_mac;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282 if (i2c_readregs(&budget_av->budget.i2c_adap, 0xa0, 0x30, mac, 6)) {
Johannes Stezenbachb82a96a2005-05-16 21:54:49 -07001283 printk(KERN_ERR "KNC1-%d: Could not read MAC from KNC1 card\n",
Johannes Stezenbachfdc53a62005-05-16 21:54:39 -07001284 budget_av->budget.dvb_adapter.num);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285 memset(mac, 0, 6);
1286 } else {
Johannes Stezenbachb82a96a2005-05-16 21:54:49 -07001287 printk(KERN_INFO "KNC1-%d: MAC addr = %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
Johannes Stezenbachfdc53a62005-05-16 21:54:39 -07001288 budget_av->budget.dvb_adapter.num,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001289 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
1290 }
1291
Johannes Stezenbachfdc53a62005-05-16 21:54:39 -07001292 budget_av->budget.dvb_adapter.priv = budget_av;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293 frontend_init(budget_av);
1294
Andrew de Quincey71a8dff2006-04-06 09:42:46 -03001295 if (!budget_av->has_saa7113) {
1296 ciintf_init(budget_av);
1297 }
1298
Linus Torvalds1da177e2005-04-16 15:20:36 -07001299 return 0;
1300}
1301
1302#define KNC1_INPUTS 2
1303static struct v4l2_input knc1_inputs[KNC1_INPUTS] = {
1304 {0, "Composite", V4L2_INPUT_TYPE_TUNER, 1, 0, V4L2_STD_PAL_BG | V4L2_STD_NTSC_M, 0},
1305 {1, "S-Video", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG | V4L2_STD_NTSC_M, 0},
1306};
1307
1308static struct saa7146_extension_ioctls ioctls[] = {
1309 {VIDIOC_ENUMINPUT, SAA7146_EXCLUSIVE},
1310 {VIDIOC_G_INPUT, SAA7146_EXCLUSIVE},
1311 {VIDIOC_S_INPUT, SAA7146_EXCLUSIVE},
1312 {0, 0}
1313};
1314
1315static int av_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg)
1316{
1317 struct saa7146_dev *dev = fh->dev;
1318 struct budget_av *budget_av = (struct budget_av *) dev->ext_priv;
1319
1320 switch (cmd) {
1321 case VIDIOC_ENUMINPUT:{
1322 struct v4l2_input *i = arg;
1323
1324 dprintk(1, "VIDIOC_ENUMINPUT %d.\n", i->index);
1325 if (i->index < 0 || i->index >= KNC1_INPUTS) {
1326 return -EINVAL;
1327 }
1328 memcpy(i, &knc1_inputs[i->index], sizeof(struct v4l2_input));
1329 return 0;
1330 }
1331 case VIDIOC_G_INPUT:{
1332 int *input = (int *) arg;
1333
1334 *input = budget_av->cur_input;
1335
1336 dprintk(1, "VIDIOC_G_INPUT %d.\n", *input);
1337 return 0;
1338 }
1339 case VIDIOC_S_INPUT:{
1340 int input = *(int *) arg;
1341 dprintk(1, "VIDIOC_S_INPUT %d.\n", input);
1342 return saa7113_setinput(budget_av, input);
1343 }
1344 default:
1345 return -ENOIOCTLCMD;
1346 }
1347 return 0;
1348}
1349
1350static struct saa7146_standard standard[] = {
1351 {.name = "PAL",.id = V4L2_STD_PAL,
1352 .v_offset = 0x17,.v_field = 288,
1353 .h_offset = 0x14,.h_pixels = 680,
1354 .v_max_out = 576,.h_max_out = 768 },
1355
1356 {.name = "NTSC",.id = V4L2_STD_NTSC,
1357 .v_offset = 0x16,.v_field = 240,
1358 .h_offset = 0x06,.h_pixels = 708,
1359 .v_max_out = 480,.h_max_out = 640, },
1360};
1361
1362static struct saa7146_ext_vv vv_data = {
1363 .inputs = 2,
1364 .capabilities = 0, // perhaps later: V4L2_CAP_VBI_CAPTURE, but that need tweaking with the saa7113
1365 .flags = 0,
1366 .stds = &standard[0],
1367 .num_stds = sizeof(standard) / sizeof(struct saa7146_standard),
1368 .ioctls = &ioctls[0],
1369 .ioctl = av_ioctl,
1370};
1371
1372static struct saa7146_extension budget_extension;
1373
1374MAKE_BUDGET_INFO(knc1s, "KNC1 DVB-S", BUDGET_KNC1S);
1375MAKE_BUDGET_INFO(knc1c, "KNC1 DVB-C", BUDGET_KNC1C);
1376MAKE_BUDGET_INFO(knc1t, "KNC1 DVB-T", BUDGET_KNC1T);
Regis Prevotf8bf1342006-01-11 23:31:53 -02001377MAKE_BUDGET_INFO(kncxs, "KNC TV STAR DVB-S", BUDGET_TVSTAR);
Thilo Berger36f4f332006-02-27 00:09:08 -03001378MAKE_BUDGET_INFO(satewpls, "Satelco EasyWatch DVB-S light", BUDGET_TVSTAR);
Johannes Stezenbach2d4f2c22005-05-16 21:54:23 -07001379MAKE_BUDGET_INFO(knc1sp, "KNC1 DVB-S Plus", BUDGET_KNC1SP);
1380MAKE_BUDGET_INFO(knc1cp, "KNC1 DVB-C Plus", BUDGET_KNC1CP);
1381MAKE_BUDGET_INFO(knc1tp, "KNC1 DVB-T Plus", BUDGET_KNC1TP);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001382MAKE_BUDGET_INFO(cin1200s, "TerraTec Cinergy 1200 DVB-S", BUDGET_CIN1200S);
Regis Prevotf8bf1342006-01-11 23:31:53 -02001383MAKE_BUDGET_INFO(cin1200sn, "TerraTec Cinergy 1200 DVB-S", BUDGET_CIN1200S);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384MAKE_BUDGET_INFO(cin1200c, "Terratec Cinergy 1200 DVB-C", BUDGET_CIN1200C);
1385MAKE_BUDGET_INFO(cin1200t, "Terratec Cinergy 1200 DVB-T", BUDGET_CIN1200T);
1386
1387static struct pci_device_id pci_tbl[] = {
1388 MAKE_EXTENSION_PCI(knc1s, 0x1131, 0x4f56),
Johannes Stezenbach2d4f2c22005-05-16 21:54:23 -07001389 MAKE_EXTENSION_PCI(knc1s, 0x1131, 0x0010),
Andrew de Quinceyeffa7912006-01-09 15:25:09 -02001390 MAKE_EXTENSION_PCI(knc1s, 0x1894, 0x0010),
Johannes Stezenbach2d4f2c22005-05-16 21:54:23 -07001391 MAKE_EXTENSION_PCI(knc1sp, 0x1131, 0x0011),
Regis Prevotf8bf1342006-01-11 23:31:53 -02001392 MAKE_EXTENSION_PCI(kncxs, 0x1894, 0x0014),
1393 MAKE_EXTENSION_PCI(kncxs, 0x1894, 0x0016),
Thilo Berger36f4f332006-02-27 00:09:08 -03001394 MAKE_EXTENSION_PCI(satewpls, 0x1894, 0x001e),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001395 MAKE_EXTENSION_PCI(knc1c, 0x1894, 0x0020),
Johannes Stezenbach2d4f2c22005-05-16 21:54:23 -07001396 MAKE_EXTENSION_PCI(knc1cp, 0x1894, 0x0021),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001397 MAKE_EXTENSION_PCI(knc1t, 0x1894, 0x0030),
Johannes Stezenbach2d4f2c22005-05-16 21:54:23 -07001398 MAKE_EXTENSION_PCI(knc1tp, 0x1894, 0x0031),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001399 MAKE_EXTENSION_PCI(cin1200s, 0x153b, 0x1154),
Regis Prevotf8bf1342006-01-11 23:31:53 -02001400 MAKE_EXTENSION_PCI(cin1200sn, 0x153b, 0x1155),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401 MAKE_EXTENSION_PCI(cin1200c, 0x153b, 0x1156),
1402 MAKE_EXTENSION_PCI(cin1200t, 0x153b, 0x1157),
1403 {
1404 .vendor = 0,
1405 }
1406};
1407
1408MODULE_DEVICE_TABLE(pci, pci_tbl);
1409
1410static struct saa7146_extension budget_extension = {
Julian Scheel27b05fd2005-07-12 13:58:39 -07001411 .name = "budget_av",
Oliver Endriss69459f32005-12-01 00:51:48 -08001412 .flags = SAA7146_I2C_SHORT_DELAY,
1413
Linus Torvalds1da177e2005-04-16 15:20:36 -07001414 .pci_tbl = pci_tbl,
1415
1416 .module = THIS_MODULE,
1417 .attach = budget_av_attach,
1418 .detach = budget_av_detach,
1419
1420 .irq_mask = MASK_10,
1421 .irq_func = budget_av_irq,
1422};
1423
1424static int __init budget_av_init(void)
1425{
1426 return saa7146_register_extension(&budget_extension);
1427}
1428
1429static void __exit budget_av_exit(void)
1430{
1431 saa7146_unregister_extension(&budget_extension);
1432}
1433
1434module_init(budget_av_init);
1435module_exit(budget_av_exit);
1436
1437MODULE_LICENSE("GPL");
1438MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, Michael Hunold, others");
1439MODULE_DESCRIPTION("driver for the SAA7146 based so-called "
1440 "budget PCI DVB w/ analog input and CI-module (e.g. the KNC cards)");