Commit | Line | Data |
---|---|---|
74ba9207 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
ae8dc8ee MP |
2 | /* |
3 | Driver for M88RS2000 demodulator and tuner | |
4 | ||
5 | Copyright (C) 2012 Malcolm Priestley (tvboxspy@gmail.com) | |
6 | Beta Driver | |
7 | ||
8 | Include various calculation code from DS3000 driver. | |
9 | Copyright (C) 2009 Konstantin Dimitrov. | |
10 | ||
ae8dc8ee MP |
11 | |
12 | */ | |
13 | #include <linux/init.h> | |
14 | #include <linux/module.h> | |
15 | #include <linux/device.h> | |
16 | #include <linux/jiffies.h> | |
17 | #include <linux/string.h> | |
18 | #include <linux/slab.h> | |
19 | #include <linux/types.h> | |
20 | ||
21 | ||
fada1935 | 22 | #include <media/dvb_frontend.h> |
ae8dc8ee MP |
23 | #include "m88rs2000.h" |
24 | ||
25 | struct m88rs2000_state { | |
26 | struct i2c_adapter *i2c; | |
27 | const struct m88rs2000_config *config; | |
28 | struct dvb_frontend frontend; | |
29 | u8 no_lock_count; | |
30 | u32 tuner_frequency; | |
31 | u32 symbol_rate; | |
0df289a2 | 32 | enum fe_code_rate fec_inner; |
ae8dc8ee MP |
33 | u8 tuner_level; |
34 | int errmode; | |
35 | }; | |
36 | ||
37 | static int m88rs2000_debug; | |
38 | ||
39 | module_param_named(debug, m88rs2000_debug, int, 0644); | |
40 | MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))."); | |
41 | ||
42 | #define dprintk(level, args...) do { \ | |
43 | if (level & m88rs2000_debug) \ | |
44 | printk(KERN_DEBUG "m88rs2000-fe: " args); \ | |
45 | } while (0) | |
46 | ||
47 | #define deb_info(args...) dprintk(0x01, args) | |
48 | #define info(format, arg...) \ | |
49 | printk(KERN_INFO "m88rs2000-fe: " format "\n" , ## arg) | |
50 | ||
b858c331 | 51 | static int m88rs2000_writereg(struct m88rs2000_state *state, |
ae8dc8ee MP |
52 | u8 reg, u8 data) |
53 | { | |
54 | int ret; | |
ae8dc8ee MP |
55 | u8 buf[] = { reg, data }; |
56 | struct i2c_msg msg = { | |
b858c331 | 57 | .addr = state->config->demod_addr, |
ae8dc8ee MP |
58 | .flags = 0, |
59 | .buf = buf, | |
60 | .len = 2 | |
61 | }; | |
62 | ||
63 | ret = i2c_transfer(state->i2c, &msg, 1); | |
64 | ||
65 | if (ret != 1) | |
4bd69e7b MCC |
66 | deb_info("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n", |
67 | __func__, reg, data, ret); | |
ae8dc8ee MP |
68 | |
69 | return (ret != 1) ? -EREMOTEIO : 0; | |
70 | } | |
71 | ||
b858c331 | 72 | static u8 m88rs2000_readreg(struct m88rs2000_state *state, u8 reg) |
ae8dc8ee MP |
73 | { |
74 | int ret; | |
75 | u8 b0[] = { reg }; | |
76 | u8 b1[] = { 0 }; | |
b858c331 | 77 | |
ae8dc8ee MP |
78 | struct i2c_msg msg[] = { |
79 | { | |
b858c331 | 80 | .addr = state->config->demod_addr, |
ae8dc8ee MP |
81 | .flags = 0, |
82 | .buf = b0, | |
83 | .len = 1 | |
84 | }, { | |
b858c331 | 85 | .addr = state->config->demod_addr, |
ae8dc8ee MP |
86 | .flags = I2C_M_RD, |
87 | .buf = b1, | |
88 | .len = 1 | |
89 | } | |
90 | }; | |
91 | ||
92 | ret = i2c_transfer(state->i2c, msg, 2); | |
93 | ||
94 | if (ret != 2) | |
95 | deb_info("%s: readreg error (reg == 0x%02x, ret == %i)\n", | |
96 | __func__, reg, ret); | |
97 | ||
98 | return b1[0]; | |
99 | } | |
100 | ||
06af15d1 MP |
101 | static u32 m88rs2000_get_mclk(struct dvb_frontend *fe) |
102 | { | |
103 | struct m88rs2000_state *state = fe->demodulator_priv; | |
104 | u32 mclk; | |
105 | u8 reg; | |
106 | /* Must not be 0x00 or 0xff */ | |
107 | reg = m88rs2000_readreg(state, 0x86); | |
108 | if (!reg || reg == 0xff) | |
109 | return 0; | |
110 | ||
111 | reg /= 2; | |
112 | reg += 1; | |
113 | ||
114 | mclk = (u32)(reg * RS2000_FE_CRYSTAL_KHZ + 28 / 2) / 28; | |
115 | ||
116 | return mclk; | |
117 | } | |
118 | ||
119 | static int m88rs2000_set_carrieroffset(struct dvb_frontend *fe, s16 offset) | |
120 | { | |
121 | struct m88rs2000_state *state = fe->demodulator_priv; | |
122 | u32 mclk; | |
123 | s32 tmp; | |
124 | u8 reg; | |
125 | int ret; | |
126 | ||
127 | mclk = m88rs2000_get_mclk(fe); | |
128 | if (!mclk) | |
129 | return -EINVAL; | |
130 | ||
131 | tmp = (offset * 4096 + (s32)mclk / 2) / (s32)mclk; | |
132 | if (tmp < 0) | |
133 | tmp += 4096; | |
134 | ||
135 | /* Carrier Offset */ | |
136 | ret = m88rs2000_writereg(state, 0x9c, (u8)(tmp >> 4)); | |
137 | ||
138 | reg = m88rs2000_readreg(state, 0x9d); | |
139 | reg &= 0xf; | |
140 | reg |= (u8)(tmp & 0xf) << 4; | |
141 | ||
142 | ret |= m88rs2000_writereg(state, 0x9d, reg); | |
143 | ||
144 | return ret; | |
145 | } | |
146 | ||
ae8dc8ee MP |
147 | static int m88rs2000_set_symbolrate(struct dvb_frontend *fe, u32 srate) |
148 | { | |
149 | struct m88rs2000_state *state = fe->demodulator_priv; | |
150 | int ret; | |
dd4491df MP |
151 | u64 temp; |
152 | u32 mclk; | |
ae8dc8ee MP |
153 | u8 b[3]; |
154 | ||
155 | if ((srate < 1000000) || (srate > 45000000)) | |
156 | return -EINVAL; | |
157 | ||
dd4491df MP |
158 | mclk = m88rs2000_get_mclk(fe); |
159 | if (!mclk) | |
160 | return -EINVAL; | |
161 | ||
ae8dc8ee | 162 | temp = srate / 1000; |
dd4491df MP |
163 | temp *= 1 << 24; |
164 | ||
165 | do_div(temp, mclk); | |
ae8dc8ee MP |
166 | |
167 | b[0] = (u8) (temp >> 16) & 0xff; | |
168 | b[1] = (u8) (temp >> 8) & 0xff; | |
169 | b[2] = (u8) temp & 0xff; | |
dd4491df | 170 | |
b858c331 IL |
171 | ret = m88rs2000_writereg(state, 0x93, b[2]); |
172 | ret |= m88rs2000_writereg(state, 0x94, b[1]); | |
173 | ret |= m88rs2000_writereg(state, 0x95, b[0]); | |
ae8dc8ee | 174 | |
dd4491df MP |
175 | if (srate > 10000000) |
176 | ret |= m88rs2000_writereg(state, 0xa0, 0x20); | |
177 | else | |
178 | ret |= m88rs2000_writereg(state, 0xa0, 0x60); | |
179 | ||
180 | ret |= m88rs2000_writereg(state, 0xa1, 0xe0); | |
181 | ||
182 | if (srate > 12000000) | |
183 | ret |= m88rs2000_writereg(state, 0xa3, 0x20); | |
184 | else if (srate > 2800000) | |
185 | ret |= m88rs2000_writereg(state, 0xa3, 0x98); | |
186 | else | |
187 | ret |= m88rs2000_writereg(state, 0xa3, 0x90); | |
188 | ||
ae8dc8ee MP |
189 | deb_info("m88rs2000: m88rs2000_set_symbolrate\n"); |
190 | return ret; | |
191 | } | |
192 | ||
193 | static int m88rs2000_send_diseqc_msg(struct dvb_frontend *fe, | |
194 | struct dvb_diseqc_master_cmd *m) | |
195 | { | |
196 | struct m88rs2000_state *state = fe->demodulator_priv; | |
197 | ||
198 | int i; | |
199 | u8 reg; | |
200 | deb_info("%s\n", __func__); | |
b858c331 IL |
201 | m88rs2000_writereg(state, 0x9a, 0x30); |
202 | reg = m88rs2000_readreg(state, 0xb2); | |
ae8dc8ee | 203 | reg &= 0x3f; |
b858c331 | 204 | m88rs2000_writereg(state, 0xb2, reg); |
ae8dc8ee | 205 | for (i = 0; i < m->msg_len; i++) |
b858c331 | 206 | m88rs2000_writereg(state, 0xb3 + i, m->msg[i]); |
ae8dc8ee | 207 | |
b858c331 | 208 | reg = m88rs2000_readreg(state, 0xb1); |
ae8dc8ee MP |
209 | reg &= 0x87; |
210 | reg |= ((m->msg_len - 1) << 3) | 0x07; | |
211 | reg &= 0x7f; | |
b858c331 | 212 | m88rs2000_writereg(state, 0xb1, reg); |
ae8dc8ee MP |
213 | |
214 | for (i = 0; i < 15; i++) { | |
b858c331 | 215 | if ((m88rs2000_readreg(state, 0xb1) & 0x40) == 0x0) |
ae8dc8ee MP |
216 | break; |
217 | msleep(20); | |
218 | } | |
219 | ||
b858c331 | 220 | reg = m88rs2000_readreg(state, 0xb1); |
ae8dc8ee MP |
221 | if ((reg & 0x40) > 0x0) { |
222 | reg &= 0x7f; | |
223 | reg |= 0x40; | |
b858c331 | 224 | m88rs2000_writereg(state, 0xb1, reg); |
ae8dc8ee MP |
225 | } |
226 | ||
b858c331 | 227 | reg = m88rs2000_readreg(state, 0xb2); |
ae8dc8ee MP |
228 | reg &= 0x3f; |
229 | reg |= 0x80; | |
b858c331 IL |
230 | m88rs2000_writereg(state, 0xb2, reg); |
231 | m88rs2000_writereg(state, 0x9a, 0xb0); | |
ae8dc8ee MP |
232 | |
233 | ||
234 | return 0; | |
235 | } | |
236 | ||
237 | static int m88rs2000_send_diseqc_burst(struct dvb_frontend *fe, | |
0df289a2 | 238 | enum fe_sec_mini_cmd burst) |
ae8dc8ee MP |
239 | { |
240 | struct m88rs2000_state *state = fe->demodulator_priv; | |
241 | u8 reg0, reg1; | |
242 | deb_info("%s\n", __func__); | |
b858c331 | 243 | m88rs2000_writereg(state, 0x9a, 0x30); |
ae8dc8ee | 244 | msleep(50); |
b858c331 IL |
245 | reg0 = m88rs2000_readreg(state, 0xb1); |
246 | reg1 = m88rs2000_readreg(state, 0xb2); | |
593a2ce0 | 247 | /* TODO complete this section */ |
b858c331 IL |
248 | m88rs2000_writereg(state, 0xb2, reg1); |
249 | m88rs2000_writereg(state, 0xb1, reg0); | |
250 | m88rs2000_writereg(state, 0x9a, 0xb0); | |
ae8dc8ee MP |
251 | |
252 | return 0; | |
253 | } | |
254 | ||
0df289a2 MCC |
255 | static int m88rs2000_set_tone(struct dvb_frontend *fe, |
256 | enum fe_sec_tone_mode tone) | |
ae8dc8ee MP |
257 | { |
258 | struct m88rs2000_state *state = fe->demodulator_priv; | |
259 | u8 reg0, reg1; | |
b858c331 IL |
260 | m88rs2000_writereg(state, 0x9a, 0x30); |
261 | reg0 = m88rs2000_readreg(state, 0xb1); | |
262 | reg1 = m88rs2000_readreg(state, 0xb2); | |
ae8dc8ee MP |
263 | |
264 | reg1 &= 0x3f; | |
265 | ||
266 | switch (tone) { | |
267 | case SEC_TONE_ON: | |
268 | reg0 |= 0x4; | |
269 | reg0 &= 0xbc; | |
593a2ce0 | 270 | break; |
ae8dc8ee MP |
271 | case SEC_TONE_OFF: |
272 | reg1 |= 0x80; | |
593a2ce0 | 273 | break; |
ae8dc8ee | 274 | default: |
593a2ce0 | 275 | break; |
ae8dc8ee | 276 | } |
b858c331 IL |
277 | m88rs2000_writereg(state, 0xb2, reg1); |
278 | m88rs2000_writereg(state, 0xb1, reg0); | |
279 | m88rs2000_writereg(state, 0x9a, 0xb0); | |
ae8dc8ee MP |
280 | return 0; |
281 | } | |
282 | ||
283 | struct inittab { | |
284 | u8 cmd; | |
285 | u8 reg; | |
286 | u8 val; | |
287 | }; | |
288 | ||
7d3c8e8f | 289 | static struct inittab m88rs2000_setup[] = { |
ae8dc8ee MP |
290 | {DEMOD_WRITE, 0x9a, 0x30}, |
291 | {DEMOD_WRITE, 0x00, 0x01}, | |
292 | {WRITE_DELAY, 0x19, 0x00}, | |
293 | {DEMOD_WRITE, 0x00, 0x00}, | |
294 | {DEMOD_WRITE, 0x9a, 0xb0}, | |
295 | {DEMOD_WRITE, 0x81, 0xc1}, | |
ae8dc8ee MP |
296 | {DEMOD_WRITE, 0x81, 0x81}, |
297 | {DEMOD_WRITE, 0x86, 0xc6}, | |
298 | {DEMOD_WRITE, 0x9a, 0x30}, | |
299 | {DEMOD_WRITE, 0xf0, 0x22}, | |
300 | {DEMOD_WRITE, 0xf1, 0xbf}, | |
301 | {DEMOD_WRITE, 0xb0, 0x45}, | |
593a2ce0 | 302 | {DEMOD_WRITE, 0xb2, 0x01}, /* set voltage pin always set 1*/ |
ae8dc8ee MP |
303 | {DEMOD_WRITE, 0x9a, 0xb0}, |
304 | {0xff, 0xaa, 0xff} | |
305 | }; | |
306 | ||
7d3c8e8f | 307 | static struct inittab m88rs2000_shutdown[] = { |
ae8dc8ee MP |
308 | {DEMOD_WRITE, 0x9a, 0x30}, |
309 | {DEMOD_WRITE, 0xb0, 0x00}, | |
310 | {DEMOD_WRITE, 0xf1, 0x89}, | |
311 | {DEMOD_WRITE, 0x00, 0x01}, | |
312 | {DEMOD_WRITE, 0x9a, 0xb0}, | |
ae8dc8ee MP |
313 | {DEMOD_WRITE, 0x81, 0x81}, |
314 | {0xff, 0xaa, 0xff} | |
315 | }; | |
316 | ||
7d3c8e8f | 317 | static struct inittab fe_reset[] = { |
ae8dc8ee MP |
318 | {DEMOD_WRITE, 0x00, 0x01}, |
319 | {DEMOD_WRITE, 0x20, 0x81}, | |
320 | {DEMOD_WRITE, 0x21, 0x80}, | |
321 | {DEMOD_WRITE, 0x10, 0x33}, | |
322 | {DEMOD_WRITE, 0x11, 0x44}, | |
323 | {DEMOD_WRITE, 0x12, 0x07}, | |
324 | {DEMOD_WRITE, 0x18, 0x20}, | |
325 | {DEMOD_WRITE, 0x28, 0x04}, | |
326 | {DEMOD_WRITE, 0x29, 0x8e}, | |
327 | {DEMOD_WRITE, 0x3b, 0xff}, | |
328 | {DEMOD_WRITE, 0x32, 0x10}, | |
329 | {DEMOD_WRITE, 0x33, 0x02}, | |
330 | {DEMOD_WRITE, 0x34, 0x30}, | |
331 | {DEMOD_WRITE, 0x35, 0xff}, | |
332 | {DEMOD_WRITE, 0x38, 0x50}, | |
333 | {DEMOD_WRITE, 0x39, 0x68}, | |
334 | {DEMOD_WRITE, 0x3c, 0x7f}, | |
335 | {DEMOD_WRITE, 0x3d, 0x0f}, | |
336 | {DEMOD_WRITE, 0x45, 0x20}, | |
337 | {DEMOD_WRITE, 0x46, 0x24}, | |
338 | {DEMOD_WRITE, 0x47, 0x7c}, | |
339 | {DEMOD_WRITE, 0x48, 0x16}, | |
340 | {DEMOD_WRITE, 0x49, 0x04}, | |
341 | {DEMOD_WRITE, 0x4a, 0x01}, | |
342 | {DEMOD_WRITE, 0x4b, 0x78}, | |
343 | {DEMOD_WRITE, 0X4d, 0xd2}, | |
344 | {DEMOD_WRITE, 0x4e, 0x6d}, | |
345 | {DEMOD_WRITE, 0x50, 0x30}, | |
346 | {DEMOD_WRITE, 0x51, 0x30}, | |
347 | {DEMOD_WRITE, 0x54, 0x7b}, | |
348 | {DEMOD_WRITE, 0x56, 0x09}, | |
349 | {DEMOD_WRITE, 0x58, 0x59}, | |
350 | {DEMOD_WRITE, 0x59, 0x37}, | |
351 | {DEMOD_WRITE, 0x63, 0xfa}, | |
352 | {0xff, 0xaa, 0xff} | |
353 | }; | |
354 | ||
7d3c8e8f | 355 | static struct inittab fe_trigger[] = { |
ae8dc8ee MP |
356 | {DEMOD_WRITE, 0x97, 0x04}, |
357 | {DEMOD_WRITE, 0x99, 0x77}, | |
358 | {DEMOD_WRITE, 0x9b, 0x64}, | |
359 | {DEMOD_WRITE, 0x9e, 0x00}, | |
360 | {DEMOD_WRITE, 0x9f, 0xf8}, | |
ae8dc8ee MP |
361 | {DEMOD_WRITE, 0x98, 0xff}, |
362 | {DEMOD_WRITE, 0xc0, 0x0f}, | |
363 | {DEMOD_WRITE, 0x89, 0x01}, | |
364 | {DEMOD_WRITE, 0x00, 0x00}, | |
365 | {WRITE_DELAY, 0x0a, 0x00}, | |
366 | {DEMOD_WRITE, 0x00, 0x01}, | |
367 | {DEMOD_WRITE, 0x00, 0x00}, | |
368 | {DEMOD_WRITE, 0x9a, 0xb0}, | |
369 | {0xff, 0xaa, 0xff} | |
370 | }; | |
371 | ||
372 | static int m88rs2000_tab_set(struct m88rs2000_state *state, | |
373 | struct inittab *tab) | |
374 | { | |
375 | int ret = 0; | |
376 | u8 i; | |
377 | if (tab == NULL) | |
378 | return -EINVAL; | |
379 | ||
380 | for (i = 0; i < 255; i++) { | |
381 | switch (tab[i].cmd) { | |
382 | case 0x01: | |
b858c331 | 383 | ret = m88rs2000_writereg(state, tab[i].reg, |
ae8dc8ee MP |
384 | tab[i].val); |
385 | break; | |
386 | case 0x10: | |
387 | if (tab[i].reg > 0) | |
388 | mdelay(tab[i].reg); | |
389 | break; | |
390 | case 0xff: | |
391 | if (tab[i].reg == 0xaa && tab[i].val == 0xff) | |
392 | return 0; | |
393 | case 0x00: | |
394 | break; | |
395 | default: | |
396 | return -EINVAL; | |
397 | } | |
398 | if (ret < 0) | |
399 | return -ENODEV; | |
400 | } | |
401 | return 0; | |
402 | } | |
403 | ||
0df289a2 MCC |
404 | static int m88rs2000_set_voltage(struct dvb_frontend *fe, |
405 | enum fe_sec_voltage volt) | |
ae8dc8ee | 406 | { |
38431a98 IL |
407 | struct m88rs2000_state *state = fe->demodulator_priv; |
408 | u8 data; | |
409 | ||
b858c331 | 410 | data = m88rs2000_readreg(state, 0xb2); |
38431a98 IL |
411 | data |= 0x03; /* bit0 V/H, bit1 off/on */ |
412 | ||
413 | switch (volt) { | |
414 | case SEC_VOLTAGE_18: | |
415 | data &= ~0x03; | |
416 | break; | |
417 | case SEC_VOLTAGE_13: | |
418 | data &= ~0x03; | |
419 | data |= 0x01; | |
420 | break; | |
421 | case SEC_VOLTAGE_OFF: | |
422 | break; | |
423 | } | |
424 | ||
b858c331 | 425 | m88rs2000_writereg(state, 0xb2, data); |
ae8dc8ee MP |
426 | |
427 | return 0; | |
428 | } | |
429 | ||
ae8dc8ee MP |
430 | static int m88rs2000_init(struct dvb_frontend *fe) |
431 | { | |
432 | struct m88rs2000_state *state = fe->demodulator_priv; | |
433 | int ret; | |
434 | ||
435 | deb_info("m88rs2000: init chip\n"); | |
436 | /* Setup frontend from shutdown/cold */ | |
081416e6 IL |
437 | if (state->config->inittab) |
438 | ret = m88rs2000_tab_set(state, | |
439 | (struct inittab *)state->config->inittab); | |
440 | else | |
441 | ret = m88rs2000_tab_set(state, m88rs2000_setup); | |
ae8dc8ee MP |
442 | |
443 | return ret; | |
444 | } | |
445 | ||
446 | static int m88rs2000_sleep(struct dvb_frontend *fe) | |
447 | { | |
448 | struct m88rs2000_state *state = fe->demodulator_priv; | |
449 | int ret; | |
450 | /* Shutdown the frondend */ | |
451 | ret = m88rs2000_tab_set(state, m88rs2000_shutdown); | |
452 | return ret; | |
453 | } | |
454 | ||
0df289a2 MCC |
455 | static int m88rs2000_read_status(struct dvb_frontend *fe, |
456 | enum fe_status *status) | |
ae8dc8ee MP |
457 | { |
458 | struct m88rs2000_state *state = fe->demodulator_priv; | |
b858c331 | 459 | u8 reg = m88rs2000_readreg(state, 0x8c); |
ae8dc8ee MP |
460 | |
461 | *status = 0; | |
462 | ||
7a9d6b43 | 463 | if ((reg & 0xee) == 0xee) { |
ae8dc8ee | 464 | *status = FE_HAS_CARRIER | FE_HAS_SIGNAL | FE_HAS_VITERBI |
ff54298b | 465 | | FE_HAS_SYNC | FE_HAS_LOCK; |
ae8dc8ee MP |
466 | if (state->config->set_ts_params) |
467 | state->config->set_ts_params(fe, CALL_IS_READ); | |
468 | } | |
469 | return 0; | |
470 | } | |
471 | ||
ae8dc8ee MP |
472 | static int m88rs2000_read_ber(struct dvb_frontend *fe, u32 *ber) |
473 | { | |
38f7889c IL |
474 | struct m88rs2000_state *state = fe->demodulator_priv; |
475 | u8 tmp0, tmp1; | |
476 | ||
b858c331 IL |
477 | m88rs2000_writereg(state, 0x9a, 0x30); |
478 | tmp0 = m88rs2000_readreg(state, 0xd8); | |
38f7889c | 479 | if ((tmp0 & 0x10) != 0) { |
b858c331 | 480 | m88rs2000_writereg(state, 0x9a, 0xb0); |
38f7889c IL |
481 | *ber = 0xffffffff; |
482 | return 0; | |
483 | } | |
484 | ||
b858c331 IL |
485 | *ber = (m88rs2000_readreg(state, 0xd7) << 8) | |
486 | m88rs2000_readreg(state, 0xd6); | |
38f7889c | 487 | |
b858c331 IL |
488 | tmp1 = m88rs2000_readreg(state, 0xd9); |
489 | m88rs2000_writereg(state, 0xd9, (tmp1 & ~7) | 4); | |
38f7889c | 490 | /* needs twice */ |
b858c331 IL |
491 | m88rs2000_writereg(state, 0xd8, (tmp0 & ~8) | 0x30); |
492 | m88rs2000_writereg(state, 0xd8, (tmp0 & ~8) | 0x30); | |
493 | m88rs2000_writereg(state, 0x9a, 0xb0); | |
38f7889c | 494 | |
ae8dc8ee MP |
495 | return 0; |
496 | } | |
497 | ||
498 | static int m88rs2000_read_signal_strength(struct dvb_frontend *fe, | |
499 | u16 *strength) | |
500 | { | |
a0a030bd MP |
501 | if (fe->ops.tuner_ops.get_rf_strength) |
502 | fe->ops.tuner_ops.get_rf_strength(fe, strength); | |
503 | ||
ae8dc8ee MP |
504 | return 0; |
505 | } | |
506 | ||
507 | static int m88rs2000_read_snr(struct dvb_frontend *fe, u16 *snr) | |
508 | { | |
38f7889c IL |
509 | struct m88rs2000_state *state = fe->demodulator_priv; |
510 | ||
b858c331 | 511 | *snr = 512 * m88rs2000_readreg(state, 0x65); |
38f7889c | 512 | |
ae8dc8ee MP |
513 | return 0; |
514 | } | |
515 | ||
516 | static int m88rs2000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) | |
517 | { | |
38f7889c IL |
518 | struct m88rs2000_state *state = fe->demodulator_priv; |
519 | u8 tmp; | |
520 | ||
b858c331 IL |
521 | *ucblocks = (m88rs2000_readreg(state, 0xd5) << 8) | |
522 | m88rs2000_readreg(state, 0xd4); | |
523 | tmp = m88rs2000_readreg(state, 0xd8); | |
524 | m88rs2000_writereg(state, 0xd8, tmp & ~0x20); | |
38f7889c | 525 | /* needs two times */ |
b858c331 IL |
526 | m88rs2000_writereg(state, 0xd8, tmp | 0x20); |
527 | m88rs2000_writereg(state, 0xd8, tmp | 0x20); | |
38f7889c | 528 | |
ae8dc8ee MP |
529 | return 0; |
530 | } | |
531 | ||
ae8dc8ee | 532 | static int m88rs2000_set_fec(struct m88rs2000_state *state, |
0df289a2 | 533 | enum fe_code_rate fec) |
ae8dc8ee | 534 | { |
49c44802 MP |
535 | u8 fec_set, reg; |
536 | int ret; | |
537 | ||
ae8dc8ee | 538 | switch (fec) { |
49c44802 MP |
539 | case FEC_1_2: |
540 | fec_set = 0x8; | |
ae8dc8ee MP |
541 | break; |
542 | case FEC_2_3: | |
49c44802 | 543 | fec_set = 0x10; |
ae8dc8ee MP |
544 | break; |
545 | case FEC_3_4: | |
49c44802 | 546 | fec_set = 0x20; |
ae8dc8ee MP |
547 | break; |
548 | case FEC_5_6: | |
49c44802 | 549 | fec_set = 0x40; |
ae8dc8ee MP |
550 | break; |
551 | case FEC_7_8: | |
49c44802 MP |
552 | fec_set = 0x80; |
553 | break; | |
ae8dc8ee MP |
554 | case FEC_AUTO: |
555 | default: | |
49c44802 | 556 | fec_set = 0x0; |
ae8dc8ee | 557 | } |
ae8dc8ee | 558 | |
49c44802 MP |
559 | reg = m88rs2000_readreg(state, 0x70); |
560 | reg &= 0x7; | |
561 | ret = m88rs2000_writereg(state, 0x70, reg | fec_set); | |
ae8dc8ee | 562 | |
49c44802 MP |
563 | ret |= m88rs2000_writereg(state, 0x76, 0x8); |
564 | ||
565 | return ret; | |
566 | } | |
ae8dc8ee | 567 | |
0df289a2 | 568 | static enum fe_code_rate m88rs2000_get_fec(struct m88rs2000_state *state) |
ae8dc8ee MP |
569 | { |
570 | u8 reg; | |
b858c331 IL |
571 | m88rs2000_writereg(state, 0x9a, 0x30); |
572 | reg = m88rs2000_readreg(state, 0x76); | |
573 | m88rs2000_writereg(state, 0x9a, 0xb0); | |
ae8dc8ee | 574 | |
a6d8e68b MP |
575 | reg &= 0xf0; |
576 | reg >>= 5; | |
577 | ||
ae8dc8ee | 578 | switch (reg) { |
a6d8e68b | 579 | case 0x4: |
ae8dc8ee | 580 | return FEC_1_2; |
a6d8e68b | 581 | case 0x3: |
ae8dc8ee | 582 | return FEC_2_3; |
a6d8e68b | 583 | case 0x2: |
ae8dc8ee | 584 | return FEC_3_4; |
a6d8e68b | 585 | case 0x1: |
ae8dc8ee | 586 | return FEC_5_6; |
a6d8e68b | 587 | case 0x0: |
ae8dc8ee | 588 | return FEC_7_8; |
ae8dc8ee MP |
589 | default: |
590 | break; | |
591 | } | |
592 | ||
593 | return FEC_AUTO; | |
594 | } | |
595 | ||
596 | static int m88rs2000_set_frontend(struct dvb_frontend *fe) | |
597 | { | |
598 | struct m88rs2000_state *state = fe->demodulator_priv; | |
599 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; | |
cac1c639 | 600 | enum fe_status status = 0; |
b858c331 | 601 | int i, ret = 0; |
b858c331 | 602 | u32 tuner_freq; |
06af15d1 | 603 | s16 offset = 0; |
ae8dc8ee MP |
604 | u8 reg; |
605 | ||
606 | state->no_lock_count = 0; | |
607 | ||
608 | if (c->delivery_system != SYS_DVBS) { | |
4bd69e7b MCC |
609 | deb_info("%s: unsupported delivery system selected (%d)\n", |
610 | __func__, c->delivery_system); | |
611 | return -EOPNOTSUPP; | |
ae8dc8ee MP |
612 | } |
613 | ||
614 | /* Set Tuner */ | |
b858c331 IL |
615 | if (fe->ops.tuner_ops.set_params) |
616 | ret = fe->ops.tuner_ops.set_params(fe); | |
617 | ||
618 | if (ret < 0) | |
619 | return -ENODEV; | |
620 | ||
74a6799c | 621 | if (fe->ops.tuner_ops.get_frequency) { |
b858c331 IL |
622 | ret = fe->ops.tuner_ops.get_frequency(fe, &tuner_freq); |
623 | ||
74a6799c MCC |
624 | if (ret < 0) |
625 | return -ENODEV; | |
ae8dc8ee | 626 | |
74a6799c MCC |
627 | offset = (s16)((s32)tuner_freq - c->frequency); |
628 | } else { | |
629 | offset = 0; | |
630 | } | |
b858c331 | 631 | |
06af15d1 MP |
632 | /* default mclk value 96.4285 * 2 * 1000 = 192857 */ |
633 | if (((c->frequency % 192857) >= (192857 - 3000)) || | |
634 | (c->frequency % 192857) <= 3000) | |
635 | ret = m88rs2000_writereg(state, 0x86, 0xc2); | |
636 | else | |
637 | ret = m88rs2000_writereg(state, 0x86, 0xc6); | |
ae8dc8ee | 638 | |
06af15d1 MP |
639 | ret |= m88rs2000_set_carrieroffset(fe, offset); |
640 | if (ret < 0) | |
641 | return -ENODEV; | |
ae8dc8ee | 642 | |
dd4491df MP |
643 | /* Reset demod by symbol rate */ |
644 | if (c->symbol_rate > 27500000) | |
645 | ret = m88rs2000_writereg(state, 0xf1, 0xa4); | |
646 | else | |
647 | ret = m88rs2000_writereg(state, 0xf1, 0xbf); | |
648 | ||
649 | ret |= m88rs2000_tab_set(state, fe_reset); | |
ae8dc8ee MP |
650 | if (ret < 0) |
651 | return -ENODEV; | |
652 | ||
ae8dc8ee | 653 | /* Set FEC */ |
49c44802 | 654 | ret = m88rs2000_set_fec(state, c->fec_inner); |
b858c331 IL |
655 | ret |= m88rs2000_writereg(state, 0x85, 0x1); |
656 | ret |= m88rs2000_writereg(state, 0x8a, 0xbf); | |
657 | ret |= m88rs2000_writereg(state, 0x8d, 0x1e); | |
658 | ret |= m88rs2000_writereg(state, 0x90, 0xf1); | |
659 | ret |= m88rs2000_writereg(state, 0x91, 0x08); | |
ae8dc8ee MP |
660 | |
661 | if (ret < 0) | |
662 | return -ENODEV; | |
663 | ||
664 | /* Set Symbol Rate */ | |
665 | ret = m88rs2000_set_symbolrate(fe, c->symbol_rate); | |
666 | if (ret < 0) | |
667 | return -ENODEV; | |
668 | ||
669 | /* Set up Demod */ | |
670 | ret = m88rs2000_tab_set(state, fe_trigger); | |
671 | if (ret < 0) | |
672 | return -ENODEV; | |
673 | ||
674 | for (i = 0; i < 25; i++) { | |
b858c331 | 675 | reg = m88rs2000_readreg(state, 0x8c); |
7a9d6b43 | 676 | if ((reg & 0xee) == 0xee) { |
ae8dc8ee MP |
677 | status = FE_HAS_LOCK; |
678 | break; | |
679 | } | |
680 | state->no_lock_count++; | |
e58c11f2 | 681 | if (state->no_lock_count == 15) { |
b858c331 | 682 | reg = m88rs2000_readreg(state, 0x70); |
ae8dc8ee | 683 | reg ^= 0x4; |
b858c331 | 684 | m88rs2000_writereg(state, 0x70, reg); |
ae8dc8ee MP |
685 | state->no_lock_count = 0; |
686 | } | |
ae8dc8ee MP |
687 | msleep(20); |
688 | } | |
689 | ||
690 | if (status & FE_HAS_LOCK) { | |
691 | state->fec_inner = m88rs2000_get_fec(state); | |
868c9a17 | 692 | /* Unknown suspect SNR level */ |
b858c331 | 693 | reg = m88rs2000_readreg(state, 0x65); |
ae8dc8ee MP |
694 | } |
695 | ||
696 | state->tuner_frequency = c->frequency; | |
697 | state->symbol_rate = c->symbol_rate; | |
698 | return 0; | |
699 | } | |
700 | ||
7e3e68bc MCC |
701 | static int m88rs2000_get_frontend(struct dvb_frontend *fe, |
702 | struct dtv_frontend_properties *c) | |
ae8dc8ee | 703 | { |
ae8dc8ee | 704 | struct m88rs2000_state *state = fe->demodulator_priv; |
7e3e68bc | 705 | |
ae8dc8ee MP |
706 | c->fec_inner = state->fec_inner; |
707 | c->frequency = state->tuner_frequency; | |
708 | c->symbol_rate = state->symbol_rate; | |
709 | return 0; | |
710 | } | |
711 | ||
8272d0a0 MP |
712 | static int m88rs2000_get_tune_settings(struct dvb_frontend *fe, |
713 | struct dvb_frontend_tune_settings *tune) | |
714 | { | |
715 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; | |
716 | ||
717 | if (c->symbol_rate > 3000000) | |
718 | tune->min_delay_ms = 2000; | |
719 | else | |
720 | tune->min_delay_ms = 3000; | |
721 | ||
722 | tune->step_size = c->symbol_rate / 16000; | |
723 | tune->max_drift = c->symbol_rate / 2000; | |
724 | ||
725 | return 0; | |
726 | } | |
727 | ||
ae8dc8ee MP |
728 | static int m88rs2000_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) |
729 | { | |
730 | struct m88rs2000_state *state = fe->demodulator_priv; | |
731 | ||
732 | if (enable) | |
b858c331 | 733 | m88rs2000_writereg(state, 0x81, 0x84); |
ae8dc8ee | 734 | else |
b858c331 | 735 | m88rs2000_writereg(state, 0x81, 0x81); |
ae8dc8ee MP |
736 | udelay(10); |
737 | return 0; | |
738 | } | |
739 | ||
740 | static void m88rs2000_release(struct dvb_frontend *fe) | |
741 | { | |
742 | struct m88rs2000_state *state = fe->demodulator_priv; | |
743 | kfree(state); | |
744 | } | |
745 | ||
bd336e63 | 746 | static const struct dvb_frontend_ops m88rs2000_ops = { |
ae8dc8ee MP |
747 | .delsys = { SYS_DVBS }, |
748 | .info = { | |
749 | .name = "M88RS2000 DVB-S", | |
f1b1eabf MCC |
750 | .frequency_min_hz = 950 * MHz, |
751 | .frequency_max_hz = 2150 * MHz, | |
752 | .frequency_stepsize_hz = 1 * MHz, | |
753 | .frequency_tolerance_hz = 5 * MHz, | |
ae8dc8ee MP |
754 | .symbol_rate_min = 1000000, |
755 | .symbol_rate_max = 45000000, | |
756 | .symbol_rate_tolerance = 500, /* ppm */ | |
757 | .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | | |
758 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | | |
3c8023a7 | 759 | FE_CAN_QPSK | FE_CAN_INVERSION_AUTO | |
ae8dc8ee MP |
760 | FE_CAN_FEC_AUTO |
761 | }, | |
762 | ||
763 | .release = m88rs2000_release, | |
764 | .init = m88rs2000_init, | |
765 | .sleep = m88rs2000_sleep, | |
ae8dc8ee MP |
766 | .i2c_gate_ctrl = m88rs2000_i2c_gate_ctrl, |
767 | .read_status = m88rs2000_read_status, | |
768 | .read_ber = m88rs2000_read_ber, | |
769 | .read_signal_strength = m88rs2000_read_signal_strength, | |
770 | .read_snr = m88rs2000_read_snr, | |
771 | .read_ucblocks = m88rs2000_read_ucblocks, | |
772 | .diseqc_send_master_cmd = m88rs2000_send_diseqc_msg, | |
773 | .diseqc_send_burst = m88rs2000_send_diseqc_burst, | |
774 | .set_tone = m88rs2000_set_tone, | |
775 | .set_voltage = m88rs2000_set_voltage, | |
776 | ||
777 | .set_frontend = m88rs2000_set_frontend, | |
778 | .get_frontend = m88rs2000_get_frontend, | |
8272d0a0 | 779 | .get_tune_settings = m88rs2000_get_tune_settings, |
ae8dc8ee MP |
780 | }; |
781 | ||
782 | struct dvb_frontend *m88rs2000_attach(const struct m88rs2000_config *config, | |
783 | struct i2c_adapter *i2c) | |
784 | { | |
785 | struct m88rs2000_state *state = NULL; | |
786 | ||
787 | /* allocate memory for the internal state */ | |
788 | state = kzalloc(sizeof(struct m88rs2000_state), GFP_KERNEL); | |
789 | if (state == NULL) | |
790 | goto error; | |
791 | ||
792 | /* setup the state */ | |
793 | state->config = config; | |
794 | state->i2c = i2c; | |
795 | state->tuner_frequency = 0; | |
796 | state->symbol_rate = 0; | |
797 | state->fec_inner = 0; | |
798 | ||
ae8dc8ee MP |
799 | /* create dvb_frontend */ |
800 | memcpy(&state->frontend.ops, &m88rs2000_ops, | |
801 | sizeof(struct dvb_frontend_ops)); | |
802 | state->frontend.demodulator_priv = state; | |
803 | return &state->frontend; | |
804 | ||
805 | error: | |
806 | kfree(state); | |
807 | ||
808 | return NULL; | |
809 | } | |
810 | EXPORT_SYMBOL(m88rs2000_attach); | |
811 | ||
812 | MODULE_DESCRIPTION("M88RS2000 DVB-S Demodulator driver"); | |
813 | MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com"); | |
814 | MODULE_LICENSE("GPL"); | |
593a2ce0 | 815 | MODULE_VERSION("1.13"); |
ae8dc8ee | 816 |