diff options
author | Igor M. Liplianin <liplianin@me.by> | 2008-09-16 17:21:11 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2008-10-12 07:37:06 -0400 |
commit | 04ad28c9916da709f38b1d43817892142c2c3508 (patch) | |
tree | 1ae338bce55ef7322dd96237a601f322639e4dac /drivers/media/dvb | |
parent | 34c080295af9b3ed9f704a881e07eb5ac128e1ed (diff) |
V4L/DVB (9017): Add support for Silicon Laboratories SI2109/2110 demodulators.
Add support for Silicon Laboratories SI2109/2110 demodulator
and cards with it, such as DvbWorld PCI2002.
Signed-off-by: Igor M. Liplianin <liplianin@me.by>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/dvb')
-rw-r--r-- | drivers/media/dvb/dm1105/Kconfig | 1 | ||||
-rw-r--r-- | drivers/media/dvb/dm1105/dm1105.c | 7 | ||||
-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/si21xx.c | 974 | ||||
-rw-r--r-- | drivers/media/dvb/frontends/si21xx.h | 37 |
6 files changed, 1023 insertions, 4 deletions
diff --git a/drivers/media/dvb/dm1105/Kconfig b/drivers/media/dvb/dm1105/Kconfig index 4b96bbba9f42..2af5fffe138e 100644 --- a/drivers/media/dvb/dm1105/Kconfig +++ b/drivers/media/dvb/dm1105/Kconfig | |||
@@ -4,6 +4,7 @@ config DVB_DM1105 | |||
4 | select DVB_PLL if !DVB_FE_CUSTOMISE | 4 | select DVB_PLL if !DVB_FE_CUSTOMISE |
5 | select DVB_STV0299 if !DVB_FE_CUSTOMISE | 5 | select DVB_STV0299 if !DVB_FE_CUSTOMISE |
6 | select DVB_CX24116 if !DVB_FE_CUSTOMISE | 6 | select DVB_CX24116 if !DVB_FE_CUSTOMISE |
7 | select DVB_SI21XX if !DVB_FE_CUSTOMISE | ||
7 | help | 8 | help |
8 | Support for cards based on the SDMC DM1105 PCI chip like | 9 | Support for cards based on the SDMC DM1105 PCI chip like |
9 | DvbWorld 2002 | 10 | DvbWorld 2002 |
diff --git a/drivers/media/dvb/dm1105/dm1105.c b/drivers/media/dvb/dm1105/dm1105.c index 204763d2de41..0c1790fe2b56 100644 --- a/drivers/media/dvb/dm1105/dm1105.c +++ b/drivers/media/dvb/dm1105/dm1105.c | |||
@@ -40,8 +40,8 @@ | |||
40 | 40 | ||
41 | #include "stv0299.h" | 41 | #include "stv0299.h" |
42 | /*#include "stv0288.h" | 42 | /*#include "stv0288.h" |
43 | *#include "si21xx.h" | ||
44 | *#include "stb6000.h"*/ | 43 | *#include "stb6000.h"*/ |
44 | #include "si21xx.h" | ||
45 | #include "cx24116.h" | 45 | #include "cx24116.h" |
46 | #include "z0194a.h" | 46 | #include "z0194a.h" |
47 | 47 | ||
@@ -600,12 +600,12 @@ static struct stv0288_config earda_config = { | |||
600 | .min_delay_ms = 100, | 600 | .min_delay_ms = 100, |
601 | }; | 601 | }; |
602 | 602 | ||
603 | #endif /* keep */ | ||
603 | static struct si21xx_config serit_config = { | 604 | static struct si21xx_config serit_config = { |
604 | .demod_address = 0x68, | 605 | .demod_address = 0x68, |
605 | .min_delay_ms = 100, | 606 | .min_delay_ms = 100, |
606 | 607 | ||
607 | }; | 608 | }; |
608 | #endif /* keep */ | ||
609 | 609 | ||
610 | static struct cx24116_config serit_sp2633_config = { | 610 | static struct cx24116_config serit_sp2633_config = { |
611 | .demod_address = 0x55, | 611 | .demod_address = 0x55, |
@@ -639,7 +639,7 @@ static int __devinit frontend_init(struct dm1105dvb *dm1105dvb) | |||
639 | &dm1105dvb->i2c_adap); | 639 | &dm1105dvb->i2c_adap); |
640 | } | 640 | } |
641 | } | 641 | } |
642 | 642 | #endif /* keep */ | |
643 | if (!dm1105dvb->fe) { | 643 | if (!dm1105dvb->fe) { |
644 | dm1105dvb->fe = dvb_attach( | 644 | dm1105dvb->fe = dvb_attach( |
645 | si21xx_attach, &serit_config, | 645 | si21xx_attach, &serit_config, |
@@ -648,7 +648,6 @@ static int __devinit frontend_init(struct dm1105dvb *dm1105dvb) | |||
648 | dm1105dvb->fe->ops.set_voltage = | 648 | dm1105dvb->fe->ops.set_voltage = |
649 | dm1105dvb_set_voltage; | 649 | dm1105dvb_set_voltage; |
650 | } | 650 | } |
651 | #endif /* keep */ | ||
652 | break; | 651 | break; |
653 | case PCI_DEVICE_ID_DW2004: | 652 | case PCI_DEVICE_ID_DW2004: |
654 | dm1105dvb->fe = dvb_attach( | 653 | dm1105dvb->fe = dvb_attach( |
diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig index d2f31da26cbc..7697391ca86c 100644 --- a/drivers/media/dvb/frontends/Kconfig +++ b/drivers/media/dvb/frontends/Kconfig | |||
@@ -99,6 +99,13 @@ config DVB_CX24116 | |||
99 | help | 99 | help |
100 | A DVB-S/S2 tuner module. Say Y when you want to support this frontend. | 100 | A DVB-S/S2 tuner module. Say Y when you want to support this frontend. |
101 | 101 | ||
102 | config DVB_SI21XX | ||
103 | tristate "Silicon Labs SI21XX based" | ||
104 | depends on DVB_CORE && I2C | ||
105 | default m if DVB_FE_CUSTOMISE | ||
106 | help | ||
107 | A DVB-S tuner module. Say Y when you want to support this frontend. | ||
108 | |||
102 | comment "DVB-T (terrestrial) frontends" | 109 | comment "DVB-T (terrestrial) frontends" |
103 | depends on DVB_CORE | 110 | depends on DVB_CORE |
104 | 111 | ||
diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile index 5deb8a542abd..c719b7fdce4a 100644 --- a/drivers/media/dvb/frontends/Makefile +++ b/drivers/media/dvb/frontends/Makefile | |||
@@ -52,3 +52,4 @@ 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 | 54 | obj-$(CONFIG_DVB_CX24116) += cx24116.o |
55 | obj-$(CONFIG_DVB_SI21XX) += si21xx.o | ||
diff --git a/drivers/media/dvb/frontends/si21xx.c b/drivers/media/dvb/frontends/si21xx.c new file mode 100644 index 000000000000..3ddbe69c45ce --- /dev/null +++ b/drivers/media/dvb/frontends/si21xx.c | |||
@@ -0,0 +1,974 @@ | |||
1 | /* DVB compliant Linux driver for the DVB-S si2109/2110 demodulator | ||
2 | * | ||
3 | * Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by) | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | */ | ||
11 | #include <linux/version.h> | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/string.h> | ||
16 | #include <linux/slab.h> | ||
17 | #include <linux/jiffies.h> | ||
18 | #include <asm/div64.h> | ||
19 | |||
20 | #include "dvb_frontend.h" | ||
21 | #include "si21xx.h" | ||
22 | |||
23 | #define REVISION_REG 0x00 | ||
24 | #define SYSTEM_MODE_REG 0x01 | ||
25 | #define TS_CTRL_REG_1 0x02 | ||
26 | #define TS_CTRL_REG_2 0x03 | ||
27 | #define PIN_CTRL_REG_1 0x04 | ||
28 | #define PIN_CTRL_REG_2 0x05 | ||
29 | #define LOCK_STATUS_REG_1 0x0f | ||
30 | #define LOCK_STATUS_REG_2 0x10 | ||
31 | #define ACQ_STATUS_REG 0x11 | ||
32 | #define ACQ_CTRL_REG_1 0x13 | ||
33 | #define ACQ_CTRL_REG_2 0x14 | ||
34 | #define PLL_DIVISOR_REG 0x15 | ||
35 | #define COARSE_TUNE_REG 0x16 | ||
36 | #define FINE_TUNE_REG_L 0x17 | ||
37 | #define FINE_TUNE_REG_H 0x18 | ||
38 | |||
39 | #define ANALOG_AGC_POWER_LEVEL_REG 0x28 | ||
40 | #define CFO_ESTIMATOR_CTRL_REG_1 0x29 | ||
41 | #define CFO_ESTIMATOR_CTRL_REG_2 0x2a | ||
42 | #define CFO_ESTIMATOR_CTRL_REG_3 0x2b | ||
43 | |||
44 | #define SYM_RATE_ESTIMATE_REG_L 0x31 | ||
45 | #define SYM_RATE_ESTIMATE_REG_M 0x32 | ||
46 | #define SYM_RATE_ESTIMATE_REG_H 0x33 | ||
47 | |||
48 | #define CFO_ESTIMATOR_OFFSET_REG_L 0x36 | ||
49 | #define CFO_ESTIMATOR_OFFSET_REG_H 0x37 | ||
50 | #define CFO_ERROR_REG_L 0x38 | ||
51 | #define CFO_ERROR_REG_H 0x39 | ||
52 | #define SYM_RATE_ESTIMATOR_CTRL_REG 0x3a | ||
53 | |||
54 | #define SYM_RATE_REG_L 0x3f | ||
55 | #define SYM_RATE_REG_M 0x40 | ||
56 | #define SYM_RATE_REG_H 0x41 | ||
57 | #define SYM_RATE_ESTIMATOR_MAXIMUM_REG 0x42 | ||
58 | #define SYM_RATE_ESTIMATOR_MINIMUM_REG 0x43 | ||
59 | |||
60 | #define C_N_ESTIMATOR_CTRL_REG 0x7c | ||
61 | #define C_N_ESTIMATOR_THRSHLD_REG 0x7d | ||
62 | #define C_N_ESTIMATOR_LEVEL_REG_L 0x7e | ||
63 | #define C_N_ESTIMATOR_LEVEL_REG_H 0x7f | ||
64 | |||
65 | #define BLIND_SCAN_CTRL_REG 0x80 | ||
66 | |||
67 | #define LSA_CTRL_REG_1 0x8D | ||
68 | #define SPCTRM_TILT_CORR_THRSHLD_REG 0x8f | ||
69 | #define ONE_DB_BNDWDTH_THRSHLD_REG 0x90 | ||
70 | #define TWO_DB_BNDWDTH_THRSHLD_REG 0x91 | ||
71 | #define THREE_DB_BNDWDTH_THRSHLD_REG 0x92 | ||
72 | #define INBAND_POWER_THRSHLD_REG 0x93 | ||
73 | #define REF_NOISE_LVL_MRGN_THRSHLD_REG 0x94 | ||
74 | |||
75 | #define VIT_SRCH_CTRL_REG_1 0xa0 | ||
76 | #define VIT_SRCH_CTRL_REG_2 0xa1 | ||
77 | #define VIT_SRCH_CTRL_REG_3 0xa2 | ||
78 | #define VIT_SRCH_STATUS_REG 0xa3 | ||
79 | #define VITERBI_BER_COUNT_REG_L 0xab | ||
80 | #define REED_SOLOMON_CTRL_REG 0xb0 | ||
81 | #define REED_SOLOMON_ERROR_COUNT_REG_L 0xb1 | ||
82 | #define PRBS_CTRL_REG 0xb5 | ||
83 | |||
84 | #define LNB_CTRL_REG_1 0xc0 | ||
85 | #define LNB_CTRL_REG_2 0xc1 | ||
86 | #define LNB_CTRL_REG_3 0xc2 | ||
87 | #define LNB_CTRL_REG_4 0xc3 | ||
88 | #define LNB_CTRL_STATUS_REG 0xc4 | ||
89 | #define LNB_FIFO_REGS_0 0xc5 | ||
90 | #define LNB_FIFO_REGS_1 0xc6 | ||
91 | #define LNB_FIFO_REGS_2 0xc7 | ||
92 | #define LNB_FIFO_REGS_3 0xc8 | ||
93 | #define LNB_FIFO_REGS_4 0xc9 | ||
94 | #define LNB_FIFO_REGS_5 0xca | ||
95 | #define LNB_SUPPLY_CTRL_REG_1 0xcb | ||
96 | #define LNB_SUPPLY_CTRL_REG_2 0xcc | ||
97 | #define LNB_SUPPLY_CTRL_REG_3 0xcd | ||
98 | #define LNB_SUPPLY_CTRL_REG_4 0xce | ||
99 | #define LNB_SUPPLY_STATUS_REG 0xcf | ||
100 | |||
101 | #define FALSE 0 | ||
102 | #define TRUE 1 | ||
103 | #define FAIL -1 | ||
104 | #define PASS 0 | ||
105 | |||
106 | #define ALLOWABLE_FS_COUNT 10 | ||
107 | #define STATUS_BER 0 | ||
108 | #define STATUS_UCBLOCKS 1 | ||
109 | |||
110 | static int debug; | ||
111 | #define dprintk(args...) \ | ||
112 | do { \ | ||
113 | if (debug) \ | ||
114 | printk(KERN_DEBUG "si21xx: " args); \ | ||
115 | } while (0) | ||
116 | |||
117 | enum { | ||
118 | ACTIVE_HIGH, | ||
119 | ACTIVE_LOW | ||
120 | }; | ||
121 | enum { | ||
122 | BYTE_WIDE, | ||
123 | BIT_WIDE | ||
124 | }; | ||
125 | enum { | ||
126 | CLK_GAPPED_MODE, | ||
127 | CLK_CONTINUOUS_MODE | ||
128 | }; | ||
129 | enum { | ||
130 | RISING_EDGE, | ||
131 | FALLING_EDGE | ||
132 | }; | ||
133 | enum { | ||
134 | MSB_FIRST, | ||
135 | LSB_FIRST | ||
136 | }; | ||
137 | enum { | ||
138 | SERIAL, | ||
139 | PARALLEL | ||
140 | }; | ||
141 | |||
142 | struct si21xx_state { | ||
143 | struct i2c_adapter *i2c; | ||
144 | const struct si21xx_config *config; | ||
145 | struct dvb_frontend frontend; | ||
146 | u8 initialised:1; | ||
147 | int errmode; | ||
148 | int fs; /*Sampling rate of the ADC in MHz*/ | ||
149 | }; | ||
150 | |||
151 | /* register default initialization */ | ||
152 | static u8 serit_sp1511lhb_inittab[] = { | ||
153 | 0x01, 0x28, /* set i2c_inc_disable */ | ||
154 | 0x20, 0x03, | ||
155 | 0x27, 0x20, | ||
156 | 0xe0, 0x45, | ||
157 | 0xe1, 0x08, | ||
158 | 0xfe, 0x01, | ||
159 | 0x01, 0x28, | ||
160 | 0x89, 0x09, | ||
161 | 0x04, 0x80, | ||
162 | 0x05, 0x01, | ||
163 | 0x06, 0x00, | ||
164 | 0x20, 0x03, | ||
165 | 0x24, 0x88, | ||
166 | 0x29, 0x09, | ||
167 | 0x2a, 0x0f, | ||
168 | 0x2c, 0x10, | ||
169 | 0x2d, 0x19, | ||
170 | 0x2e, 0x08, | ||
171 | 0x2f, 0x10, | ||
172 | 0x30, 0x19, | ||
173 | 0x34, 0x20, | ||
174 | 0x35, 0x03, | ||
175 | 0x45, 0x02, | ||
176 | 0x46, 0x45, | ||
177 | 0x47, 0xd0, | ||
178 | 0x48, 0x00, | ||
179 | 0x49, 0x40, | ||
180 | 0x4a, 0x03, | ||
181 | 0x4c, 0xfd, | ||
182 | 0x4f, 0x2e, | ||
183 | 0x50, 0x2e, | ||
184 | 0x51, 0x10, | ||
185 | 0x52, 0x10, | ||
186 | 0x56, 0x92, | ||
187 | 0x59, 0x00, | ||
188 | 0x5a, 0x2d, | ||
189 | 0x5b, 0x33, | ||
190 | 0x5c, 0x1f, | ||
191 | 0x5f, 0x76, | ||
192 | 0x62, 0xc0, | ||
193 | 0x63, 0xc0, | ||
194 | 0x64, 0xf3, | ||
195 | 0x65, 0xf3, | ||
196 | 0x79, 0x40, | ||
197 | 0x6a, 0x40, | ||
198 | 0x6b, 0x0a, | ||
199 | 0x6c, 0x80, | ||
200 | 0x6d, 0x27, | ||
201 | 0x71, 0x06, | ||
202 | 0x75, 0x60, | ||
203 | 0x78, 0x00, | ||
204 | 0x79, 0xb5, | ||
205 | 0x7c, 0x05, | ||
206 | 0x7d, 0x1a, | ||
207 | 0x87, 0x55, | ||
208 | 0x88, 0x72, | ||
209 | 0x8f, 0x08, | ||
210 | 0x90, 0xe0, | ||
211 | 0x94, 0x40, | ||
212 | 0xa0, 0x3f, | ||
213 | 0xa1, 0xc0, | ||
214 | 0xa4, 0xcc, | ||
215 | 0xa5, 0x66, | ||
216 | 0xa6, 0x66, | ||
217 | 0xa7, 0x7b, | ||
218 | 0xa8, 0x7b, | ||
219 | 0xa9, 0x7b, | ||
220 | 0xaa, 0x9a, | ||
221 | 0xed, 0x04, | ||
222 | 0xad, 0x00, | ||
223 | 0xae, 0x03, | ||
224 | 0xcc, 0xab, | ||
225 | 0x01, 0x08, | ||
226 | 0xff, 0xff | ||
227 | }; | ||
228 | |||
229 | /* low level read/writes */ | ||
230 | static int si21_writeregs(struct si21xx_state *state, u8 reg1, | ||
231 | u8 *data, int len) | ||
232 | { | ||
233 | int ret; | ||
234 | u8 buf[60];/* = { reg1, data };*/ | ||
235 | struct i2c_msg msg = { | ||
236 | .addr = state->config->demod_address, | ||
237 | .flags = 0, | ||
238 | .buf = buf, | ||
239 | .len = len + 1 | ||
240 | }; | ||
241 | |||
242 | msg.buf[0] = reg1; | ||
243 | memcpy(msg.buf + 1, data, len); | ||
244 | |||
245 | ret = i2c_transfer(state->i2c, &msg, 1); | ||
246 | |||
247 | if (ret != 1) | ||
248 | dprintk("%s: writereg error (reg1 == 0x%02x, data == 0x%02x, " | ||
249 | "ret == %i)\n", __func__, reg1, data[0], ret); | ||
250 | |||
251 | return (ret != 1) ? -EREMOTEIO : 0; | ||
252 | } | ||
253 | |||
254 | static int si21_writereg(struct si21xx_state *state, u8 reg, u8 data) | ||
255 | { | ||
256 | int ret; | ||
257 | u8 buf[] = { reg, data }; | ||
258 | struct i2c_msg msg = { | ||
259 | .addr = state->config->demod_address, | ||
260 | .flags = 0, | ||
261 | .buf = buf, | ||
262 | .len = 2 | ||
263 | }; | ||
264 | |||
265 | ret = i2c_transfer(state->i2c, &msg, 1); | ||
266 | |||
267 | if (ret != 1) | ||
268 | dprintk("%s: writereg error (reg == 0x%02x, data == 0x%02x, " | ||
269 | "ret == %i)\n", __func__, reg, data, ret); | ||
270 | |||
271 | return (ret != 1) ? -EREMOTEIO : 0; | ||
272 | } | ||
273 | |||
274 | static int si21_write(struct dvb_frontend *fe, u8 *buf, int len) | ||
275 | { | ||
276 | struct si21xx_state *state = fe->demodulator_priv; | ||
277 | |||
278 | if (len != 2) | ||
279 | return -EINVAL; | ||
280 | |||
281 | return si21_writereg(state, buf[0], buf[1]); | ||
282 | } | ||
283 | |||
284 | static u8 si21_readreg(struct si21xx_state *state, u8 reg) | ||
285 | { | ||
286 | int ret; | ||
287 | u8 b0[] = { reg }; | ||
288 | u8 b1[] = { 0 }; | ||
289 | struct i2c_msg msg[] = { | ||
290 | { | ||
291 | .addr = state->config->demod_address, | ||
292 | .flags = 0, | ||
293 | .buf = b0, | ||
294 | .len = 1 | ||
295 | }, { | ||
296 | .addr = state->config->demod_address, | ||
297 | .flags = I2C_M_RD, | ||
298 | .buf = b1, | ||
299 | .len = 1 | ||
300 | } | ||
301 | }; | ||
302 | |||
303 | ret = i2c_transfer(state->i2c, msg, 2); | ||
304 | |||
305 | if (ret != 2) | ||
306 | dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", | ||
307 | __func__, reg, ret); | ||
308 | |||
309 | return b1[0]; | ||
310 | } | ||
311 | |||
312 | static int si21_readregs(struct si21xx_state *state, u8 reg1, u8 *b, u8 len) | ||
313 | { | ||
314 | int ret; | ||
315 | struct i2c_msg msg[] = { | ||
316 | { | ||
317 | .addr = state->config->demod_address, | ||
318 | .flags = 0, | ||
319 | .buf = ®1, | ||
320 | .len = 1 | ||
321 | }, { | ||
322 | .addr = state->config->demod_address, | ||
323 | .flags = I2C_M_RD, | ||
324 | .buf = b, | ||
325 | .len = len | ||
326 | } | ||
327 | }; | ||
328 | |||
329 | ret = i2c_transfer(state->i2c, msg, 2); | ||
330 | |||
331 | if (ret != 2) | ||
332 | dprintk("%s: readreg error (ret == %i)\n", __func__, ret); | ||
333 | |||
334 | return ret == 2 ? 0 : -1; | ||
335 | } | ||
336 | |||
337 | static int si21xx_wait_diseqc_idle(struct si21xx_state *state, int timeout) | ||
338 | { | ||
339 | unsigned long start = jiffies; | ||
340 | |||
341 | dprintk("%s\n", __func__); | ||
342 | |||
343 | while ((si21_readreg(state, LNB_CTRL_REG_1) & 0x8) == 8) { | ||
344 | if (jiffies - start > timeout) { | ||
345 | dprintk("%s: timeout!!\n", __func__); | ||
346 | return -ETIMEDOUT; | ||
347 | } | ||
348 | msleep(10); | ||
349 | }; | ||
350 | |||
351 | return 0; | ||
352 | } | ||
353 | |||
354 | static int si21xx_set_symbolrate(struct dvb_frontend *fe, u32 srate) | ||
355 | { | ||
356 | struct si21xx_state *state = fe->demodulator_priv; | ||
357 | u32 sym_rate, data_rate; | ||
358 | int i; | ||
359 | u8 sym_rate_bytes[3]; | ||
360 | |||
361 | dprintk("%s : srate = %i\n", __func__ , srate); | ||
362 | |||
363 | if ((srate < 1000000) || (srate > 45000000)) | ||
364 | return -EINVAL; | ||
365 | |||
366 | data_rate = srate; | ||
367 | sym_rate = 0; | ||
368 | |||
369 | for (i = 0; i < 4; ++i) { | ||
370 | sym_rate /= 100; | ||
371 | sym_rate = sym_rate + ((data_rate % 100) * 0x800000) / | ||
372 | state->fs; | ||
373 | data_rate /= 100; | ||
374 | } | ||
375 | for (i = 0; i < 3; ++i) | ||
376 | sym_rate_bytes[i] = (u8)((sym_rate >> (i * 8)) & 0xff); | ||
377 | |||
378 | si21_writeregs(state, SYM_RATE_REG_L, sym_rate_bytes, 0x03); | ||
379 | |||
380 | return 0; | ||
381 | } | ||
382 | |||
383 | static int si21xx_send_diseqc_msg(struct dvb_frontend *fe, | ||
384 | struct dvb_diseqc_master_cmd *m) | ||
385 | { | ||
386 | struct si21xx_state *state = fe->demodulator_priv; | ||
387 | u8 lnb_status; | ||
388 | u8 LNB_CTRL_1; | ||
389 | int status; | ||
390 | |||
391 | dprintk("%s\n", __func__); | ||
392 | |||
393 | status = PASS; | ||
394 | LNB_CTRL_1 = 0; | ||
395 | |||
396 | status |= si21_readregs(state, LNB_CTRL_STATUS_REG, &lnb_status, 0x01); | ||
397 | status |= si21_readregs(state, LNB_CTRL_REG_1, &lnb_status, 0x01); | ||
398 | |||
399 | /*fill the FIFO*/ | ||
400 | status |= si21_writeregs(state, LNB_FIFO_REGS_0, m->msg, m->msg_len); | ||
401 | |||
402 | LNB_CTRL_1 = (lnb_status & 0x70); | ||
403 | LNB_CTRL_1 |= m->msg_len; | ||
404 | |||
405 | LNB_CTRL_1 |= 0x80; /* begin LNB signaling */ | ||
406 | |||
407 | status |= si21_writeregs(state, LNB_CTRL_REG_1, &LNB_CTRL_1, 0x01); | ||
408 | |||
409 | return status; | ||
410 | } | ||
411 | |||
412 | static int si21xx_send_diseqc_burst(struct dvb_frontend *fe, | ||
413 | fe_sec_mini_cmd_t burst) | ||
414 | { | ||
415 | struct si21xx_state *state = fe->demodulator_priv; | ||
416 | u8 val; | ||
417 | |||
418 | dprintk("%s\n", __func__); | ||
419 | |||
420 | if (si21xx_wait_diseqc_idle(state, 100) < 0) | ||
421 | return -ETIMEDOUT; | ||
422 | |||
423 | val = (0x80 | si21_readreg(state, 0xc1)); | ||
424 | if (si21_writereg(state, LNB_CTRL_REG_1, | ||
425 | burst == SEC_MINI_A ? (val & ~0x10) : (val | 0x10))) | ||
426 | return -EREMOTEIO; | ||
427 | |||
428 | if (si21xx_wait_diseqc_idle(state, 100) < 0) | ||
429 | return -ETIMEDOUT; | ||
430 | |||
431 | if (si21_writereg(state, LNB_CTRL_REG_1, val)) | ||
432 | return -EREMOTEIO; | ||
433 | |||
434 | return 0; | ||
435 | } | ||
436 | /* 30.06.2008 */ | ||
437 | static int si21xx_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) | ||
438 | { | ||
439 | struct si21xx_state *state = fe->demodulator_priv; | ||
440 | u8 val; | ||
441 | |||
442 | dprintk("%s\n", __func__); | ||
443 | val = (0x80 | si21_readreg(state, LNB_CTRL_REG_1)); | ||
444 | |||
445 | switch (tone) { | ||
446 | case SEC_TONE_ON: | ||
447 | return si21_writereg(state, LNB_CTRL_REG_1, val | 0x20); | ||
448 | |||
449 | case SEC_TONE_OFF: | ||
450 | return si21_writereg(state, LNB_CTRL_REG_1, (val & ~0x20)); | ||
451 | |||
452 | default: | ||
453 | return -EINVAL; | ||
454 | } | ||
455 | } | ||
456 | |||
457 | static int si21xx_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt) | ||
458 | { | ||
459 | struct si21xx_state *state = fe->demodulator_priv; | ||
460 | |||
461 | u8 val; | ||
462 | dprintk("%s: %s\n", __func__, | ||
463 | volt == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" : | ||
464 | volt == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??"); | ||
465 | |||
466 | |||
467 | val = (0x80 | si21_readreg(state, LNB_CTRL_REG_1)); | ||
468 | |||
469 | switch (volt) { | ||
470 | case SEC_VOLTAGE_18: | ||
471 | return si21_writereg(state, LNB_CTRL_REG_1, val | 0x40); | ||
472 | break; | ||
473 | case SEC_VOLTAGE_13: | ||
474 | return si21_writereg(state, LNB_CTRL_REG_1, (val & ~0x40)); | ||
475 | break; | ||
476 | default: | ||
477 | return -EINVAL; | ||
478 | }; | ||
479 | } | ||
480 | |||
481 | static int si21xx_init(struct dvb_frontend *fe) | ||
482 | { | ||
483 | struct si21xx_state *state = fe->demodulator_priv; | ||
484 | int i; | ||
485 | int status = 0; | ||
486 | u8 reg1; | ||
487 | u8 val; | ||
488 | u8 reg2[2]; | ||
489 | |||
490 | dprintk("%s\n", __func__); | ||
491 | |||
492 | for (i = 0; ; i += 2) { | ||
493 | reg1 = serit_sp1511lhb_inittab[i]; | ||
494 | val = serit_sp1511lhb_inittab[i+1]; | ||
495 | if (reg1 == 0xff && val == 0xff) | ||
496 | break; | ||
497 | si21_writeregs(state, reg1, &val, 1); | ||
498 | } | ||
499 | |||
500 | /*DVB QPSK SYSTEM MODE REG*/ | ||
501 | reg1 = 0x08; | ||
502 | si21_writeregs(state, SYSTEM_MODE_REG, ®1, 0x01); | ||
503 | |||
504 | /*transport stream config*/ | ||
505 | /* | ||
506 | mode = PARALLEL; | ||
507 | sdata_form = LSB_FIRST; | ||
508 | clk_edge = FALLING_EDGE; | ||
509 | clk_mode = CLK_GAPPED_MODE; | ||
510 | strt_len = BYTE_WIDE; | ||
511 | sync_pol = ACTIVE_HIGH; | ||
512 | val_pol = ACTIVE_HIGH; | ||
513 | err_pol = ACTIVE_HIGH; | ||
514 | sclk_rate = 0x00; | ||
515 | parity = 0x00 ; | ||
516 | data_delay = 0x00; | ||
517 | clk_delay = 0x00; | ||
518 | pclk_smooth = 0x00; | ||
519 | */ | ||
520 | reg2[0] = | ||
521 | PARALLEL + (LSB_FIRST << 1) | ||
522 | + (FALLING_EDGE << 2) + (CLK_GAPPED_MODE << 3) | ||
523 | + (BYTE_WIDE << 4) + (ACTIVE_HIGH << 5) | ||
524 | + (ACTIVE_HIGH << 6) + (ACTIVE_HIGH << 7); | ||
525 | |||
526 | reg2[1] = 0; | ||
527 | /* sclk_rate + (parity << 2) | ||
528 | + (data_delay << 3) + (clk_delay << 4) | ||
529 | + (pclk_smooth << 5); | ||
530 | */ | ||
531 | status |= si21_writeregs(state, TS_CTRL_REG_1, reg2, 0x02); | ||
532 | if (status != 0) | ||
533 | dprintk(" %s : TS Set Error\n", __func__); | ||
534 | |||
535 | return 0; | ||
536 | |||
537 | } | ||
538 | |||
539 | static int si21_read_status(struct dvb_frontend *fe, fe_status_t *status) | ||
540 | { | ||
541 | struct si21xx_state *state = fe->demodulator_priv; | ||
542 | u8 regs_read[2]; | ||
543 | u8 reg_read; | ||
544 | u8 i; | ||
545 | u8 lock; | ||
546 | u8 signal = si21_readreg(state, ANALOG_AGC_POWER_LEVEL_REG); | ||
547 | |||
548 | si21_readregs(state, LOCK_STATUS_REG_1, regs_read, 0x02); | ||
549 | reg_read = 0; | ||
550 | |||
551 | for (i = 0; i < 7; ++i) | ||
552 | reg_read |= ((regs_read[0] >> i) & 0x01) << (6 - i); | ||
553 | |||
554 | lock = ((reg_read & 0x7f) | (regs_read[1] & 0x80)); | ||
555 | |||
556 | dprintk("%s : FE_READ_STATUS : VSTATUS: 0x%02x\n", __func__, lock); | ||
557 | *status = 0; | ||
558 | |||
559 | if (signal > 10) | ||
560 | *status |= FE_HAS_SIGNAL; | ||
561 | |||
562 | if (lock & 0x2) | ||
563 | *status |= FE_HAS_CARRIER; | ||
564 | |||
565 | if (lock & 0x20) | ||
566 | *status |= FE_HAS_VITERBI; | ||
567 | |||
568 | if (lock & 0x40) | ||
569 | *status |= FE_HAS_SYNC; | ||
570 | |||
571 | if ((lock & 0x7b) == 0x7b) | ||
572 | *status |= FE_HAS_LOCK; | ||
573 | |||
574 | return 0; | ||
575 | } | ||
576 | |||
577 | static int si21_read_signal_strength(struct dvb_frontend *fe, u16 *strength) | ||
578 | { | ||
579 | struct si21xx_state *state = fe->demodulator_priv; | ||
580 | |||
581 | /*status = si21_readreg(state, ANALOG_AGC_POWER_LEVEL_REG, | ||
582 | (u8*)agclevel, 0x01);*/ | ||
583 | |||
584 | u16 signal = (3 * si21_readreg(state, 0x27) * | ||
585 | si21_readreg(state, 0x28)); | ||
586 | |||
587 | dprintk("%s : AGCPWR: 0x%02x%02x, signal=0x%04x\n", __func__, | ||
588 | si21_readreg(state, 0x27), | ||
589 | si21_readreg(state, 0x28), (int) signal); | ||
590 | |||
591 | signal <<= 4; | ||
592 | *strength = signal; | ||
593 | |||
594 | return 0; | ||
595 | } | ||
596 | |||
597 | static int si21_read_ber(struct dvb_frontend *fe, u32 *ber) | ||
598 | { | ||
599 | struct si21xx_state *state = fe->demodulator_priv; | ||
600 | |||
601 | dprintk("%s\n", __func__); | ||
602 | |||
603 | if (state->errmode != STATUS_BER) | ||
604 | return 0; | ||
605 | |||
606 | *ber = (si21_readreg(state, 0x1d) << 8) | | ||
607 | si21_readreg(state, 0x1e); | ||
608 | |||
609 | return 0; | ||
610 | } | ||
611 | |||
612 | static int si21_read_snr(struct dvb_frontend *fe, u16 *snr) | ||
613 | { | ||
614 | struct si21xx_state *state = fe->demodulator_priv; | ||
615 | |||
616 | s32 xsnr = 0xffff - ((si21_readreg(state, 0x24) << 8) | | ||
617 | si21_readreg(state, 0x25)); | ||
618 | xsnr = 3 * (xsnr - 0xa100); | ||
619 | *snr = (xsnr > 0xffff) ? 0xffff : (xsnr < 0) ? 0 : xsnr; | ||
620 | |||
621 | dprintk("%s\n", __func__); | ||
622 | |||
623 | return 0; | ||
624 | } | ||
625 | |||
626 | static int si21_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) | ||
627 | { | ||
628 | struct si21xx_state *state = fe->demodulator_priv; | ||
629 | |||
630 | dprintk("%s\n", __func__); | ||
631 | |||
632 | if (state->errmode != STATUS_UCBLOCKS) | ||
633 | *ucblocks = 0; | ||
634 | else | ||
635 | *ucblocks = (si21_readreg(state, 0x1d) << 8) | | ||
636 | si21_readreg(state, 0x1e); | ||
637 | |||
638 | return 0; | ||
639 | } | ||
640 | |||
641 | /* initiates a channel acquisition sequence | ||
642 | using the specified symbol rate and code rate */ | ||
643 | static int si21xx_setacquire(struct dvb_frontend *fe, int symbrate, | ||
644 | fe_code_rate_t crate) | ||
645 | { | ||
646 | |||
647 | struct si21xx_state *state = fe->demodulator_priv; | ||
648 | u8 coderates[] = { | ||
649 | 0x0, 0x01, 0x02, 0x04, 0x00, | ||
650 | 0x8, 0x10, 0x20, 0x00, 0x3f | ||
651 | }; | ||
652 | |||
653 | u8 coderate_ptr; | ||
654 | int status; | ||
655 | u8 start_acq = 0x80; | ||
656 | u8 reg, regs[3]; | ||
657 | |||
658 | dprintk("%s\n", __func__); | ||
659 | |||
660 | status = PASS; | ||
661 | coderate_ptr = coderates[crate]; | ||
662 | |||
663 | si21xx_set_symbolrate(fe, symbrate); | ||
664 | |||
665 | /* write code rates to use in the Viterbi search */ | ||
666 | status |= si21_writeregs(state, | ||
667 | VIT_SRCH_CTRL_REG_1, | ||
668 | &coderate_ptr, 0x01); | ||
669 | |||
670 | /* clear acq_start bit */ | ||
671 | status |= si21_readregs(state, ACQ_CTRL_REG_2, ®, 0x01); | ||
672 | reg &= ~start_acq; | ||
673 | status |= si21_writeregs(state, ACQ_CTRL_REG_2, ®, 0x01); | ||
674 | |||
675 | /* use new Carrier Frequency Offset Estimator (QuickLock) */ | ||
676 | regs[0] = 0xCB; | ||
677 | regs[1] = 0x40; | ||
678 | regs[2] = 0xCB; | ||
679 | |||
680 | status |= si21_writeregs(state, | ||
681 | TWO_DB_BNDWDTH_THRSHLD_REG, | ||
682 | ®s[0], 0x03); | ||
683 | reg = 0x56; | ||
684 | status |= si21_writeregs(state, | ||
685 | LSA_CTRL_REG_1, ®, 1); | ||
686 | reg = 0x05; | ||
687 | status |= si21_writeregs(state, | ||
688 | BLIND_SCAN_CTRL_REG, ®, 1); | ||
689 | /* start automatic acq */ | ||
690 | status |= si21_writeregs(state, | ||
691 | ACQ_CTRL_REG_2, &start_acq, 0x01); | ||
692 | |||
693 | return status; | ||
694 | } | ||
695 | |||
696 | static int si21xx_set_property(struct dvb_frontend *fe, struct dtv_property *p) | ||
697 | { | ||
698 | dprintk("%s(..)\n", __func__); | ||
699 | return 0; | ||
700 | } | ||
701 | |||
702 | static int si21xx_get_property(struct dvb_frontend *fe, struct dtv_property *p) | ||
703 | { | ||
704 | dprintk("%s(..)\n", __func__); | ||
705 | return 0; | ||
706 | } | ||
707 | |||
708 | static int si21xx_set_frontend(struct dvb_frontend *fe, | ||
709 | struct dvb_frontend_parameters *dfp) | ||
710 | { | ||
711 | struct si21xx_state *state = fe->demodulator_priv; | ||
712 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; | ||
713 | |||
714 | /* freq Channel carrier frequency in KHz (i.e. 1550000 KHz) | ||
715 | datarate Channel symbol rate in Sps (i.e. 22500000 Sps)*/ | ||
716 | |||
717 | /* in MHz */ | ||
718 | unsigned char coarse_tune_freq; | ||
719 | int fine_tune_freq; | ||
720 | unsigned char sample_rate = 0; | ||
721 | /* boolean */ | ||
722 | unsigned int inband_interferer_ind; | ||
723 | |||
724 | /* INTERMEDIATE VALUES */ | ||
725 | int icoarse_tune_freq; /* MHz */ | ||
726 | int ifine_tune_freq; /* MHz */ | ||
727 | unsigned int band_high; | ||
728 | unsigned int band_low; | ||
729 | unsigned int x1; | ||
730 | unsigned int x2; | ||
731 | int i; | ||
732 | unsigned int inband_interferer_div2[ALLOWABLE_FS_COUNT] = { | ||
733 | FALSE, FALSE, FALSE, FALSE, FALSE, | ||
734 | FALSE, FALSE, FALSE, FALSE, FALSE | ||
735 | }; | ||
736 | unsigned int inband_interferer_div4[ALLOWABLE_FS_COUNT] = { | ||
737 | FALSE, FALSE, FALSE, FALSE, FALSE, | ||
738 | FALSE, FALSE, FALSE, FALSE, FALSE | ||
739 | }; | ||
740 | |||
741 | int status; | ||
742 | |||
743 | /* allowable sample rates for ADC in MHz */ | ||
744 | int afs[ALLOWABLE_FS_COUNT] = { 200, 192, 193, 194, 195, | ||
745 | 196, 204, 205, 206, 207 | ||
746 | }; | ||
747 | /* in MHz */ | ||
748 | int if_limit_high; | ||
749 | int if_limit_low; | ||
750 | int lnb_lo; | ||
751 | int lnb_uncertanity; | ||
752 | |||
753 | int rf_freq; | ||
754 | int data_rate; | ||
755 | unsigned char regs[4]; | ||
756 | |||
757 | dprintk("%s : FE_SET_FRONTEND\n", __func__); | ||
758 | |||
759 | if (c->delivery_system != SYS_DVBS) { | ||
760 | dprintk("%s: unsupported delivery system selected (%d)\n", | ||
761 | __func__, c->delivery_system); | ||
762 | return -EOPNOTSUPP; | ||
763 | } | ||
764 | |||
765 | for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) | ||
766 | inband_interferer_div2[i] = inband_interferer_div4[i] = FALSE; | ||
767 | |||
768 | if_limit_high = -700000; | ||
769 | if_limit_low = -100000; | ||
770 | /* in MHz */ | ||
771 | lnb_lo = 0; | ||
772 | lnb_uncertanity = 0; | ||
773 | |||
774 | rf_freq = 10 * c->frequency ; | ||
775 | data_rate = c->symbol_rate / 100; | ||
776 | |||
777 | status = PASS; | ||
778 | |||
779 | band_low = (rf_freq - lnb_lo) - ((lnb_uncertanity * 200) | ||
780 | + (data_rate * 135)) / 200; | ||
781 | |||
782 | band_high = (rf_freq - lnb_lo) + ((lnb_uncertanity * 200) | ||
783 | + (data_rate * 135)) / 200; | ||
784 | |||
785 | |||
786 | icoarse_tune_freq = 100000 * | ||
787 | (((rf_freq - lnb_lo) - | ||
788 | (if_limit_low + if_limit_high) / 2) | ||
789 | / 100000); | ||
790 | |||
791 | ifine_tune_freq = (rf_freq - lnb_lo) - icoarse_tune_freq ; | ||
792 | |||
793 | for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) { | ||
794 | x1 = ((rf_freq - lnb_lo) / (afs[i] * 2500)) * | ||
795 | (afs[i] * 2500) + afs[i] * 2500; | ||
796 | |||
797 | x2 = ((rf_freq - lnb_lo) / (afs[i] * 2500)) * | ||
798 | (afs[i] * 2500); | ||
799 | |||
800 | if (((band_low < x1) && (x1 < band_high)) || | ||
801 | ((band_low < x2) && (x2 < band_high))) | ||
802 | inband_interferer_div4[i] = TRUE; | ||
803 | |||
804 | } | ||
805 | |||
806 | for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) { | ||
807 | x1 = ((rf_freq - lnb_lo) / (afs[i] * 5000)) * | ||
808 | (afs[i] * 5000) + afs[i] * 5000; | ||
809 | |||
810 | x2 = ((rf_freq - lnb_lo) / (afs[i] * 5000)) * | ||
811 | (afs[i] * 5000); | ||
812 | |||
813 | if (((band_low < x1) && (x1 < band_high)) || | ||
814 | ((band_low < x2) && (x2 < band_high))) | ||
815 | inband_interferer_div2[i] = TRUE; | ||
816 | } | ||
817 | |||
818 | inband_interferer_ind = TRUE; | ||
819 | for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) | ||
820 | inband_interferer_ind &= inband_interferer_div2[i] | | ||
821 | inband_interferer_div4[i]; | ||
822 | |||
823 | if (inband_interferer_ind) { | ||
824 | for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) { | ||
825 | if (inband_interferer_div2[i] == FALSE) { | ||
826 | sample_rate = (u8) afs[i]; | ||
827 | break; | ||
828 | } | ||
829 | } | ||
830 | } else { | ||
831 | for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) { | ||
832 | if ((inband_interferer_div2[i] | | ||
833 | inband_interferer_div4[i]) == FALSE) { | ||
834 | sample_rate = (u8) afs[i]; | ||
835 | break; | ||
836 | } | ||
837 | } | ||
838 | |||
839 | } | ||
840 | |||
841 | if (sample_rate > 207 || sample_rate < 192) | ||
842 | sample_rate = 200; | ||
843 | |||
844 | fine_tune_freq = ((0x4000 * (ifine_tune_freq / 10)) / | ||
845 | ((sample_rate) * 1000)); | ||
846 | |||
847 | coarse_tune_freq = (u8)(icoarse_tune_freq / 100000); | ||
848 | |||
849 | regs[0] = sample_rate; | ||
850 | regs[1] = coarse_tune_freq; | ||
851 | regs[2] = fine_tune_freq & 0xFF; | ||
852 | regs[3] = fine_tune_freq >> 8 & 0xFF; | ||
853 | |||
854 | status |= si21_writeregs(state, PLL_DIVISOR_REG, ®s[0], 0x04); | ||
855 | |||
856 | state->fs = sample_rate;/*ADC MHz*/ | ||
857 | si21xx_setacquire(fe, c->symbol_rate, c->fec_inner); | ||
858 | |||
859 | return 0; | ||
860 | } | ||
861 | |||
862 | static int si21xx_sleep(struct dvb_frontend *fe) | ||
863 | { | ||
864 | struct si21xx_state *state = fe->demodulator_priv; | ||
865 | u8 regdata; | ||
866 | |||
867 | dprintk("%s\n", __func__); | ||
868 | |||
869 | si21_readregs(state, SYSTEM_MODE_REG, ®data, 0x01); | ||
870 | regdata |= 1 << 6; | ||
871 | si21_writeregs(state, SYSTEM_MODE_REG, ®data, 0x01); | ||
872 | state->initialised = 0; | ||
873 | |||
874 | return 0; | ||
875 | } | ||
876 | |||
877 | static void si21xx_release(struct dvb_frontend *fe) | ||
878 | { | ||
879 | struct si21xx_state *state = fe->demodulator_priv; | ||
880 | |||
881 | dprintk("%s\n", __func__); | ||
882 | |||
883 | kfree(state); | ||
884 | } | ||
885 | |||
886 | static struct dvb_frontend_ops si21xx_ops = { | ||
887 | |||
888 | .info = { | ||
889 | .name = "SL SI21XX DVB-S", | ||
890 | .type = FE_QPSK, | ||
891 | .frequency_min = 950000, | ||
892 | .frequency_max = 2150000, | ||
893 | .frequency_stepsize = 125, /* kHz for QPSK frontends */ | ||
894 | .frequency_tolerance = 0, | ||
895 | .symbol_rate_min = 1000000, | ||
896 | .symbol_rate_max = 45000000, | ||
897 | .symbol_rate_tolerance = 500, /* ppm */ | ||
898 | .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | | ||
899 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | | ||
900 | FE_CAN_QPSK | | ||
901 | FE_CAN_FEC_AUTO | ||
902 | }, | ||
903 | |||
904 | .release = si21xx_release, | ||
905 | .init = si21xx_init, | ||
906 | .sleep = si21xx_sleep, | ||
907 | .write = si21_write, | ||
908 | .read_status = si21_read_status, | ||
909 | .read_ber = si21_read_ber, | ||
910 | .read_signal_strength = si21_read_signal_strength, | ||
911 | .read_snr = si21_read_snr, | ||
912 | .read_ucblocks = si21_read_ucblocks, | ||
913 | .diseqc_send_master_cmd = si21xx_send_diseqc_msg, | ||
914 | .diseqc_send_burst = si21xx_send_diseqc_burst, | ||
915 | .set_tone = si21xx_set_tone, | ||
916 | .set_voltage = si21xx_set_voltage, | ||
917 | |||
918 | .set_property = si21xx_set_property, | ||
919 | .get_property = si21xx_get_property, | ||
920 | .set_frontend = si21xx_set_frontend, | ||
921 | }; | ||
922 | |||
923 | struct dvb_frontend *si21xx_attach(const struct si21xx_config *config, | ||
924 | struct i2c_adapter *i2c) | ||
925 | { | ||
926 | struct si21xx_state *state = NULL; | ||
927 | int id; | ||
928 | |||
929 | dprintk("%s\n", __func__); | ||
930 | |||
931 | /* allocate memory for the internal state */ | ||
932 | state = kmalloc(sizeof(struct si21xx_state), GFP_KERNEL); | ||
933 | if (state == NULL) | ||
934 | goto error; | ||
935 | |||
936 | /* setup the state */ | ||
937 | state->config = config; | ||
938 | state->i2c = i2c; | ||
939 | state->initialised = 0; | ||
940 | state->errmode = STATUS_BER; | ||
941 | |||
942 | /* check if the demod is there */ | ||
943 | id = si21_readreg(state, SYSTEM_MODE_REG); | ||
944 | si21_writereg(state, SYSTEM_MODE_REG, id | 0x40); /* standby off */ | ||
945 | msleep(200); | ||
946 | id = si21_readreg(state, 0x00); | ||
947 | |||
948 | /* register 0x00 contains: | ||
949 | 0x34 for SI2107 | ||
950 | 0x24 for SI2108 | ||
951 | 0x14 for SI2109 | ||
952 | 0x04 for SI2110 | ||
953 | */ | ||
954 | if (id != 0x04 && id != 0x14) | ||
955 | goto error; | ||
956 | |||
957 | /* create dvb_frontend */ | ||
958 | memcpy(&state->frontend.ops, &si21xx_ops, | ||
959 | sizeof(struct dvb_frontend_ops)); | ||
960 | state->frontend.demodulator_priv = state; | ||
961 | return &state->frontend; | ||
962 | |||
963 | error: | ||
964 | kfree(state); | ||
965 | return NULL; | ||
966 | } | ||
967 | EXPORT_SYMBOL(si21xx_attach); | ||
968 | |||
969 | module_param(debug, int, 0644); | ||
970 | MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); | ||
971 | |||
972 | MODULE_DESCRIPTION("SL SI21XX DVB Demodulator driver"); | ||
973 | MODULE_AUTHOR("Igor M. Liplianin"); | ||
974 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/media/dvb/frontends/si21xx.h b/drivers/media/dvb/frontends/si21xx.h new file mode 100644 index 000000000000..141b5b8a5f63 --- /dev/null +++ b/drivers/media/dvb/frontends/si21xx.h | |||
@@ -0,0 +1,37 @@ | |||
1 | #ifndef SI21XX_H | ||
2 | #define SI21XX_H | ||
3 | |||
4 | #include <linux/dvb/frontend.h> | ||
5 | #include "dvb_frontend.h" | ||
6 | |||
7 | struct si21xx_config { | ||
8 | /* the demodulator's i2c address */ | ||
9 | u8 demod_address; | ||
10 | |||
11 | /* minimum delay before retuning */ | ||
12 | int min_delay_ms; | ||
13 | }; | ||
14 | |||
15 | #if defined(CONFIG_DVB_SI21XX) || \ | ||
16 | (defined(CONFIG_DVB_SI21XX_MODULE) && defined(MODULE)) | ||
17 | extern struct dvb_frontend *si21xx_attach(const struct si21xx_config *config, | ||
18 | struct i2c_adapter *i2c); | ||
19 | #else | ||
20 | static inline struct dvb_frontend *si21xx_attach( | ||
21 | const struct si21xx_config *config, struct i2c_adapter *i2c) | ||
22 | { | ||
23 | printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); | ||
24 | return NULL; | ||
25 | } | ||
26 | #endif | ||
27 | |||
28 | static inline int si21xx_writeregister(struct dvb_frontend *fe, u8 reg, u8 val) | ||
29 | { | ||
30 | int r = 0; | ||
31 | u8 buf[] = {reg, val}; | ||
32 | if (fe->ops.write) | ||
33 | r = fe->ops.write(fe, buf, 2); | ||
34 | return r; | ||
35 | } | ||
36 | |||
37 | #endif | ||