1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | Driver for Philips TDA8083 based QPSK Demodulator |
4 | |
5 | Copyright (C) 2001 Convergence Integrated Media GmbH |
6 | |
7 | written by Ralph Metzler <ralph@convergence.de> |
8 | |
9 | adoption to the new DVB frontend API and diagnostic ioctl's |
10 | by Holger Waechtler <holger@convergence.de> |
11 | |
12 | |
13 | */ |
14 | |
15 | #include <linux/init.h> |
16 | #include <linux/kernel.h> |
17 | #include <linux/module.h> |
18 | #include <linux/string.h> |
19 | #include <linux/slab.h> |
20 | #include <linux/jiffies.h> |
21 | #include <media/dvb_frontend.h> |
22 | #include "tda8083.h" |
23 | |
24 | |
25 | struct tda8083_state { |
26 | struct i2c_adapter* i2c; |
27 | /* configuration settings */ |
28 | const struct tda8083_config* config; |
29 | struct dvb_frontend frontend; |
30 | }; |
31 | |
32 | static int debug; |
33 | #define dprintk(args...) \ |
34 | do { \ |
35 | if (debug) printk(KERN_DEBUG "tda8083: " args); \ |
36 | } while (0) |
37 | |
38 | |
39 | static u8 tda8083_init_tab [] = { |
40 | 0x04, 0x00, 0x4a, 0x79, 0x04, 0x00, 0xff, 0xea, |
41 | 0x48, 0x42, 0x79, 0x60, 0x70, 0x52, 0x9a, 0x10, |
42 | 0x0e, 0x10, 0xf2, 0xa7, 0x93, 0x0b, 0x05, 0xc8, |
43 | 0x9d, 0x00, 0x42, 0x80, 0x00, 0x60, 0x40, 0x00, |
44 | 0x00, 0x75, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, |
45 | 0x00, 0x00, 0x00, 0x00 |
46 | }; |
47 | |
48 | |
49 | static int tda8083_writereg (struct tda8083_state* state, u8 reg, u8 data) |
50 | { |
51 | int ret; |
52 | u8 buf [] = { reg, data }; |
53 | struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; |
54 | |
55 | ret = i2c_transfer(adap: state->i2c, msgs: &msg, num: 1); |
56 | |
57 | if (ret != 1) |
58 | dprintk ("%s: writereg error (reg %02x, ret == %i)\n" , |
59 | __func__, reg, ret); |
60 | |
61 | return (ret != 1) ? -1 : 0; |
62 | } |
63 | |
64 | static int tda8083_readregs (struct tda8083_state* state, u8 reg1, u8 *b, u8 len) |
65 | { |
66 | int ret; |
67 | struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = ®1, .len = 1 }, |
68 | { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b, .len = len } }; |
69 | |
70 | ret = i2c_transfer(adap: state->i2c, msgs: msg, num: 2); |
71 | |
72 | if (ret != 2) |
73 | dprintk ("%s: readreg error (reg %02x, ret == %i)\n" , |
74 | __func__, reg1, ret); |
75 | |
76 | return ret == 2 ? 0 : -1; |
77 | } |
78 | |
79 | static inline u8 tda8083_readreg (struct tda8083_state* state, u8 reg) |
80 | { |
81 | u8 val; |
82 | |
83 | tda8083_readregs (state, reg1: reg, b: &val, len: 1); |
84 | |
85 | return val; |
86 | } |
87 | |
88 | static int tda8083_set_inversion(struct tda8083_state *state, |
89 | enum fe_spectral_inversion inversion) |
90 | { |
91 | /* XXX FIXME: implement other modes than FEC_AUTO */ |
92 | if (inversion == INVERSION_AUTO) |
93 | return 0; |
94 | |
95 | return -EINVAL; |
96 | } |
97 | |
98 | static int tda8083_set_fec(struct tda8083_state *state, enum fe_code_rate fec) |
99 | { |
100 | if (fec == FEC_AUTO) |
101 | return tda8083_writereg (state, reg: 0x07, data: 0xff); |
102 | |
103 | if (fec >= FEC_1_2 && fec <= FEC_8_9) |
104 | return tda8083_writereg (state, reg: 0x07, data: 1 << (FEC_8_9 - fec)); |
105 | |
106 | return -EINVAL; |
107 | } |
108 | |
109 | static enum fe_code_rate tda8083_get_fec(struct tda8083_state *state) |
110 | { |
111 | u8 index; |
112 | static enum fe_code_rate fec_tab[] = { |
113 | FEC_8_9, FEC_1_2, FEC_2_3, FEC_3_4, |
114 | FEC_4_5, FEC_5_6, FEC_6_7, FEC_7_8 |
115 | }; |
116 | |
117 | index = tda8083_readreg(state, reg: 0x0e) & 0x07; |
118 | |
119 | return fec_tab [index]; |
120 | } |
121 | |
122 | static int tda8083_set_symbolrate (struct tda8083_state* state, u32 srate) |
123 | { |
124 | u32 ratio; |
125 | u32 tmp; |
126 | u8 filter; |
127 | |
128 | if (srate > 32000000) |
129 | srate = 32000000; |
130 | if (srate < 500000) |
131 | srate = 500000; |
132 | |
133 | filter = 0; |
134 | if (srate < 24000000) |
135 | filter = 2; |
136 | if (srate < 16000000) |
137 | filter = 3; |
138 | |
139 | tmp = 31250 << 16; |
140 | ratio = tmp / srate; |
141 | |
142 | tmp = (tmp % srate) << 8; |
143 | ratio = (ratio << 8) + tmp / srate; |
144 | |
145 | tmp = (tmp % srate) << 8; |
146 | ratio = (ratio << 8) + tmp / srate; |
147 | |
148 | dprintk("tda8083: ratio == %08x\n" , (unsigned int) ratio); |
149 | |
150 | tda8083_writereg (state, reg: 0x05, data: filter); |
151 | tda8083_writereg (state, reg: 0x02, data: (ratio >> 16) & 0xff); |
152 | tda8083_writereg (state, reg: 0x03, data: (ratio >> 8) & 0xff); |
153 | tda8083_writereg (state, reg: 0x04, data: (ratio ) & 0xff); |
154 | |
155 | tda8083_writereg (state, reg: 0x00, data: 0x3c); |
156 | tda8083_writereg (state, reg: 0x00, data: 0x04); |
157 | |
158 | return 1; |
159 | } |
160 | |
161 | static void tda8083_wait_diseqc_fifo (struct tda8083_state* state, int timeout) |
162 | { |
163 | unsigned long start = jiffies; |
164 | |
165 | while (time_is_after_jiffies(start + timeout) && |
166 | !(tda8083_readreg(state, reg: 0x02) & 0x80)) |
167 | { |
168 | msleep(msecs: 50); |
169 | } |
170 | } |
171 | |
172 | static int tda8083_set_tone(struct tda8083_state *state, |
173 | enum fe_sec_tone_mode tone) |
174 | { |
175 | tda8083_writereg (state, reg: 0x26, data: 0xf1); |
176 | |
177 | switch (tone) { |
178 | case SEC_TONE_OFF: |
179 | return tda8083_writereg (state, reg: 0x29, data: 0x00); |
180 | case SEC_TONE_ON: |
181 | return tda8083_writereg (state, reg: 0x29, data: 0x80); |
182 | default: |
183 | return -EINVAL; |
184 | } |
185 | } |
186 | |
187 | static int tda8083_set_voltage(struct tda8083_state *state, |
188 | enum fe_sec_voltage voltage) |
189 | { |
190 | switch (voltage) { |
191 | case SEC_VOLTAGE_13: |
192 | return tda8083_writereg (state, reg: 0x20, data: 0x00); |
193 | case SEC_VOLTAGE_18: |
194 | return tda8083_writereg (state, reg: 0x20, data: 0x11); |
195 | default: |
196 | return -EINVAL; |
197 | } |
198 | } |
199 | |
200 | static int tda8083_send_diseqc_burst(struct tda8083_state *state, |
201 | enum fe_sec_mini_cmd burst) |
202 | { |
203 | switch (burst) { |
204 | case SEC_MINI_A: |
205 | tda8083_writereg (state, reg: 0x29, data: (5 << 2)); /* send burst A */ |
206 | break; |
207 | case SEC_MINI_B: |
208 | tda8083_writereg (state, reg: 0x29, data: (7 << 2)); /* send B */ |
209 | break; |
210 | default: |
211 | return -EINVAL; |
212 | } |
213 | |
214 | tda8083_wait_diseqc_fifo (state, timeout: 100); |
215 | |
216 | return 0; |
217 | } |
218 | |
219 | static int tda8083_send_diseqc_msg(struct dvb_frontend *fe, |
220 | struct dvb_diseqc_master_cmd *m) |
221 | { |
222 | struct tda8083_state* state = fe->demodulator_priv; |
223 | int i; |
224 | |
225 | tda8083_writereg (state, reg: 0x29, data: (m->msg_len - 3) | (1 << 2)); /* enable */ |
226 | |
227 | for (i=0; i<m->msg_len; i++) |
228 | tda8083_writereg (state, reg: 0x23 + i, data: m->msg[i]); |
229 | |
230 | tda8083_writereg (state, reg: 0x29, data: (m->msg_len - 3) | (3 << 2)); /* send!! */ |
231 | |
232 | tda8083_wait_diseqc_fifo (state, timeout: 100); |
233 | |
234 | return 0; |
235 | } |
236 | |
237 | static int tda8083_read_status(struct dvb_frontend *fe, |
238 | enum fe_status *status) |
239 | { |
240 | struct tda8083_state* state = fe->demodulator_priv; |
241 | |
242 | u8 signal = ~tda8083_readreg (state, reg: 0x01); |
243 | u8 sync = tda8083_readreg (state, reg: 0x02); |
244 | |
245 | *status = 0; |
246 | |
247 | if (signal > 10) |
248 | *status |= FE_HAS_SIGNAL; |
249 | |
250 | if (sync & 0x01) |
251 | *status |= FE_HAS_CARRIER; |
252 | |
253 | if (sync & 0x02) |
254 | *status |= FE_HAS_VITERBI; |
255 | |
256 | if (sync & 0x10) |
257 | *status |= FE_HAS_SYNC; |
258 | |
259 | if (sync & 0x20) /* frontend can not lock */ |
260 | *status |= FE_TIMEDOUT; |
261 | |
262 | if ((sync & 0x1f) == 0x1f) |
263 | *status |= FE_HAS_LOCK; |
264 | |
265 | return 0; |
266 | } |
267 | |
268 | static int tda8083_read_ber(struct dvb_frontend* fe, u32* ber) |
269 | { |
270 | struct tda8083_state* state = fe->demodulator_priv; |
271 | int ret; |
272 | u8 buf[3]; |
273 | |
274 | if ((ret = tda8083_readregs(state, reg1: 0x0b, b: buf, len: sizeof(buf)))) |
275 | return ret; |
276 | |
277 | *ber = ((buf[0] & 0x1f) << 16) | (buf[1] << 8) | buf[2]; |
278 | |
279 | return 0; |
280 | } |
281 | |
282 | static int tda8083_read_signal_strength(struct dvb_frontend* fe, u16* strength) |
283 | { |
284 | struct tda8083_state* state = fe->demodulator_priv; |
285 | |
286 | u8 signal = ~tda8083_readreg (state, reg: 0x01); |
287 | *strength = (signal << 8) | signal; |
288 | |
289 | return 0; |
290 | } |
291 | |
292 | static int tda8083_read_snr(struct dvb_frontend* fe, u16* snr) |
293 | { |
294 | struct tda8083_state* state = fe->demodulator_priv; |
295 | |
296 | u8 _snr = tda8083_readreg (state, reg: 0x08); |
297 | *snr = (_snr << 8) | _snr; |
298 | |
299 | return 0; |
300 | } |
301 | |
302 | static int tda8083_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) |
303 | { |
304 | struct tda8083_state* state = fe->demodulator_priv; |
305 | |
306 | *ucblocks = tda8083_readreg(state, reg: 0x0f); |
307 | if (*ucblocks == 0xff) |
308 | *ucblocks = 0xffffffff; |
309 | |
310 | return 0; |
311 | } |
312 | |
313 | static int tda8083_set_frontend(struct dvb_frontend *fe) |
314 | { |
315 | struct dtv_frontend_properties *p = &fe->dtv_property_cache; |
316 | struct tda8083_state* state = fe->demodulator_priv; |
317 | |
318 | if (fe->ops.tuner_ops.set_params) { |
319 | fe->ops.tuner_ops.set_params(fe); |
320 | if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); |
321 | } |
322 | |
323 | tda8083_set_inversion (state, inversion: p->inversion); |
324 | tda8083_set_fec(state, fec: p->fec_inner); |
325 | tda8083_set_symbolrate(state, srate: p->symbol_rate); |
326 | |
327 | tda8083_writereg (state, reg: 0x00, data: 0x3c); |
328 | tda8083_writereg (state, reg: 0x00, data: 0x04); |
329 | |
330 | return 0; |
331 | } |
332 | |
333 | static int tda8083_get_frontend(struct dvb_frontend *fe, |
334 | struct dtv_frontend_properties *p) |
335 | { |
336 | struct tda8083_state* state = fe->demodulator_priv; |
337 | |
338 | /* FIXME: get symbolrate & frequency offset...*/ |
339 | /*p->frequency = ???;*/ |
340 | p->inversion = (tda8083_readreg (state, reg: 0x0e) & 0x80) ? |
341 | INVERSION_ON : INVERSION_OFF; |
342 | p->fec_inner = tda8083_get_fec(state); |
343 | /*p->symbol_rate = tda8083_get_symbolrate (state);*/ |
344 | |
345 | return 0; |
346 | } |
347 | |
348 | static int tda8083_sleep(struct dvb_frontend* fe) |
349 | { |
350 | struct tda8083_state* state = fe->demodulator_priv; |
351 | |
352 | tda8083_writereg (state, reg: 0x00, data: 0x02); |
353 | return 0; |
354 | } |
355 | |
356 | static int tda8083_init(struct dvb_frontend* fe) |
357 | { |
358 | struct tda8083_state* state = fe->demodulator_priv; |
359 | int i; |
360 | |
361 | for (i=0; i<44; i++) |
362 | tda8083_writereg (state, reg: i, data: tda8083_init_tab[i]); |
363 | |
364 | tda8083_writereg (state, reg: 0x00, data: 0x3c); |
365 | tda8083_writereg (state, reg: 0x00, data: 0x04); |
366 | |
367 | return 0; |
368 | } |
369 | |
370 | static int tda8083_diseqc_send_burst(struct dvb_frontend *fe, |
371 | enum fe_sec_mini_cmd burst) |
372 | { |
373 | struct tda8083_state* state = fe->demodulator_priv; |
374 | |
375 | tda8083_send_diseqc_burst (state, burst); |
376 | tda8083_writereg (state, reg: 0x00, data: 0x3c); |
377 | tda8083_writereg (state, reg: 0x00, data: 0x04); |
378 | |
379 | return 0; |
380 | } |
381 | |
382 | static int tda8083_diseqc_set_tone(struct dvb_frontend *fe, |
383 | enum fe_sec_tone_mode tone) |
384 | { |
385 | struct tda8083_state* state = fe->demodulator_priv; |
386 | |
387 | tda8083_set_tone (state, tone); |
388 | tda8083_writereg (state, reg: 0x00, data: 0x3c); |
389 | tda8083_writereg (state, reg: 0x00, data: 0x04); |
390 | |
391 | return 0; |
392 | } |
393 | |
394 | static int tda8083_diseqc_set_voltage(struct dvb_frontend *fe, |
395 | enum fe_sec_voltage voltage) |
396 | { |
397 | struct tda8083_state* state = fe->demodulator_priv; |
398 | |
399 | tda8083_set_voltage (state, voltage); |
400 | tda8083_writereg (state, reg: 0x00, data: 0x3c); |
401 | tda8083_writereg (state, reg: 0x00, data: 0x04); |
402 | |
403 | return 0; |
404 | } |
405 | |
406 | static void tda8083_release(struct dvb_frontend* fe) |
407 | { |
408 | struct tda8083_state* state = fe->demodulator_priv; |
409 | kfree(objp: state); |
410 | } |
411 | |
412 | static const struct dvb_frontend_ops tda8083_ops; |
413 | |
414 | struct dvb_frontend* tda8083_attach(const struct tda8083_config* config, |
415 | struct i2c_adapter* i2c) |
416 | { |
417 | struct tda8083_state* state = NULL; |
418 | |
419 | /* allocate memory for the internal state */ |
420 | state = kzalloc(size: sizeof(struct tda8083_state), GFP_KERNEL); |
421 | if (state == NULL) goto error; |
422 | |
423 | /* setup the state */ |
424 | state->config = config; |
425 | state->i2c = i2c; |
426 | |
427 | /* check if the demod is there */ |
428 | if ((tda8083_readreg(state, reg: 0x00)) != 0x05) goto error; |
429 | |
430 | /* create dvb_frontend */ |
431 | memcpy(&state->frontend.ops, &tda8083_ops, sizeof(struct dvb_frontend_ops)); |
432 | state->frontend.demodulator_priv = state; |
433 | return &state->frontend; |
434 | |
435 | error: |
436 | kfree(objp: state); |
437 | return NULL; |
438 | } |
439 | |
440 | static const struct dvb_frontend_ops tda8083_ops = { |
441 | .delsys = { SYS_DVBS }, |
442 | .info = { |
443 | .name = "Philips TDA8083 DVB-S" , |
444 | .frequency_min_hz = 920 * MHz, /* TDA8060 */ |
445 | .frequency_max_hz = 2200 * MHz, /* TDA8060 */ |
446 | .frequency_stepsize_hz = 125 * kHz, |
447 | .symbol_rate_min = 12000000, |
448 | .symbol_rate_max = 30000000, |
449 | /* .symbol_rate_tolerance = ???,*/ |
450 | .caps = FE_CAN_INVERSION_AUTO | |
451 | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | |
452 | FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | |
453 | FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO | |
454 | FE_CAN_QPSK | FE_CAN_MUTE_TS |
455 | }, |
456 | |
457 | .release = tda8083_release, |
458 | |
459 | .init = tda8083_init, |
460 | .sleep = tda8083_sleep, |
461 | |
462 | .set_frontend = tda8083_set_frontend, |
463 | .get_frontend = tda8083_get_frontend, |
464 | |
465 | .read_status = tda8083_read_status, |
466 | .read_signal_strength = tda8083_read_signal_strength, |
467 | .read_snr = tda8083_read_snr, |
468 | .read_ber = tda8083_read_ber, |
469 | .read_ucblocks = tda8083_read_ucblocks, |
470 | |
471 | .diseqc_send_master_cmd = tda8083_send_diseqc_msg, |
472 | .diseqc_send_burst = tda8083_diseqc_send_burst, |
473 | .set_tone = tda8083_diseqc_set_tone, |
474 | .set_voltage = tda8083_diseqc_set_voltage, |
475 | }; |
476 | |
477 | module_param(debug, int, 0644); |
478 | MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)." ); |
479 | |
480 | MODULE_DESCRIPTION("Philips TDA8083 DVB-S Demodulator" ); |
481 | MODULE_AUTHOR("Ralph Metzler, Holger Waechtler" ); |
482 | MODULE_LICENSE("GPL" ); |
483 | |
484 | EXPORT_SYMBOL_GPL(tda8083_attach); |
485 | |