diff options
author | Steven Toth <stoth@linuxtv.org> | 2008-09-04 00:14:43 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2008-10-12 07:37:03 -0400 |
commit | 0d46748c3f874defbbbf98bcf40c7b18964abbc0 (patch) | |
tree | c1ea0786742508d2a95c0671040149143e4b10d0 /drivers | |
parent | 6b73eeafbc856c0cef7166242f0e55403407f355 (diff) |
V4L/DVB (8986): cx24116: Adding DVB-S2 demodulator support
Adds support for the COnexant cx24116 DVB-S2 demodulator.
TODO: checkpatch cleanup.
Signed-off-by: Steven Toth <stoth@linuxtv.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/media/dvb/frontends/Kconfig | 7 | ||||
-rw-r--r-- | drivers/media/dvb/frontends/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/dvb/frontends/cx24116.c | 950 | ||||
-rw-r--r-- | drivers/media/dvb/frontends/cx24116.h | 50 |
4 files changed, 1008 insertions, 0 deletions
diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig index 097d9f00144f..d2f31da26cbc 100644 --- a/drivers/media/dvb/frontends/Kconfig +++ b/drivers/media/dvb/frontends/Kconfig | |||
@@ -92,6 +92,13 @@ config DVB_TUA6100 | |||
92 | help | 92 | help |
93 | A DVB-S PLL chip. | 93 | A DVB-S PLL chip. |
94 | 94 | ||
95 | config DVB_CX24116 | ||
96 | tristate "Conexant CX24116 based" | ||
97 | depends on DVB_CORE && I2C | ||
98 | default m if DVB_FE_CUSTOMISE | ||
99 | help | ||
100 | A DVB-S/S2 tuner module. Say Y when you want to support this frontend. | ||
101 | |||
95 | comment "DVB-T (terrestrial) frontends" | 102 | comment "DVB-T (terrestrial) frontends" |
96 | depends on DVB_CORE | 103 | depends on DVB_CORE |
97 | 104 | ||
diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile index 1b5478886240..5deb8a542abd 100644 --- a/drivers/media/dvb/frontends/Makefile +++ b/drivers/media/dvb/frontends/Makefile | |||
@@ -51,3 +51,4 @@ obj-$(CONFIG_DVB_S5H1411) += s5h1411.o | |||
51 | obj-$(CONFIG_DVB_LGS8GL5) += lgs8gl5.o | 51 | obj-$(CONFIG_DVB_LGS8GL5) += lgs8gl5.o |
52 | obj-$(CONFIG_DVB_DUMMY_FE) += dvb_dummy_fe.o | 52 | obj-$(CONFIG_DVB_DUMMY_FE) += dvb_dummy_fe.o |
53 | obj-$(CONFIG_DVB_AF9013) += af9013.o | 53 | obj-$(CONFIG_DVB_AF9013) += af9013.o |
54 | obj-$(CONFIG_DVB_CX24116) += cx24116.o | ||
diff --git a/drivers/media/dvb/frontends/cx24116.c b/drivers/media/dvb/frontends/cx24116.c new file mode 100644 index 000000000000..1402086a5c44 --- /dev/null +++ b/drivers/media/dvb/frontends/cx24116.c | |||
@@ -0,0 +1,950 @@ | |||
1 | /* | ||
2 | Conexant cx24116/cx24118 - DVBS/S2 Satellite demod/tuner driver | ||
3 | |||
4 | Copyright (C) 2006-2008 Steven Toth <stoth@hauppauge.com> | ||
5 | |||
6 | This program is free software; you can redistribute it and/or modify | ||
7 | it under the terms of the GNU General Public License as published by | ||
8 | the Free Software Foundation; either version 2 of the License, or | ||
9 | (at your option) any later version. | ||
10 | |||
11 | This program is distributed in the hope that it will be useful, | ||
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | GNU General Public License for more details. | ||
15 | |||
16 | You should have received a copy of the GNU General Public License | ||
17 | along with this program; if not, write to the Free Software | ||
18 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
19 | */ | ||
20 | |||
21 | /* | ||
22 | * Updates by Darron Broad 2007. | ||
23 | * | ||
24 | * March | ||
25 | * Fixed some bugs. | ||
26 | * Added diseqc support. | ||
27 | * Added corrected signal strength support. | ||
28 | * | ||
29 | * August | ||
30 | * Sync with legacy version. | ||
31 | * Some clean ups. | ||
32 | */ | ||
33 | |||
34 | #include <linux/slab.h> | ||
35 | #include <linux/kernel.h> | ||
36 | #include <linux/module.h> | ||
37 | #include <linux/moduleparam.h> | ||
38 | #include <linux/init.h> | ||
39 | #include <linux/firmware.h> | ||
40 | |||
41 | #include "dvb_frontend.h" | ||
42 | #include "cx24116.h" | ||
43 | |||
44 | /* | ||
45 | * Fetch firmware in the following manner. | ||
46 | * | ||
47 | * #!/bin/sh | ||
48 | * wget ftp://167.206.143.11/outgoing/Oxford/88x_2_117_24275_1_INF.zip | ||
49 | * unzip 88x_2_117_24275_1_INF.zip | ||
50 | * dd if=Driver88/hcw88bda.sys of=dvb-fe-cx24116.fw skip=81768 bs=1 count=32522 | ||
51 | */ | ||
52 | #define CX24116_DEFAULT_FIRMWARE "dvb-fe-cx24116.fw" | ||
53 | #define CX24116_SEARCH_RANGE_KHZ 5000 | ||
54 | |||
55 | /* registers (TO BE COMPLETED) */ | ||
56 | #define CX24116_REG_SIGNAL (0xd5) | ||
57 | |||
58 | /* arg buffer size */ | ||
59 | #define CX24116_ARGLEN (0x1e) | ||
60 | |||
61 | /* arg offset for DiSEqC */ | ||
62 | #define CX24116_DISEQC_BURST (1) | ||
63 | #define CX24116_DISEQC_ARG2_2 (2) /* unknown value=2 */ | ||
64 | #define CX24116_DISEQC_ARG3_0 (3) /* unknown value=0 */ | ||
65 | #define CX24116_DISEQC_ARG4_0 (4) /* unknown value=0 */ | ||
66 | #define CX24116_DISEQC_MSGLEN (5) | ||
67 | #define CX24116_DISEQC_MSGOFS (6) | ||
68 | |||
69 | /* DiSEqC burst */ | ||
70 | #define CX24116_DISEQC_MINI_A (0) | ||
71 | #define CX24116_DISEQC_MINI_B (1) | ||
72 | |||
73 | static int debug = 0; | ||
74 | #define dprintk(args...) \ | ||
75 | do { \ | ||
76 | if (debug) printk ("cx24116: " args); \ | ||
77 | } while (0) | ||
78 | |||
79 | enum cmds | ||
80 | { | ||
81 | CMD_INIT_CMD10 = 0x10, | ||
82 | CMD_TUNEREQUEST = 0x11, | ||
83 | CMD_INIT_CMD13 = 0x13, | ||
84 | CMD_INIT_CMD14 = 0x14, | ||
85 | CMD_SEND_DISEQC = 0x21, | ||
86 | CMD_SET_TONEPRE = 0x22, | ||
87 | CMD_SET_TONE = 0x23, | ||
88 | }; | ||
89 | |||
90 | /* The Demod/Tuner can't easily provide these, we cache them */ | ||
91 | struct cx24116_tuning | ||
92 | { | ||
93 | u32 frequency; | ||
94 | u32 symbol_rate; | ||
95 | fe_spectral_inversion_t inversion; | ||
96 | fe_code_rate_t fec; | ||
97 | |||
98 | fe_modulation_t modulation; | ||
99 | |||
100 | /* Demod values */ | ||
101 | u8 fec_val; | ||
102 | u8 fec_mask; | ||
103 | u8 inversion_val; | ||
104 | }; | ||
105 | |||
106 | /* Basic commands that are sent to the firmware */ | ||
107 | struct cx24116_cmd | ||
108 | { | ||
109 | u8 len; | ||
110 | u8 args[CX24116_ARGLEN]; | ||
111 | }; | ||
112 | |||
113 | struct cx24116_state | ||
114 | { | ||
115 | struct i2c_adapter* i2c; | ||
116 | const struct cx24116_config* config; | ||
117 | |||
118 | struct dvb_frontend frontend; | ||
119 | |||
120 | struct cx24116_tuning dcur; | ||
121 | struct cx24116_tuning dnxt; | ||
122 | |||
123 | u8 skip_fw_load; | ||
124 | u8 burst; | ||
125 | }; | ||
126 | |||
127 | static int cx24116_writereg(struct cx24116_state* state, int reg, int data) | ||
128 | { | ||
129 | u8 buf[] = { reg, data }; | ||
130 | struct i2c_msg msg = { .addr = state->config->demod_address, | ||
131 | .flags = 0, .buf = buf, .len = 2 }; | ||
132 | int err; | ||
133 | |||
134 | if (debug>1) | ||
135 | printk("cx24116: %s: write reg 0x%02x, value 0x%02x\n", | ||
136 | __func__,reg, data); | ||
137 | |||
138 | if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) { | ||
139 | printk("%s: writereg error(err == %i, reg == 0x%02x," | ||
140 | " value == 0x%02x)\n", __func__, err, reg, data); | ||
141 | return -EREMOTEIO; | ||
142 | } | ||
143 | |||
144 | return 0; | ||
145 | } | ||
146 | |||
147 | /* Bulk byte writes to a single I2C address, for 32k firmware load */ | ||
148 | static int cx24116_writeregN(struct cx24116_state* state, int reg, u8 *data, u16 len) | ||
149 | { | ||
150 | int ret = -EREMOTEIO; | ||
151 | struct i2c_msg msg; | ||
152 | u8 *buf; | ||
153 | |||
154 | buf = kmalloc(len + 1, GFP_KERNEL); | ||
155 | if (buf == NULL) { | ||
156 | printk("Unable to kmalloc\n"); | ||
157 | ret = -ENOMEM; | ||
158 | goto error; | ||
159 | } | ||
160 | |||
161 | *(buf) = reg; | ||
162 | memcpy(buf + 1, data, len); | ||
163 | |||
164 | msg.addr = state->config->demod_address; | ||
165 | msg.flags = 0; | ||
166 | msg.buf = buf; | ||
167 | msg.len = len + 1; | ||
168 | |||
169 | if (debug>1) | ||
170 | printk("cx24116: %s: write regN 0x%02x, len = %d\n", | ||
171 | __func__,reg, len); | ||
172 | |||
173 | if ((ret = i2c_transfer(state->i2c, &msg, 1)) != 1) { | ||
174 | printk("%s: writereg error(err == %i, reg == 0x%02x\n", | ||
175 | __func__, ret, reg); | ||
176 | ret = -EREMOTEIO; | ||
177 | } | ||
178 | |||
179 | error: | ||
180 | kfree(buf); | ||
181 | |||
182 | return ret; | ||
183 | } | ||
184 | |||
185 | static int cx24116_readreg(struct cx24116_state* state, u8 reg) | ||
186 | { | ||
187 | int ret; | ||
188 | u8 b0[] = { reg }; | ||
189 | u8 b1[] = { 0 }; | ||
190 | struct i2c_msg msg[] = { | ||
191 | { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, | ||
192 | { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } | ||
193 | }; | ||
194 | |||
195 | ret = i2c_transfer(state->i2c, msg, 2); | ||
196 | |||
197 | if (ret != 2) { | ||
198 | printk("%s: reg=0x%x (error=%d)\n", __func__, reg, ret); | ||
199 | return ret; | ||
200 | } | ||
201 | |||
202 | if (debug>1) | ||
203 | printk("cx24116: read reg 0x%02x, value 0x%02x\n",reg, b1[0]); | ||
204 | |||
205 | return b1[0]; | ||
206 | } | ||
207 | |||
208 | static int cx24116_set_inversion(struct cx24116_state* state, fe_spectral_inversion_t inversion) | ||
209 | { | ||
210 | dprintk("%s(%d)\n", __func__, inversion); | ||
211 | |||
212 | switch (inversion) { | ||
213 | case INVERSION_OFF: | ||
214 | state->dnxt.inversion_val = 0x00; | ||
215 | break; | ||
216 | case INVERSION_ON: | ||
217 | state->dnxt.inversion_val = 0x04; | ||
218 | break; | ||
219 | case INVERSION_AUTO: | ||
220 | state->dnxt.inversion_val = 0x0C; | ||
221 | break; | ||
222 | default: | ||
223 | return -EINVAL; | ||
224 | } | ||
225 | |||
226 | state->dnxt.inversion = inversion; | ||
227 | |||
228 | return 0; | ||
229 | } | ||
230 | |||
231 | /* A table of modulation, fec and configuration bytes for the demod. | ||
232 | * Not all S2 mmodulation schemes are support and not all rates with | ||
233 | * a scheme are support. Especially, no auto detect when in S2 mode. | ||
234 | */ | ||
235 | struct cx24116_modfec { | ||
236 | fe_modulation_t modulation; | ||
237 | fe_code_rate_t fec; | ||
238 | u8 mask; /* In DVBS mode this is used to autodetect */ | ||
239 | u8 val; /* Passed to the firmware to indicate mode selection */ | ||
240 | } CX24116_MODFEC_MODES[] = { | ||
241 | /* QPSK. For unknown rates we set hardware to auto detect 0xfe 0x30 */ | ||
242 | { QPSK, FEC_NONE, 0xfe, 0x30 }, | ||
243 | { QPSK, FEC_1_2, 0x02, 0x2e }, | ||
244 | { QPSK, FEC_2_3, 0x04, 0x2f }, | ||
245 | { QPSK, FEC_3_4, 0x08, 0x30 }, | ||
246 | { QPSK, FEC_4_5, 0xfe, 0x30 }, | ||
247 | { QPSK, FEC_5_6, 0x20, 0x31 }, | ||
248 | { QPSK, FEC_6_7, 0xfe, 0x30 }, | ||
249 | { QPSK, FEC_7_8, 0x80, 0x32 }, | ||
250 | { QPSK, FEC_8_9, 0xfe, 0x30 }, | ||
251 | { QPSK, FEC_AUTO, 0xfe, 0x30 }, | ||
252 | /* NBC-QPSK */ | ||
253 | { NBC_QPSK, FEC_1_2, 0x00, 0x04 }, | ||
254 | { NBC_QPSK, FEC_3_5, 0x00, 0x05 }, | ||
255 | { NBC_QPSK, FEC_2_3, 0x00, 0x06 }, | ||
256 | { NBC_QPSK, FEC_3_4, 0x00, 0x07 }, | ||
257 | { NBC_QPSK, FEC_4_5, 0x00, 0x08 }, | ||
258 | { NBC_QPSK, FEC_5_6, 0x00, 0x09 }, | ||
259 | { NBC_QPSK, FEC_8_9, 0x00, 0x0a }, | ||
260 | { NBC_QPSK, FEC_9_10, 0x00, 0x0b }, | ||
261 | /* 8PSK */ | ||
262 | { _8PSK, FEC_3_5, 0x00, 0x0c }, | ||
263 | { _8PSK, FEC_2_3, 0x00, 0x0d }, | ||
264 | { _8PSK, FEC_3_4, 0x00, 0x0e }, | ||
265 | { _8PSK, FEC_5_6, 0x00, 0x0f }, | ||
266 | { _8PSK, FEC_9_10, 0x00, 0x11 }, | ||
267 | }; | ||
268 | |||
269 | static int cx24116_lookup_fecmod(struct cx24116_state* state, | ||
270 | fe_modulation_t m, fe_code_rate_t f) | ||
271 | { | ||
272 | int i, ret = -EOPNOTSUPP; | ||
273 | |||
274 | for(i=0 ; i < sizeof(CX24116_MODFEC_MODES) / sizeof(struct cx24116_modfec) ; i++) | ||
275 | { | ||
276 | if( (m == CX24116_MODFEC_MODES[i].modulation) && | ||
277 | (f == CX24116_MODFEC_MODES[i].fec) ) | ||
278 | { | ||
279 | ret = i; | ||
280 | break; | ||
281 | } | ||
282 | } | ||
283 | |||
284 | return ret; | ||
285 | } | ||
286 | |||
287 | static int cx24116_set_fec(struct cx24116_state* state, fe_modulation_t mod, fe_code_rate_t fec) | ||
288 | { | ||
289 | int ret = 0; | ||
290 | dprintk("%s()\n", __func__); | ||
291 | |||
292 | ret = cx24116_lookup_fecmod(state, mod, fec); | ||
293 | |||
294 | if(ret < 0) | ||
295 | return ret; | ||
296 | |||
297 | state->dnxt.fec_val = CX24116_MODFEC_MODES[ret].val; | ||
298 | state->dnxt.fec_mask = CX24116_MODFEC_MODES[ret].mask; | ||
299 | dprintk("%s() fec_val/mask = 0x%02x/0x%02x\n", __func__, | ||
300 | state->dnxt.fec_val, state->dnxt.fec_mask); | ||
301 | |||
302 | return 0; | ||
303 | } | ||
304 | |||
305 | static int cx24116_set_symbolrate(struct cx24116_state* state, u32 rate) | ||
306 | { | ||
307 | int ret = 0; | ||
308 | |||
309 | dprintk("%s()\n", __func__); | ||
310 | |||
311 | state->dnxt.symbol_rate = rate; | ||
312 | |||
313 | dprintk("%s() symbol_rate = %d\n", __func__, state->dnxt.symbol_rate); | ||
314 | |||
315 | /* check if symbol rate is within limits */ | ||
316 | if ((state->dnxt.symbol_rate > state->frontend.ops.info.symbol_rate_max) || | ||
317 | (state->dnxt.symbol_rate < state->frontend.ops.info.symbol_rate_min)) | ||
318 | ret = -EOPNOTSUPP; | ||
319 | |||
320 | return ret; | ||
321 | } | ||
322 | |||
323 | static int cx24116_load_firmware (struct dvb_frontend* fe, const struct firmware *fw); | ||
324 | |||
325 | static int cx24116_firmware_ondemand(struct dvb_frontend* fe) | ||
326 | { | ||
327 | struct cx24116_state *state = fe->demodulator_priv; | ||
328 | const struct firmware *fw; | ||
329 | int ret = 0; | ||
330 | |||
331 | dprintk("%s()\n",__func__); | ||
332 | |||
333 | if (cx24116_readreg(state, 0x20) > 0) | ||
334 | { | ||
335 | |||
336 | if (state->skip_fw_load) | ||
337 | return 0; | ||
338 | |||
339 | /* Load firmware */ | ||
340 | /* request the firmware, this will block until someone uploads it */ | ||
341 | printk("%s: Waiting for firmware upload (%s)...\n", __func__, CX24116_DEFAULT_FIRMWARE); | ||
342 | ret = request_firmware(&fw, CX24116_DEFAULT_FIRMWARE, &state->i2c->dev); | ||
343 | printk("%s: Waiting for firmware upload(2)...\n", __func__); | ||
344 | if (ret) { | ||
345 | printk("%s: No firmware uploaded (timeout or file not found?)\n", __func__); | ||
346 | return ret; | ||
347 | } | ||
348 | |||
349 | /* Make sure we don't recurse back through here during loading */ | ||
350 | state->skip_fw_load = 1; | ||
351 | |||
352 | ret = cx24116_load_firmware(fe, fw); | ||
353 | if (ret) | ||
354 | printk("%s: Writing firmware to device failed\n", __func__); | ||
355 | |||
356 | release_firmware(fw); | ||
357 | |||
358 | printk("%s: Firmware upload %s\n", __func__, ret == 0 ? "complete" : "failed"); | ||
359 | |||
360 | /* Ensure firmware is always loaded if required */ | ||
361 | state->skip_fw_load = 0; | ||
362 | } | ||
363 | |||
364 | return ret; | ||
365 | } | ||
366 | |||
367 | /* Take a basic firmware command structure, format it and forward it for processing */ | ||
368 | static int cx24116_cmd_execute(struct dvb_frontend* fe, struct cx24116_cmd *cmd) | ||
369 | { | ||
370 | struct cx24116_state *state = fe->demodulator_priv; | ||
371 | int i, ret; | ||
372 | |||
373 | dprintk("%s()\n", __func__); | ||
374 | |||
375 | /* Load the firmware if required */ | ||
376 | if ( (ret = cx24116_firmware_ondemand(fe)) != 0) | ||
377 | { | ||
378 | printk("%s(): Unable initialise the firmware\n", __func__); | ||
379 | return ret; | ||
380 | } | ||
381 | |||
382 | /* Write the command */ | ||
383 | for(i = 0; i < cmd->len ; i++) | ||
384 | { | ||
385 | dprintk("%s: 0x%02x == 0x%02x\n", __func__, i, cmd->args[i]); | ||
386 | cx24116_writereg(state, i, cmd->args[i]); | ||
387 | } | ||
388 | |||
389 | /* Start execution and wait for cmd to terminate */ | ||
390 | cx24116_writereg(state, 0x1f, 0x01); | ||
391 | while( cx24116_readreg(state, 0x1f) ) | ||
392 | { | ||
393 | msleep(10); | ||
394 | if(i++ > 64) | ||
395 | { | ||
396 | /* Avoid looping forever if the firmware does no respond */ | ||
397 | printk("%s() Firmware not responding\n", __func__); | ||
398 | return -EREMOTEIO; | ||
399 | } | ||
400 | } | ||
401 | return 0; | ||
402 | } | ||
403 | |||
404 | static int cx24116_load_firmware (struct dvb_frontend* fe, const struct firmware *fw) | ||
405 | { | ||
406 | struct cx24116_state* state = fe->demodulator_priv; | ||
407 | struct cx24116_cmd cmd; | ||
408 | int ret; | ||
409 | |||
410 | dprintk("%s\n", __func__); | ||
411 | dprintk("Firmware is %zu bytes (%02x %02x .. %02x %02x)\n" | ||
412 | ,fw->size | ||
413 | ,fw->data[0] | ||
414 | ,fw->data[1] | ||
415 | ,fw->data[ fw->size-2 ] | ||
416 | ,fw->data[ fw->size-1 ] | ||
417 | ); | ||
418 | |||
419 | /* Toggle 88x SRST pin to reset demod */ | ||
420 | if (state->config->reset_device) | ||
421 | state->config->reset_device(fe); | ||
422 | |||
423 | /* Begin the firmware load process */ | ||
424 | /* Prepare the demod, load the firmware, cleanup after load */ | ||
425 | cx24116_writereg(state, 0xF1, 0x08); | ||
426 | cx24116_writereg(state, 0xF2, cx24116_readreg(state, 0xF2) | 0x03); | ||
427 | cx24116_writereg(state, 0xF3, 0x46); | ||
428 | cx24116_writereg(state, 0xF9, 0x00); | ||
429 | |||
430 | cx24116_writereg(state, 0xF0, 0x03); | ||
431 | cx24116_writereg(state, 0xF4, 0x81); | ||
432 | cx24116_writereg(state, 0xF5, 0x00); | ||
433 | cx24116_writereg(state, 0xF6, 0x00); | ||
434 | |||
435 | /* write the entire firmware as one transaction */ | ||
436 | cx24116_writeregN(state, 0xF7, fw->data, fw->size); | ||
437 | |||
438 | cx24116_writereg(state, 0xF4, 0x10); | ||
439 | cx24116_writereg(state, 0xF0, 0x00); | ||
440 | cx24116_writereg(state, 0xF8, 0x06); | ||
441 | |||
442 | /* Firmware CMD 10: Chip config? */ | ||
443 | cmd.args[0x00] = CMD_INIT_CMD10; | ||
444 | cmd.args[0x01] = 0x05; | ||
445 | cmd.args[0x02] = 0xdc; | ||
446 | cmd.args[0x03] = 0xda; | ||
447 | cmd.args[0x04] = 0xae; | ||
448 | cmd.args[0x05] = 0xaa; | ||
449 | cmd.args[0x06] = 0x04; | ||
450 | cmd.args[0x07] = 0x9d; | ||
451 | cmd.args[0x08] = 0xfc; | ||
452 | cmd.args[0x09] = 0x06; | ||
453 | cmd.len= 0x0a; | ||
454 | ret = cx24116_cmd_execute(fe, &cmd); | ||
455 | if (ret != 0) | ||
456 | return ret; | ||
457 | |||
458 | cx24116_writereg(state, 0x9d, 0x00); | ||
459 | |||
460 | /* Firmware CMD 14: Unknown */ | ||
461 | cmd.args[0x00] = CMD_INIT_CMD14; | ||
462 | cmd.args[0x01] = 0x00; | ||
463 | cmd.args[0x02] = 0x00; | ||
464 | cmd.len= 0x03; | ||
465 | ret = cx24116_cmd_execute(fe, &cmd); | ||
466 | if (ret != 0) | ||
467 | return ret; | ||
468 | |||
469 | cx24116_writereg(state, 0xe5, 0x00); | ||
470 | |||
471 | /* Firmware CMD 13: Unknown - Firmware config? */ | ||
472 | cmd.args[0x00] = CMD_INIT_CMD13; | ||
473 | cmd.args[0x01] = 0x01; | ||
474 | cmd.args[0x02] = 0x75; | ||
475 | cmd.args[0x03] = 0x00; | ||
476 | cmd.args[0x04] = 0x02; | ||
477 | cmd.args[0x05] = 0x00; | ||
478 | cmd.len= 0x06; | ||
479 | ret = cx24116_cmd_execute(fe, &cmd); | ||
480 | if (ret != 0) | ||
481 | return ret; | ||
482 | |||
483 | return 0; | ||
484 | } | ||
485 | |||
486 | static int cx24116_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) | ||
487 | { | ||
488 | /* The isl6421 module will override this function in the fops. */ | ||
489 | dprintk("%s() This should never appear if the isl6421 module is loaded correctly\n",__func__); | ||
490 | |||
491 | return -EOPNOTSUPP; | ||
492 | } | ||
493 | |||
494 | static int cx24116_read_status(struct dvb_frontend* fe, fe_status_t* status) | ||
495 | { | ||
496 | struct cx24116_state *state = fe->demodulator_priv; | ||
497 | |||
498 | int lock = cx24116_readreg(state, 0x9d); | ||
499 | |||
500 | dprintk("%s: status = 0x%02x\n", __func__, lock); | ||
501 | |||
502 | *status = 0; | ||
503 | |||
504 | if (lock & 0x01) | ||
505 | *status |= FE_HAS_SIGNAL; | ||
506 | if (lock & 0x02) | ||
507 | *status |= FE_HAS_CARRIER; | ||
508 | if (lock & 0x04) | ||
509 | *status |= FE_HAS_VITERBI; | ||
510 | if (lock & 0x08) | ||
511 | *status |= FE_HAS_SYNC | FE_HAS_LOCK; | ||
512 | |||
513 | return 0; | ||
514 | } | ||
515 | |||
516 | /* TODO: Not clear how we do this */ | ||
517 | static int cx24116_read_ber(struct dvb_frontend* fe, u32* ber) | ||
518 | { | ||
519 | //struct cx24116_state *state = fe->demodulator_priv; | ||
520 | dprintk("%s()\n", __func__); | ||
521 | *ber = 0; | ||
522 | |||
523 | return 0; | ||
524 | } | ||
525 | |||
526 | /* Signal strength (0..100)% = (sig & 0xf0) * 10 + (sig & 0x0f) * 10 / 16 */ | ||
527 | static int cx24116_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength) | ||
528 | { | ||
529 | struct cx24116_state *state = fe->demodulator_priv; | ||
530 | u8 strength_reg; | ||
531 | static const u32 strength_tab[] = { /* 10 x Table (rounded up) */ | ||
532 | 0x00000,0x0199A,0x03333,0x04ccD,0x06667,0x08000,0x0999A,0x0b333,0x0cccD,0x0e667, | ||
533 | 0x10000,0x1199A,0x13333,0x14ccD,0x16667,0x18000 }; | ||
534 | |||
535 | dprintk("%s()\n", __func__); | ||
536 | |||
537 | strength_reg = cx24116_readreg(state, CX24116_REG_SIGNAL); | ||
538 | |||
539 | if(strength_reg < 0xa0) | ||
540 | *signal_strength = strength_tab [ ( strength_reg & 0xf0 ) >> 4 ] + | ||
541 | ( strength_tab [ ( strength_reg & 0x0f ) ] >> 4 ); | ||
542 | else | ||
543 | *signal_strength = 0xffff; | ||
544 | |||
545 | dprintk("%s: Signal strength (raw / cooked) = (0x%02x / 0x%04x)\n", | ||
546 | __func__,strength_reg,*signal_strength); | ||
547 | |||
548 | return 0; | ||
549 | } | ||
550 | |||
551 | /* TODO: Not clear how we do this */ | ||
552 | static int cx24116_read_snr(struct dvb_frontend* fe, u16* snr) | ||
553 | { | ||
554 | //struct cx24116_state *state = fe->demodulator_priv; | ||
555 | dprintk("%s()\n", __func__); | ||
556 | *snr = 0; | ||
557 | |||
558 | return 0; | ||
559 | } | ||
560 | |||
561 | /* TODO: Not clear how we do this */ | ||
562 | static int cx24116_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) | ||
563 | { | ||
564 | //struct cx24116_state *state = fe->demodulator_priv; | ||
565 | dprintk("%s()\n", __func__); | ||
566 | *ucblocks = 0; | ||
567 | |||
568 | return 0; | ||
569 | } | ||
570 | |||
571 | /* Overwrite the current tuning params, we are about to tune */ | ||
572 | static void cx24116_clone_params(struct dvb_frontend* fe) | ||
573 | { | ||
574 | struct cx24116_state *state = fe->demodulator_priv; | ||
575 | memcpy(&state->dcur, &state->dnxt, sizeof(state->dcur)); | ||
576 | } | ||
577 | |||
578 | static int cx24116_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) | ||
579 | { | ||
580 | struct cx24116_cmd cmd; | ||
581 | int ret; | ||
582 | |||
583 | dprintk("%s(%d)\n", __func__, tone); | ||
584 | if ( (tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF) ) { | ||
585 | printk("%s: Invalid, tone=%d\n", __func__, tone); | ||
586 | return -EINVAL; | ||
587 | } | ||
588 | |||
589 | /* This is always done before the tone is set */ | ||
590 | cmd.args[0x00] = CMD_SET_TONEPRE; | ||
591 | cmd.args[0x01] = 0x00; | ||
592 | cmd.len= 0x02; | ||
593 | ret = cx24116_cmd_execute(fe, &cmd); | ||
594 | if (ret != 0) | ||
595 | return ret; | ||
596 | |||
597 | /* Now we set the tone */ | ||
598 | cmd.args[0x00] = CMD_SET_TONE; | ||
599 | cmd.args[0x01] = 0x00; | ||
600 | cmd.args[0x02] = 0x00; | ||
601 | |||
602 | switch (tone) { | ||
603 | case SEC_TONE_ON: | ||
604 | dprintk("%s: setting tone on\n", __func__); | ||
605 | cmd.args[0x03] = 0x01; | ||
606 | break; | ||
607 | case SEC_TONE_OFF: | ||
608 | dprintk("%s: setting tone off\n",__func__); | ||
609 | cmd.args[0x03] = 0x00; | ||
610 | break; | ||
611 | } | ||
612 | cmd.len= 0x04; | ||
613 | |||
614 | return cx24116_cmd_execute(fe, &cmd); | ||
615 | } | ||
616 | |||
617 | /* Initialise DiSEqC */ | ||
618 | static int cx24116_diseqc_init(struct dvb_frontend* fe) | ||
619 | { | ||
620 | struct cx24116_state *state = fe->demodulator_priv; | ||
621 | |||
622 | /* Default DiSEqC burst state */ | ||
623 | state->burst = CX24116_DISEQC_MINI_A; | ||
624 | |||
625 | return 0; | ||
626 | } | ||
627 | |||
628 | /* Send DiSEqC message with derived burst (hack) || previous burst */ | ||
629 | static int cx24116_send_diseqc_msg(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd *d) | ||
630 | { | ||
631 | struct cx24116_state *state = fe->demodulator_priv; | ||
632 | struct cx24116_cmd cmd; | ||
633 | int i, ret; | ||
634 | |||
635 | /* Dump DiSEqC message */ | ||
636 | if (debug) { | ||
637 | printk("cx24116: %s(", __func__); | ||
638 | for(i = 0 ; i < d->msg_len ;) { | ||
639 | printk("0x%02x", d->msg[i]); | ||
640 | if(++i < d->msg_len) | ||
641 | printk(", "); | ||
642 | } | ||
643 | printk(")\n"); | ||
644 | } | ||
645 | |||
646 | if(d->msg_len > (CX24116_ARGLEN - CX24116_DISEQC_MSGOFS)) | ||
647 | return -EINVAL; | ||
648 | |||
649 | cmd.args[0x00] = CMD_SEND_DISEQC; | ||
650 | cmd.args[CX24116_DISEQC_ARG2_2] = 0x02; | ||
651 | cmd.args[CX24116_DISEQC_ARG3_0] = 0x00; | ||
652 | cmd.args[CX24116_DISEQC_ARG4_0] = 0x00; | ||
653 | |||
654 | /* DiSEqC message */ | ||
655 | for (i = 0; i < d->msg_len; i++) | ||
656 | cmd.args[CX24116_DISEQC_MSGOFS + i] = d->msg[i]; | ||
657 | |||
658 | /* Hack: Derive burst from command else use previous burst */ | ||
659 | if(d->msg_len >= 4 && d->msg[2] == 0x38) | ||
660 | cmd.args[CX24116_DISEQC_BURST] = (d->msg[3] >> 2) & 1; | ||
661 | else | ||
662 | cmd.args[CX24116_DISEQC_BURST] = state->burst; | ||
663 | |||
664 | cmd.args[CX24116_DISEQC_MSGLEN] = d->msg_len; | ||
665 | cmd.len = CX24116_DISEQC_MSGOFS + d->msg_len; | ||
666 | |||
667 | ret = cx24116_cmd_execute(fe, &cmd); | ||
668 | |||
669 | /* Firmware command duration is unknown, so guess... | ||
670 | * | ||
671 | * Eutelsat spec: | ||
672 | * >15ms delay + | ||
673 | * 13.5ms per byte + | ||
674 | * >15ms delay + | ||
675 | * 12.5ms burst + | ||
676 | * >15ms delay | ||
677 | */ | ||
678 | if(ret == 0) | ||
679 | msleep( (cmd.args[CX24116_DISEQC_MSGLEN] << 4) + 60 ); | ||
680 | |||
681 | return ret; | ||
682 | } | ||
683 | |||
684 | /* Send DiSEqC burst */ | ||
685 | static int cx24116_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t burst) | ||
686 | { | ||
687 | struct cx24116_state *state = fe->demodulator_priv; | ||
688 | struct cx24116_cmd cmd; | ||
689 | int ret; | ||
690 | |||
691 | dprintk("%s(%d)\n",__func__,(int)burst); | ||
692 | |||
693 | cmd.args[0x00] = CMD_SEND_DISEQC; | ||
694 | cmd.args[CX24116_DISEQC_ARG2_2] = 0x02; | ||
695 | cmd.args[CX24116_DISEQC_ARG3_0] = 0x00; | ||
696 | cmd.args[CX24116_DISEQC_ARG4_0] = 0x00; | ||
697 | |||
698 | if (burst == SEC_MINI_A) | ||
699 | cmd.args[CX24116_DISEQC_BURST] = CX24116_DISEQC_MINI_A; | ||
700 | else if(burst == SEC_MINI_B) | ||
701 | cmd.args[CX24116_DISEQC_BURST] = CX24116_DISEQC_MINI_B; | ||
702 | else | ||
703 | return -EINVAL; | ||
704 | |||
705 | /* Cache as previous burst state */ | ||
706 | state->burst= cmd.args[CX24116_DISEQC_BURST]; | ||
707 | |||
708 | cmd.args[CX24116_DISEQC_MSGLEN] = 0x00; | ||
709 | cmd.len= CX24116_DISEQC_MSGOFS; | ||
710 | |||
711 | ret= cx24116_cmd_execute(fe, &cmd); | ||
712 | |||
713 | /* Firmware command duration is unknown, so guess... */ | ||
714 | if(ret == 0) | ||
715 | msleep(60); | ||
716 | |||
717 | return ret; | ||
718 | } | ||
719 | |||
720 | static void cx24116_release(struct dvb_frontend* fe) | ||
721 | { | ||
722 | struct cx24116_state* state = fe->demodulator_priv; | ||
723 | dprintk("%s\n",__func__); | ||
724 | kfree(state); | ||
725 | } | ||
726 | |||
727 | static struct dvb_frontend_ops cx24116_ops; | ||
728 | |||
729 | struct dvb_frontend* cx24116_attach(const struct cx24116_config* config, | ||
730 | struct i2c_adapter* i2c) | ||
731 | { | ||
732 | struct cx24116_state* state = NULL; | ||
733 | int ret; | ||
734 | |||
735 | dprintk("%s\n",__func__); | ||
736 | |||
737 | /* allocate memory for the internal state */ | ||
738 | state = kmalloc(sizeof(struct cx24116_state), GFP_KERNEL); | ||
739 | if (state == NULL) { | ||
740 | printk("Unable to kmalloc\n"); | ||
741 | goto error; | ||
742 | } | ||
743 | |||
744 | /* setup the state */ | ||
745 | memset(state, 0, sizeof(struct cx24116_state)); | ||
746 | |||
747 | state->config = config; | ||
748 | state->i2c = i2c; | ||
749 | |||
750 | /* check if the demod is present */ | ||
751 | ret = (cx24116_readreg(state, 0xFF) << 8) | cx24116_readreg(state, 0xFE); | ||
752 | if (ret != 0x0501) { | ||
753 | printk("Invalid probe, probably not a CX24116 device\n"); | ||
754 | goto error; | ||
755 | } | ||
756 | |||
757 | /* create dvb_frontend */ | ||
758 | memcpy(&state->frontend.ops, &cx24116_ops, sizeof(struct dvb_frontend_ops)); | ||
759 | state->frontend.demodulator_priv = state; | ||
760 | return &state->frontend; | ||
761 | |||
762 | error: | ||
763 | kfree(state); | ||
764 | |||
765 | return NULL; | ||
766 | } | ||
767 | |||
768 | static int cx24116_get_params(struct dvb_frontend* fe) | ||
769 | { | ||
770 | struct cx24116_state *state = fe->demodulator_priv; | ||
771 | struct tv_frontend_properties *cache = &fe->tv_property_cache; | ||
772 | |||
773 | dprintk("%s()\n",__func__); | ||
774 | |||
775 | cache->frequency = state->dcur.frequency; | ||
776 | cache->inversion = state->dcur.inversion; | ||
777 | cache->modulation = state->dcur.modulation; | ||
778 | cache->fec_inner = state->dcur.fec; | ||
779 | cache->symbol_rate = state->dcur.symbol_rate; | ||
780 | |||
781 | return 0; | ||
782 | } | ||
783 | |||
784 | static int cx24116_initfe(struct dvb_frontend* fe) | ||
785 | { | ||
786 | dprintk("%s()\n",__func__); | ||
787 | |||
788 | return cx24116_diseqc_init(fe); | ||
789 | } | ||
790 | |||
791 | static int cx24116_set_property(struct dvb_frontend *fe, tv_property_t* tvp) | ||
792 | { | ||
793 | dprintk("%s(..)\n", __func__); | ||
794 | return 0; | ||
795 | } | ||
796 | |||
797 | static int cx24116_set_params(struct dvb_frontend *fe) | ||
798 | { | ||
799 | dprintk("%s(..) We were notified that a tune request may occur\n", __func__); | ||
800 | return 0; | ||
801 | } | ||
802 | |||
803 | /* dvb-core told us to tune, the tv property cache will be complete, | ||
804 | * it's safe for is to pull values and use them for tuning purposes. | ||
805 | */ | ||
806 | static int cx24116_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) | ||
807 | { | ||
808 | struct cx24116_state *state = fe->demodulator_priv; | ||
809 | struct tv_frontend_properties *c = &fe->tv_property_cache; | ||
810 | struct cx24116_cmd cmd; | ||
811 | fe_status_t tunerstat; | ||
812 | int ret; | ||
813 | u8 retune=4; | ||
814 | |||
815 | dprintk("%s()\n",__func__); | ||
816 | |||
817 | state->dnxt.modulation = c->modulation; | ||
818 | state->dnxt.frequency = c->frequency; | ||
819 | |||
820 | if ((ret = cx24116_set_inversion(state, c->inversion)) != 0) | ||
821 | return ret; | ||
822 | |||
823 | if ((ret = cx24116_set_fec(state, c->modulation, c->fec_inner)) != 0) | ||
824 | return ret; | ||
825 | |||
826 | if ((ret = cx24116_set_symbolrate(state, c->symbol_rate)) != 0) | ||
827 | return ret; | ||
828 | |||
829 | /* discard the 'current' tuning parameters and prepare to tune */ | ||
830 | cx24116_clone_params(fe); | ||
831 | |||
832 | dprintk("%s: frequency = %d\n", __func__, state->dcur.frequency); | ||
833 | dprintk("%s: symbol_rate = %d\n", __func__, state->dcur.symbol_rate); | ||
834 | dprintk("%s: FEC = %d (mask/val = 0x%02x/0x%02x)\n", __func__, | ||
835 | state->dcur.fec, state->dcur.fec_mask, state->dcur.fec_val); | ||
836 | dprintk("%s: Inversion = %d (val = 0x%02x)\n", __func__, | ||
837 | state->dcur.inversion, state->dcur.inversion_val); | ||
838 | |||
839 | if (state->config->set_ts_params) | ||
840 | state->config->set_ts_params(fe, 0); | ||
841 | |||
842 | /* Prepare a tune request */ | ||
843 | cmd.args[0x00] = CMD_TUNEREQUEST; | ||
844 | |||
845 | /* Frequency */ | ||
846 | cmd.args[0x01] = (state->dcur.frequency & 0xff0000) >> 16; | ||
847 | cmd.args[0x02] = (state->dcur.frequency & 0x00ff00) >> 8; | ||
848 | cmd.args[0x03] = (state->dcur.frequency & 0x0000ff); | ||
849 | |||
850 | /* Symbol Rate */ | ||
851 | cmd.args[0x04] = ((state->dcur.symbol_rate / 1000) & 0xff00) >> 8; | ||
852 | cmd.args[0x05] = ((state->dcur.symbol_rate / 1000) & 0x00ff); | ||
853 | |||
854 | /* Automatic Inversion */ | ||
855 | cmd.args[0x06] = state->dcur.inversion_val; | ||
856 | |||
857 | /* Modulation / FEC & Pilot Off */ | ||
858 | cmd.args[0x07] = state->dcur.fec_val; | ||
859 | |||
860 | if (c->pilot == PILOT_ON) | ||
861 | cmd.args[0x07] |= 0x40; | ||
862 | |||
863 | cmd.args[0x08] = CX24116_SEARCH_RANGE_KHZ >> 8; | ||
864 | cmd.args[0x09] = CX24116_SEARCH_RANGE_KHZ & 0xff; | ||
865 | cmd.args[0x0a] = 0x00; | ||
866 | cmd.args[0x0b] = 0x00; | ||
867 | cmd.args[0x0c] = 0x02; | ||
868 | cmd.args[0x0d] = state->dcur.fec_mask; | ||
869 | cmd.args[0x0e] = 0x06; | ||
870 | cmd.args[0x0f] = 0x00; | ||
871 | cmd.args[0x10] = 0x00; | ||
872 | cmd.args[0x11] = 0xFA; | ||
873 | cmd.args[0x12] = 0x24; | ||
874 | cmd.len= 0x13; | ||
875 | |||
876 | /* We need to support pilot and non-pilot tuning in the | ||
877 | * driver automatically. This is a workaround for because | ||
878 | * the demod does not support autodetect. | ||
879 | */ | ||
880 | do { | ||
881 | /* Reset status register? */ | ||
882 | cx24116_writereg(state, 0x9d, 0xc1); | ||
883 | |||
884 | /* Tune */ | ||
885 | ret = cx24116_cmd_execute(fe, &cmd); | ||
886 | if( ret != 0 ) | ||
887 | break; | ||
888 | |||
889 | /* The hardware can take time to lock, wait a while */ | ||
890 | msleep(500); | ||
891 | |||
892 | cx24116_read_status(fe, &tunerstat); | ||
893 | if(tunerstat & FE_HAS_SIGNAL) { | ||
894 | if(tunerstat & FE_HAS_SYNC) | ||
895 | /* Tuned */ | ||
896 | break; | ||
897 | else if(c->pilot == PILOT_AUTO) | ||
898 | /* Toggle pilot bit */ | ||
899 | cmd.args[0x07] ^= 0x40; | ||
900 | } | ||
901 | } | ||
902 | while(--retune); | ||
903 | |||
904 | return ret; | ||
905 | } | ||
906 | |||
907 | static struct dvb_frontend_ops cx24116_ops = { | ||
908 | |||
909 | .info = { | ||
910 | .name = "Conexant CX24116/CX24118", | ||
911 | .type = FE_QPSK, | ||
912 | .frequency_min = 950000, | ||
913 | .frequency_max = 2150000, | ||
914 | .frequency_stepsize = 1011, /* kHz for QPSK frontends */ | ||
915 | .frequency_tolerance = 5000, | ||
916 | .symbol_rate_min = 1000000, | ||
917 | .symbol_rate_max = 45000000, | ||
918 | .caps = FE_CAN_INVERSION_AUTO | | ||
919 | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | | ||
920 | FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | | ||
921 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | | ||
922 | FE_CAN_QPSK | FE_CAN_RECOVER | ||
923 | }, | ||
924 | |||
925 | .release = cx24116_release, | ||
926 | |||
927 | .init = cx24116_initfe, | ||
928 | .read_status = cx24116_read_status, | ||
929 | .read_ber = cx24116_read_ber, | ||
930 | .read_signal_strength = cx24116_read_signal_strength, | ||
931 | .read_snr = cx24116_read_snr, | ||
932 | .read_ucblocks = cx24116_read_ucblocks, | ||
933 | .set_tone = cx24116_set_tone, | ||
934 | .set_voltage = cx24116_set_voltage, | ||
935 | .diseqc_send_master_cmd = cx24116_send_diseqc_msg, | ||
936 | .diseqc_send_burst = cx24116_diseqc_send_burst, | ||
937 | |||
938 | .set_property = cx24116_set_property, | ||
939 | .set_params = cx24116_set_params, | ||
940 | .set_frontend = cx24116_set_frontend, | ||
941 | }; | ||
942 | |||
943 | module_param(debug, int, 0644); | ||
944 | MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); | ||
945 | |||
946 | MODULE_DESCRIPTION("DVB Frontend module for Conexant cx24116/cx24118 hardware"); | ||
947 | MODULE_AUTHOR("Steven Toth"); | ||
948 | MODULE_LICENSE("GPL"); | ||
949 | |||
950 | EXPORT_SYMBOL(cx24116_attach); | ||
diff --git a/drivers/media/dvb/frontends/cx24116.h b/drivers/media/dvb/frontends/cx24116.h new file mode 100644 index 000000000000..27896725204a --- /dev/null +++ b/drivers/media/dvb/frontends/cx24116.h | |||
@@ -0,0 +1,50 @@ | |||
1 | /* | ||
2 | Conexant cx24116/cx24118 - DVBS/S2 Satellite demod/tuner driver | ||
3 | |||
4 | Copyright (C) 2006 Steven Toth <stoth@linuxtv.com> | ||
5 | |||
6 | This program is free software; you can redistribute it and/or modify | ||
7 | it under the terms of the GNU General Public License as published by | ||
8 | the Free Software Foundation; either version 2 of the License, or | ||
9 | (at your option) any later version. | ||
10 | |||
11 | This program is distributed in the hope that it will be useful, | ||
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | GNU General Public License for more details. | ||
15 | |||
16 | You should have received a copy of the GNU General Public License | ||
17 | along with this program; if not, write to the Free Software | ||
18 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
19 | */ | ||
20 | |||
21 | #ifndef CX24116_H | ||
22 | #define CX24116_H | ||
23 | |||
24 | #include <linux/dvb/frontend.h> | ||
25 | |||
26 | struct cx24116_config | ||
27 | { | ||
28 | /* the demodulator's i2c address */ | ||
29 | u8 demod_address; | ||
30 | |||
31 | /* Need to set device param for start_dma */ | ||
32 | int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured); | ||
33 | |||
34 | /* Need to reset device during firmware loading */ | ||
35 | int (*reset_device)(struct dvb_frontend* fe); | ||
36 | }; | ||
37 | |||
38 | #if defined(CONFIG_DVB_CX24116) || defined(CONFIG_DVB_CX24116_MODULE) | ||
39 | extern struct dvb_frontend* cx24116_attach(const struct cx24116_config* config, | ||
40 | struct i2c_adapter* i2c); | ||
41 | #else | ||
42 | static inline struct dvb_frontend* cx24116_attach(const struct cx24116_config* config, | ||
43 | struct i2c_adapter* i2c) | ||
44 | { | ||
45 | printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); | ||
46 | return NULL; | ||
47 | } | ||
48 | #endif // CONFIG_DVB_CX24116 | ||
49 | |||
50 | #endif /* CX24116_H */ | ||