Commit | Line | Data |
---|---|---|
c942fddf | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
5afc9a25 JD |
2 | /* |
3 | Conexant cx24120/cx24118 - DVBS/S2 Satellite demod/tuner driver | |
4 | ||
5 | Copyright (C) 2008 Patrick Boettcher <pb@linuxtv.org> | |
6 | Copyright (C) 2009 Sergey Tyurin <forum.free-x.de> | |
7 | Updated 2012 by Jannis Achstetter <jannis_achstetter@web.de> | |
8 | Copyright (C) 2015 Jemma Denson <jdenson@gmail.com> | |
9 | April 2015 | |
10 | Refactored & simplified driver | |
11 | Updated to work with delivery system supplied by DVBv5 | |
12 | Add frequency, fec & pilot to get_frontend | |
13 | ||
14 | Cards supported: Technisat Skystar S2 | |
15 | ||
5afc9a25 JD |
16 | */ |
17 | ||
18 | #include <linux/slab.h> | |
19 | #include <linux/kernel.h> | |
20 | #include <linux/module.h> | |
21 | #include <linux/moduleparam.h> | |
22 | #include <linux/init.h> | |
23 | #include <linux/firmware.h> | |
fada1935 | 24 | #include <media/dvb_frontend.h> |
5afc9a25 JD |
25 | #include "cx24120.h" |
26 | ||
27 | #define CX24120_SEARCH_RANGE_KHZ 5000 | |
28 | #define CX24120_FIRMWARE "dvb-fe-cx24120-1.20.58.2.fw" | |
29 | ||
30 | /* cx24120 i2c registers */ | |
2e89a5e0 PB |
31 | #define CX24120_REG_CMD_START 0x00 /* write cmd_id */ |
32 | #define CX24120_REG_CMD_ARGS 0x01 /* write command arguments */ | |
33 | #define CX24120_REG_CMD_END 0x1f /* write 0x01 for end */ | |
5afc9a25 | 34 | |
2e89a5e0 PB |
35 | #define CX24120_REG_MAILBOX 0x33 |
36 | #define CX24120_REG_FREQ3 0x34 /* frequency */ | |
37 | #define CX24120_REG_FREQ2 0x35 | |
38 | #define CX24120_REG_FREQ1 0x36 | |
5afc9a25 | 39 | |
2e89a5e0 PB |
40 | #define CX24120_REG_FECMODE 0x39 /* FEC status */ |
41 | #define CX24120_REG_STATUS 0x3a /* Tuner status */ | |
42 | #define CX24120_REG_SIGSTR_H 0x3a /* Signal strength high */ | |
43 | #define CX24120_REG_SIGSTR_L 0x3b /* Signal strength low byte */ | |
44 | #define CX24120_REG_QUALITY_H 0x40 /* SNR high byte */ | |
45 | #define CX24120_REG_QUALITY_L 0x41 /* SNR low byte */ | |
5afc9a25 | 46 | |
2e89a5e0 PB |
47 | #define CX24120_REG_BER_HH 0x47 /* BER high byte of high word */ |
48 | #define CX24120_REG_BER_HL 0x48 /* BER low byte of high word */ | |
49 | #define CX24120_REG_BER_LH 0x49 /* BER high byte of low word */ | |
50 | #define CX24120_REG_BER_LL 0x4a /* BER low byte of low word */ | |
5afc9a25 | 51 | |
2e89a5e0 PB |
52 | #define CX24120_REG_UCB_H 0x50 /* UCB high byte */ |
53 | #define CX24120_REG_UCB_L 0x51 /* UCB low byte */ | |
5afc9a25 | 54 | |
2e89a5e0 PB |
55 | #define CX24120_REG_CLKDIV 0xe6 |
56 | #define CX24120_REG_RATEDIV 0xf0 | |
5afc9a25 | 57 | |
2e89a5e0 | 58 | #define CX24120_REG_REVISION 0xff /* Chip revision (ro) */ |
5afc9a25 | 59 | |
5afc9a25 JD |
60 | /* Command messages */ |
61 | enum command_message_id { | |
62 | CMD_VCO_SET = 0x10, /* cmd.len = 12; */ | |
63 | CMD_TUNEREQUEST = 0x11, /* cmd.len = 15; */ | |
64 | ||
65 | CMD_MPEG_ONOFF = 0x13, /* cmd.len = 4; */ | |
66 | CMD_MPEG_INIT = 0x14, /* cmd.len = 7; */ | |
67 | CMD_BANDWIDTH = 0x15, /* cmd.len = 12; */ | |
68 | CMD_CLOCK_READ = 0x16, /* read clock */ | |
69 | CMD_CLOCK_SET = 0x17, /* cmd.len = 10; */ | |
70 | ||
71 | CMD_DISEQC_MSG1 = 0x20, /* cmd.len = 11; */ | |
72 | CMD_DISEQC_MSG2 = 0x21, /* cmd.len = d->msg_len + 6; */ | |
73 | CMD_SETVOLTAGE = 0x22, /* cmd.len = 2; */ | |
74 | CMD_SETTONE = 0x23, /* cmd.len = 4; */ | |
75 | CMD_DISEQC_BURST = 0x24, /* cmd.len not used !!! */ | |
76 | ||
77 | CMD_READ_SNR = 0x1a, /* Read signal strength */ | |
78 | CMD_START_TUNER = 0x1b, /* ??? */ | |
79 | ||
80 | CMD_FWVERSION = 0x35, | |
81 | ||
ddcb252e | 82 | CMD_BER_CTRL = 0x3c, /* cmd.len = 0x03; */ |
5afc9a25 JD |
83 | }; |
84 | ||
85 | #define CX24120_MAX_CMD_LEN 30 | |
86 | ||
87 | /* pilot mask */ | |
2e89a5e0 PB |
88 | #define CX24120_PILOT_OFF 0x00 |
89 | #define CX24120_PILOT_ON 0x40 | |
90 | #define CX24120_PILOT_AUTO 0x80 | |
5afc9a25 JD |
91 | |
92 | /* signal status */ | |
2e89a5e0 PB |
93 | #define CX24120_HAS_SIGNAL 0x01 |
94 | #define CX24120_HAS_CARRIER 0x02 | |
95 | #define CX24120_HAS_VITERBI 0x04 | |
96 | #define CX24120_HAS_LOCK 0x08 | |
97 | #define CX24120_HAS_UNK1 0x10 | |
98 | #define CX24120_HAS_UNK2 0x20 | |
99 | #define CX24120_STATUS_MASK 0x0f | |
100 | #define CX24120_SIGNAL_MASK 0xc0 | |
5afc9a25 | 101 | |
ddcb252e JD |
102 | /* ber window */ |
103 | #define CX24120_BER_WINDOW 16 | |
104 | #define CX24120_BER_WSIZE ((1 << CX24120_BER_WINDOW) * 208 * 8) | |
105 | ||
c5fb0f5f PB |
106 | #define info(args...) pr_info("cx24120: " args) |
107 | #define err(args...) pr_err("cx24120: ### ERROR: " args) | |
5afc9a25 JD |
108 | |
109 | /* The Demod/Tuner can't easily provide these, we cache them */ | |
110 | struct cx24120_tuning { | |
111 | u32 frequency; | |
112 | u32 symbol_rate; | |
0df289a2 MCC |
113 | enum fe_spectral_inversion inversion; |
114 | enum fe_code_rate fec; | |
5afc9a25 | 115 | |
0df289a2 MCC |
116 | enum fe_delivery_system delsys; |
117 | enum fe_modulation modulation; | |
118 | enum fe_pilot pilot; | |
5afc9a25 JD |
119 | |
120 | /* Demod values */ | |
121 | u8 fec_val; | |
122 | u8 fec_mask; | |
123 | u8 clkdiv; | |
124 | u8 ratediv; | |
125 | u8 inversion_val; | |
126 | u8 pilot_val; | |
127 | }; | |
128 | ||
5afc9a25 JD |
129 | /* Private state */ |
130 | struct cx24120_state { | |
131 | struct i2c_adapter *i2c; | |
132 | const struct cx24120_config *config; | |
133 | struct dvb_frontend frontend; | |
134 | ||
135 | u8 cold_init; | |
136 | u8 mpeg_enabled; | |
6138dc2f | 137 | u8 need_clock_set; |
5afc9a25 JD |
138 | |
139 | /* current and next tuning parameters */ | |
140 | struct cx24120_tuning dcur; | |
141 | struct cx24120_tuning dnxt; | |
1462612c | 142 | |
0df289a2 | 143 | enum fe_status fe_status; |
ddcb252e | 144 | |
c9adafa3 | 145 | /* dvbv5 stats calculations */ |
80e9710b | 146 | u32 bitrate; |
ddcb252e | 147 | u32 berw_usecs; |
fc443284 | 148 | u32 ber_prev; |
bf8de2d3 | 149 | u32 ucb_offset; |
ddcb252e | 150 | unsigned long ber_jiffies_stats; |
80e9710b | 151 | unsigned long per_jiffies_stats; |
5afc9a25 JD |
152 | }; |
153 | ||
5afc9a25 JD |
154 | /* Command message to firmware */ |
155 | struct cx24120_cmd { | |
156 | u8 id; | |
157 | u8 len; | |
158 | u8 arg[CX24120_MAX_CMD_LEN]; | |
159 | }; | |
160 | ||
5afc9a25 JD |
161 | /* Read single register */ |
162 | static int cx24120_readreg(struct cx24120_state *state, u8 reg) | |
163 | { | |
164 | int ret; | |
165 | u8 buf = 0; | |
166 | struct i2c_msg msg[] = { | |
fbdbab72 JD |
167 | { |
168 | .addr = state->config->i2c_addr, | |
5afc9a25 JD |
169 | .flags = 0, |
170 | .len = 1, | |
2e89a5e0 PB |
171 | .buf = ® |
172 | }, { | |
173 | .addr = state->config->i2c_addr, | |
5afc9a25 JD |
174 | .flags = I2C_M_RD, |
175 | .len = 1, | |
2e89a5e0 PB |
176 | .buf = &buf |
177 | } | |
5afc9a25 | 178 | }; |
2e89a5e0 | 179 | |
5afc9a25 JD |
180 | ret = i2c_transfer(state->i2c, msg, 2); |
181 | if (ret != 2) { | |
2e89a5e0 | 182 | err("Read error: reg=0x%02x, ret=%i)\n", reg, ret); |
5afc9a25 JD |
183 | return ret; |
184 | } | |
185 | ||
2f3f07fb | 186 | dev_dbg(&state->i2c->dev, "reg=0x%02x; data=0x%02x\n", reg, buf); |
5afc9a25 JD |
187 | |
188 | return buf; | |
189 | } | |
190 | ||
5afc9a25 JD |
191 | /* Write single register */ |
192 | static int cx24120_writereg(struct cx24120_state *state, u8 reg, u8 data) | |
193 | { | |
194 | u8 buf[] = { reg, data }; | |
195 | struct i2c_msg msg = { | |
196 | .addr = state->config->i2c_addr, | |
197 | .flags = 0, | |
198 | .buf = buf, | |
2e89a5e0 PB |
199 | .len = 2 |
200 | }; | |
5afc9a25 JD |
201 | int ret; |
202 | ||
203 | ret = i2c_transfer(state->i2c, &msg, 1); | |
204 | if (ret != 1) { | |
205 | err("Write error: i2c_write error(err == %i, 0x%02x: 0x%02x)\n", | |
1ff2e8ed | 206 | ret, reg, data); |
5afc9a25 JD |
207 | return ret; |
208 | } | |
209 | ||
2f3f07fb | 210 | dev_dbg(&state->i2c->dev, "reg=0x%02x; data=0x%02x\n", reg, data); |
5afc9a25 JD |
211 | |
212 | return 0; | |
213 | } | |
214 | ||
f7a77ebf | 215 | /* Write multiple registers in chunks of i2c_wr_max-sized buffers */ |
1ff2e8ed PB |
216 | static int cx24120_writeregs(struct cx24120_state *state, |
217 | u8 reg, const u8 *values, u16 len, u8 incr) | |
5afc9a25 JD |
218 | { |
219 | int ret; | |
f7a77ebf PB |
220 | u16 max = state->config->i2c_wr_max > 0 ? |
221 | state->config->i2c_wr_max : | |
222 | len; | |
5afc9a25 JD |
223 | |
224 | struct i2c_msg msg = { | |
225 | .addr = state->config->i2c_addr, | |
226 | .flags = 0, | |
f7a77ebf PB |
227 | }; |
228 | ||
229 | msg.buf = kmalloc(max + 1, GFP_KERNEL); | |
1ff2e8ed | 230 | if (!msg.buf) |
f7a77ebf | 231 | return -ENOMEM; |
5afc9a25 JD |
232 | |
233 | while (len) { | |
f7a77ebf PB |
234 | msg.buf[0] = reg; |
235 | msg.len = len > max ? max : len; | |
236 | memcpy(&msg.buf[1], values, msg.len); | |
5afc9a25 | 237 | |
f7a77ebf PB |
238 | len -= msg.len; /* data length revers counter */ |
239 | values += msg.len; /* incr data pointer */ | |
5afc9a25 JD |
240 | |
241 | if (incr) | |
242 | reg += msg.len; | |
f7a77ebf | 243 | msg.len++; /* don't forget the addr byte */ |
5afc9a25 JD |
244 | |
245 | ret = i2c_transfer(state->i2c, &msg, 1); | |
246 | if (ret != 1) { | |
247 | err("i2c_write error(err == %i, 0x%02x)\n", ret, reg); | |
f7a77ebf | 248 | goto out; |
5afc9a25 JD |
249 | } |
250 | ||
2f3f07fb JD |
251 | dev_dbg(&state->i2c->dev, "reg=0x%02x; data=%*ph\n", |
252 | reg, msg.len - 1, msg.buf + 1); | |
5afc9a25 JD |
253 | } |
254 | ||
f7a77ebf PB |
255 | ret = 0; |
256 | ||
257 | out: | |
258 | kfree(msg.buf); | |
259 | return ret; | |
5afc9a25 JD |
260 | } |
261 | ||
bd336e63 | 262 | static const struct dvb_frontend_ops cx24120_ops; |
5afc9a25 JD |
263 | |
264 | struct dvb_frontend *cx24120_attach(const struct cx24120_config *config, | |
1ff2e8ed | 265 | struct i2c_adapter *i2c) |
5afc9a25 | 266 | { |
1ff2e8ed | 267 | struct cx24120_state *state; |
5afc9a25 JD |
268 | int demod_rev; |
269 | ||
270 | info("Conexant cx24120/cx24118 - DVBS/S2 Satellite demod/tuner\n"); | |
1ff2e8ed PB |
271 | state = kzalloc(sizeof(*state), GFP_KERNEL); |
272 | if (!state) { | |
5afc9a25 JD |
273 | err("Unable to allocate memory for cx24120_state\n"); |
274 | goto error; | |
275 | } | |
276 | ||
277 | /* setup the state */ | |
278 | state->config = config; | |
279 | state->i2c = i2c; | |
280 | ||
281 | /* check if the demod is present and has proper type */ | |
282 | demod_rev = cx24120_readreg(state, CX24120_REG_REVISION); | |
283 | switch (demod_rev) { | |
284 | case 0x07: | |
285 | info("Demod cx24120 rev. 0x07 detected.\n"); | |
286 | break; | |
287 | case 0x05: | |
288 | info("Demod cx24120 rev. 0x05 detected.\n"); | |
289 | break; | |
290 | default: | |
1ff2e8ed | 291 | err("Unsupported demod revision: 0x%x detected.\n", demod_rev); |
5afc9a25 JD |
292 | goto error; |
293 | } | |
294 | ||
295 | /* create dvb_frontend */ | |
296 | state->cold_init = 0; | |
297 | memcpy(&state->frontend.ops, &cx24120_ops, | |
2e89a5e0 | 298 | sizeof(struct dvb_frontend_ops)); |
5afc9a25 JD |
299 | state->frontend.demodulator_priv = state; |
300 | ||
301 | info("Conexant cx24120/cx24118 attached.\n"); | |
302 | return &state->frontend; | |
303 | ||
304 | error: | |
305 | kfree(state); | |
306 | return NULL; | |
307 | } | |
308 | EXPORT_SYMBOL(cx24120_attach); | |
309 | ||
310 | static int cx24120_test_rom(struct cx24120_state *state) | |
311 | { | |
312 | int err, ret; | |
313 | ||
314 | err = cx24120_readreg(state, 0xfd); | |
315 | if (err & 4) { | |
316 | ret = cx24120_readreg(state, 0xdf) & 0xfe; | |
317 | err = cx24120_writereg(state, 0xdf, ret); | |
318 | } | |
319 | return err; | |
320 | } | |
321 | ||
5afc9a25 JD |
322 | static int cx24120_read_snr(struct dvb_frontend *fe, u16 *snr) |
323 | { | |
3b5eb504 | 324 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
5afc9a25 | 325 | |
3b5eb504 JD |
326 | if (c->cnr.stat[0].scale != FE_SCALE_DECIBEL) |
327 | *snr = 0; | |
328 | else | |
329 | *snr = div_s64(c->cnr.stat[0].svalue, 100); | |
5afc9a25 JD |
330 | |
331 | return 0; | |
332 | } | |
333 | ||
5afc9a25 JD |
334 | static int cx24120_read_ber(struct dvb_frontend *fe, u32 *ber) |
335 | { | |
336 | struct cx24120_state *state = fe->demodulator_priv; | |
fc443284 | 337 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
5afc9a25 | 338 | |
fc443284 JD |
339 | if (c->post_bit_error.stat[0].scale != FE_SCALE_COUNTER) { |
340 | *ber = 0; | |
341 | return 0; | |
342 | } | |
343 | ||
344 | *ber = c->post_bit_error.stat[0].uvalue - state->ber_prev; | |
345 | state->ber_prev = c->post_bit_error.stat[0].uvalue; | |
5afc9a25 JD |
346 | |
347 | return 0; | |
348 | } | |
349 | ||
350 | static int cx24120_msg_mpeg_output_global_config(struct cx24120_state *state, | |
1ff2e8ed | 351 | u8 flag); |
5afc9a25 JD |
352 | |
353 | /* Check if we're running a command that needs to disable mpeg out */ | |
354 | static void cx24120_check_cmd(struct cx24120_state *state, u8 id) | |
355 | { | |
356 | switch (id) { | |
357 | case CMD_TUNEREQUEST: | |
358 | case CMD_CLOCK_READ: | |
359 | case CMD_DISEQC_MSG1: | |
360 | case CMD_DISEQC_MSG2: | |
361 | case CMD_SETVOLTAGE: | |
362 | case CMD_SETTONE: | |
270e7071 | 363 | case CMD_DISEQC_BURST: |
5afc9a25 JD |
364 | cx24120_msg_mpeg_output_global_config(state, 0); |
365 | /* Old driver would do a msleep(100) here */ | |
366 | default: | |
367 | return; | |
368 | } | |
369 | } | |
370 | ||
5afc9a25 JD |
371 | /* Send a message to the firmware */ |
372 | static int cx24120_message_send(struct cx24120_state *state, | |
1ff2e8ed | 373 | struct cx24120_cmd *cmd) |
5afc9a25 | 374 | { |
65b01665 | 375 | int ficus; |
5afc9a25 JD |
376 | |
377 | if (state->mpeg_enabled) { | |
378 | /* Disable mpeg out on certain commands */ | |
379 | cx24120_check_cmd(state, cmd->id); | |
380 | } | |
381 | ||
65b01665 MCC |
382 | cx24120_writereg(state, CX24120_REG_CMD_START, cmd->id); |
383 | cx24120_writeregs(state, CX24120_REG_CMD_ARGS, &cmd->arg[0], | |
384 | cmd->len, 1); | |
385 | cx24120_writereg(state, CX24120_REG_CMD_END, 0x01); | |
5afc9a25 JD |
386 | |
387 | ficus = 1000; | |
388 | while (cx24120_readreg(state, CX24120_REG_CMD_END)) { | |
389 | msleep(20); | |
390 | ficus -= 20; | |
391 | if (ficus == 0) { | |
392 | err("Error sending message to firmware\n"); | |
393 | return -EREMOTEIO; | |
394 | } | |
395 | } | |
2f3f07fb | 396 | dev_dbg(&state->i2c->dev, "sent message 0x%02x\n", cmd->id); |
5afc9a25 JD |
397 | |
398 | return 0; | |
399 | } | |
400 | ||
401 | /* Send a message and fill arg[] with the results */ | |
402 | static int cx24120_message_sendrcv(struct cx24120_state *state, | |
1ff2e8ed | 403 | struct cx24120_cmd *cmd, u8 numreg) |
5afc9a25 JD |
404 | { |
405 | int ret, i; | |
406 | ||
407 | if (numreg > CX24120_MAX_CMD_LEN) { | |
408 | err("Too many registers to read. cmd->reg = %d", numreg); | |
409 | return -EREMOTEIO; | |
410 | } | |
411 | ||
412 | ret = cx24120_message_send(state, cmd); | |
413 | if (ret != 0) | |
414 | return ret; | |
415 | ||
416 | if (!numreg) | |
417 | return 0; | |
418 | ||
419 | /* Read numreg registers starting from register cmd->len */ | |
420 | for (i = 0; i < numreg; i++) | |
1ff2e8ed | 421 | cmd->arg[i] = cx24120_readreg(state, (cmd->len + i + 1)); |
5afc9a25 JD |
422 | |
423 | return 0; | |
424 | } | |
425 | ||
5afc9a25 | 426 | static int cx24120_read_signal_strength(struct dvb_frontend *fe, |
1ff2e8ed | 427 | u16 *signal_strength) |
5afc9a25 | 428 | { |
34ce475d | 429 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
5afc9a25 | 430 | |
34ce475d JD |
431 | if (c->strength.stat[0].scale != FE_SCALE_RELATIVE) |
432 | *signal_strength = 0; | |
433 | else | |
434 | *signal_strength = c->strength.stat[0].uvalue; | |
5afc9a25 | 435 | |
5afc9a25 JD |
436 | return 0; |
437 | } | |
438 | ||
5afc9a25 | 439 | static int cx24120_msg_mpeg_output_global_config(struct cx24120_state *state, |
1ff2e8ed | 440 | u8 enable) |
5afc9a25 JD |
441 | { |
442 | struct cx24120_cmd cmd; | |
443 | int ret; | |
444 | ||
445 | cmd.id = CMD_MPEG_ONOFF; | |
446 | cmd.len = 4; | |
447 | cmd.arg[0] = 0x01; | |
448 | cmd.arg[1] = 0x00; | |
449 | cmd.arg[2] = enable ? 0 : (u8)(-1); | |
450 | cmd.arg[3] = 0x01; | |
451 | ||
452 | ret = cx24120_message_send(state, &cmd); | |
453 | if (ret != 0) { | |
2f3f07fb JD |
454 | dev_dbg(&state->i2c->dev, "failed to %s MPEG output\n", |
455 | enable ? "enable" : "disable"); | |
5afc9a25 JD |
456 | return ret; |
457 | } | |
458 | ||
459 | state->mpeg_enabled = enable; | |
2f3f07fb JD |
460 | dev_dbg(&state->i2c->dev, "MPEG output %s\n", |
461 | enable ? "enabled" : "disabled"); | |
5afc9a25 JD |
462 | |
463 | return 0; | |
464 | } | |
465 | ||
5afc9a25 JD |
466 | static int cx24120_msg_mpeg_output_config(struct cx24120_state *state, u8 seq) |
467 | { | |
468 | struct cx24120_cmd cmd; | |
469 | struct cx24120_initial_mpeg_config i = | |
470 | state->config->initial_mpeg_config; | |
471 | ||
472 | cmd.id = CMD_MPEG_INIT; | |
473 | cmd.len = 7; | |
1ff2e8ed | 474 | cmd.arg[0] = seq; /* sequental number - can be 0,1,2 */ |
5afc9a25 JD |
475 | cmd.arg[1] = ((i.x1 & 0x01) << 1) | ((i.x1 >> 1) & 0x01); |
476 | cmd.arg[2] = 0x05; | |
477 | cmd.arg[3] = 0x02; | |
478 | cmd.arg[4] = ((i.x2 >> 1) & 0x01); | |
479 | cmd.arg[5] = (i.x2 & 0xf0) | (i.x3 & 0x0f); | |
480 | cmd.arg[6] = 0x10; | |
481 | ||
482 | return cx24120_message_send(state, &cmd); | |
483 | } | |
484 | ||
5afc9a25 | 485 | static int cx24120_diseqc_send_burst(struct dvb_frontend *fe, |
0df289a2 | 486 | enum fe_sec_mini_cmd burst) |
5afc9a25 JD |
487 | { |
488 | struct cx24120_state *state = fe->demodulator_priv; | |
489 | struct cx24120_cmd cmd; | |
490 | ||
2f3f07fb JD |
491 | dev_dbg(&state->i2c->dev, "\n"); |
492 | ||
fbdbab72 JD |
493 | /* |
494 | * Yes, cmd.len is set to zero. The old driver | |
5afc9a25 JD |
495 | * didn't specify any len, but also had a |
496 | * memset 0 before every use of the cmd struct | |
497 | * which would have set it to zero. | |
498 | * This quite probably needs looking into. | |
499 | */ | |
500 | cmd.id = CMD_DISEQC_BURST; | |
501 | cmd.len = 0; | |
502 | cmd.arg[0] = 0x00; | |
7c95e25e | 503 | cmd.arg[1] = (burst == SEC_MINI_B) ? 0x01 : 0x00; |
1ff2e8ed | 504 | |
5afc9a25 JD |
505 | return cx24120_message_send(state, &cmd); |
506 | } | |
507 | ||
0df289a2 | 508 | static int cx24120_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone) |
5afc9a25 JD |
509 | { |
510 | struct cx24120_state *state = fe->demodulator_priv; | |
511 | struct cx24120_cmd cmd; | |
512 | ||
2f3f07fb | 513 | dev_dbg(&state->i2c->dev, "(%d)\n", tone); |
5afc9a25 JD |
514 | |
515 | if ((tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF)) { | |
516 | err("Invalid tone=%d\n", tone); | |
517 | return -EINVAL; | |
518 | } | |
519 | ||
520 | cmd.id = CMD_SETTONE; | |
521 | cmd.len = 4; | |
522 | cmd.arg[0] = 0x00; | |
523 | cmd.arg[1] = 0x00; | |
524 | cmd.arg[2] = 0x00; | |
1ff2e8ed | 525 | cmd.arg[3] = (tone == SEC_TONE_ON) ? 0x01 : 0x00; |
5afc9a25 JD |
526 | |
527 | return cx24120_message_send(state, &cmd); | |
528 | } | |
529 | ||
5afc9a25 | 530 | static int cx24120_set_voltage(struct dvb_frontend *fe, |
0df289a2 | 531 | enum fe_sec_voltage voltage) |
5afc9a25 JD |
532 | { |
533 | struct cx24120_state *state = fe->demodulator_priv; | |
534 | struct cx24120_cmd cmd; | |
535 | ||
2f3f07fb | 536 | dev_dbg(&state->i2c->dev, "(%d)\n", voltage); |
5afc9a25 JD |
537 | |
538 | cmd.id = CMD_SETVOLTAGE; | |
539 | cmd.len = 2; | |
540 | cmd.arg[0] = 0x00; | |
1ff2e8ed | 541 | cmd.arg[1] = (voltage == SEC_VOLTAGE_18) ? 0x01 : 0x00; |
5afc9a25 JD |
542 | |
543 | return cx24120_message_send(state, &cmd); | |
544 | } | |
545 | ||
5afc9a25 | 546 | static int cx24120_send_diseqc_msg(struct dvb_frontend *fe, |
1ff2e8ed | 547 | struct dvb_diseqc_master_cmd *d) |
5afc9a25 JD |
548 | { |
549 | struct cx24120_state *state = fe->demodulator_priv; | |
550 | struct cx24120_cmd cmd; | |
551 | int back_count; | |
552 | ||
2f3f07fb | 553 | dev_dbg(&state->i2c->dev, "\n"); |
5afc9a25 JD |
554 | |
555 | cmd.id = CMD_DISEQC_MSG1; | |
556 | cmd.len = 11; | |
557 | cmd.arg[0] = 0x00; | |
558 | cmd.arg[1] = 0x00; | |
559 | cmd.arg[2] = 0x03; | |
560 | cmd.arg[3] = 0x16; | |
561 | cmd.arg[4] = 0x28; | |
562 | cmd.arg[5] = 0x01; | |
563 | cmd.arg[6] = 0x01; | |
564 | cmd.arg[7] = 0x14; | |
565 | cmd.arg[8] = 0x19; | |
566 | cmd.arg[9] = 0x14; | |
567 | cmd.arg[10] = 0x1e; | |
568 | ||
569 | if (cx24120_message_send(state, &cmd)) { | |
570 | err("send 1st message(0x%x) failed\n", cmd.id); | |
571 | return -EREMOTEIO; | |
572 | } | |
573 | ||
574 | cmd.id = CMD_DISEQC_MSG2; | |
575 | cmd.len = d->msg_len + 6; | |
576 | cmd.arg[0] = 0x00; | |
577 | cmd.arg[1] = 0x01; | |
578 | cmd.arg[2] = 0x02; | |
579 | cmd.arg[3] = 0x00; | |
580 | cmd.arg[4] = 0x00; | |
581 | cmd.arg[5] = d->msg_len; | |
582 | ||
583 | memcpy(&cmd.arg[6], &d->msg, d->msg_len); | |
584 | ||
585 | if (cx24120_message_send(state, &cmd)) { | |
586 | err("send 2nd message(0x%x) failed\n", cmd.id); | |
587 | return -EREMOTEIO; | |
588 | } | |
589 | ||
590 | back_count = 500; | |
591 | do { | |
592 | if (!(cx24120_readreg(state, 0x93) & 0x01)) { | |
2f3f07fb | 593 | dev_dbg(&state->i2c->dev, "diseqc sequence sent\n"); |
5afc9a25 JD |
594 | return 0; |
595 | } | |
596 | msleep(20); | |
597 | back_count -= 20; | |
598 | } while (back_count); | |
599 | ||
600 | err("Too long waiting for diseqc.\n"); | |
601 | return -ETIMEDOUT; | |
602 | } | |
603 | ||
1462612c | 604 | static void cx24120_get_stats(struct cx24120_state *state) |
9fc18f18 JD |
605 | { |
606 | struct dvb_frontend *fe = &state->frontend; | |
607 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; | |
34ce475d | 608 | struct cx24120_cmd cmd; |
ddcb252e | 609 | int ret, cnr, msecs; |
80e9710b | 610 | u16 sig, ucb; |
ddcb252e | 611 | u32 ber; |
9fc18f18 | 612 | |
2f3f07fb | 613 | dev_dbg(&state->i2c->dev, "\n"); |
9fc18f18 JD |
614 | |
615 | /* signal strength */ | |
1462612c | 616 | if (state->fe_status & FE_HAS_SIGNAL) { |
34ce475d JD |
617 | cmd.id = CMD_READ_SNR; |
618 | cmd.len = 1; | |
619 | cmd.arg[0] = 0x00; | |
620 | ||
621 | ret = cx24120_message_send(state, &cmd); | |
622 | if (ret != 0) { | |
623 | err("error reading signal strength\n"); | |
9fc18f18 | 624 | return; |
34ce475d JD |
625 | } |
626 | ||
627 | /* raw */ | |
b0cdf1a1 JD |
628 | sig = cx24120_readreg(state, CX24120_REG_SIGSTR_H) >> 6; |
629 | sig = sig << 8; | |
630 | sig |= cx24120_readreg(state, CX24120_REG_SIGSTR_L); | |
631 | dev_dbg(&state->i2c->dev, | |
2f3f07fb | 632 | "signal strength from firmware = 0x%x\n", sig); |
34ce475d JD |
633 | |
634 | /* cooked */ | |
b0cdf1a1 | 635 | sig = -100 * sig + 94324; |
9fc18f18 JD |
636 | |
637 | c->strength.stat[0].scale = FE_SCALE_RELATIVE; | |
b0cdf1a1 | 638 | c->strength.stat[0].uvalue = sig; |
9fc18f18 JD |
639 | } else { |
640 | c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; | |
641 | } | |
642 | ||
3b5eb504 JD |
643 | /* CNR */ |
644 | if (state->fe_status & FE_HAS_VITERBI) { | |
645 | cnr = cx24120_readreg(state, CX24120_REG_QUALITY_H) << 8; | |
646 | cnr |= cx24120_readreg(state, CX24120_REG_QUALITY_L); | |
2f3f07fb | 647 | dev_dbg(&state->i2c->dev, "read SNR index = %d\n", cnr); |
3b5eb504 JD |
648 | |
649 | /* guessed - seems about right */ | |
650 | cnr = cnr * 100; | |
651 | ||
652 | c->cnr.stat[0].scale = FE_SCALE_DECIBEL; | |
653 | c->cnr.stat[0].svalue = cnr; | |
654 | } else { | |
655 | c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; | |
656 | } | |
9fc18f18 | 657 | |
e3f2f63e JD |
658 | /* BER & UCB require lock */ |
659 | if (!(state->fe_status & FE_HAS_LOCK)) { | |
660 | c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; | |
661 | c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; | |
662 | c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; | |
663 | c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; | |
664 | return; | |
665 | } | |
666 | ||
ddcb252e JD |
667 | /* BER */ |
668 | if (time_after(jiffies, state->ber_jiffies_stats)) { | |
669 | msecs = (state->berw_usecs + 500) / 1000; | |
670 | state->ber_jiffies_stats = jiffies + msecs_to_jiffies(msecs); | |
671 | ||
fc443284 JD |
672 | ber = cx24120_readreg(state, CX24120_REG_BER_HH) << 24; |
673 | ber |= cx24120_readreg(state, CX24120_REG_BER_HL) << 16; | |
674 | ber |= cx24120_readreg(state, CX24120_REG_BER_LH) << 8; | |
675 | ber |= cx24120_readreg(state, CX24120_REG_BER_LL); | |
676 | dev_dbg(&state->i2c->dev, "read BER index = %d\n", ber); | |
ddcb252e JD |
677 | |
678 | c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; | |
679 | c->post_bit_error.stat[0].uvalue += ber; | |
680 | ||
681 | c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; | |
682 | c->post_bit_count.stat[0].uvalue += CX24120_BER_WSIZE; | |
683 | } | |
684 | ||
80e9710b JD |
685 | /* UCB */ |
686 | if (time_after(jiffies, state->per_jiffies_stats)) { | |
687 | state->per_jiffies_stats = jiffies + msecs_to_jiffies(1000); | |
688 | ||
689 | ucb = cx24120_readreg(state, CX24120_REG_UCB_H) << 8; | |
690 | ucb |= cx24120_readreg(state, CX24120_REG_UCB_L); | |
691 | dev_dbg(&state->i2c->dev, "ucblocks = %d\n", ucb); | |
692 | ||
bf8de2d3 JD |
693 | /* handle reset */ |
694 | if (ucb < state->ucb_offset) | |
695 | state->ucb_offset = c->block_error.stat[0].uvalue; | |
696 | ||
80e9710b | 697 | c->block_error.stat[0].scale = FE_SCALE_COUNTER; |
bf8de2d3 | 698 | c->block_error.stat[0].uvalue = ucb + state->ucb_offset; |
80e9710b JD |
699 | |
700 | c->block_count.stat[0].scale = FE_SCALE_COUNTER; | |
701 | c->block_count.stat[0].uvalue += state->bitrate / 8 / 208; | |
702 | } | |
9fc18f18 JD |
703 | } |
704 | ||
6138dc2f JD |
705 | static void cx24120_set_clock_ratios(struct dvb_frontend *fe); |
706 | ||
5afc9a25 | 707 | /* Read current tuning status */ |
0df289a2 | 708 | static int cx24120_read_status(struct dvb_frontend *fe, enum fe_status *status) |
5afc9a25 JD |
709 | { |
710 | struct cx24120_state *state = fe->demodulator_priv; | |
711 | int lock; | |
712 | ||
713 | lock = cx24120_readreg(state, CX24120_REG_STATUS); | |
714 | ||
2f3f07fb | 715 | dev_dbg(&state->i2c->dev, "status = 0x%02x\n", lock); |
5afc9a25 JD |
716 | |
717 | *status = 0; | |
718 | ||
719 | if (lock & CX24120_HAS_SIGNAL) | |
720 | *status = FE_HAS_SIGNAL; | |
721 | if (lock & CX24120_HAS_CARRIER) | |
722 | *status |= FE_HAS_CARRIER; | |
723 | if (lock & CX24120_HAS_VITERBI) | |
724 | *status |= FE_HAS_VITERBI | FE_HAS_SYNC; | |
725 | if (lock & CX24120_HAS_LOCK) | |
726 | *status |= FE_HAS_LOCK; | |
727 | ||
fbdbab72 JD |
728 | /* |
729 | * TODO: is FE_HAS_SYNC in the right place? | |
5afc9a25 | 730 | * Other cx241xx drivers have this slightly |
fbdbab72 JD |
731 | * different |
732 | */ | |
5afc9a25 | 733 | |
1462612c JD |
734 | state->fe_status = *status; |
735 | cx24120_get_stats(state); | |
9fc18f18 | 736 | |
6138dc2f JD |
737 | /* Set the clock once tuned in */ |
738 | if (state->need_clock_set && *status & FE_HAS_LOCK) { | |
739 | /* Set clock ratios */ | |
740 | cx24120_set_clock_ratios(fe); | |
741 | ||
742 | /* Old driver would do a msleep(200) here */ | |
743 | ||
744 | /* Renable mpeg output */ | |
745 | if (!state->mpeg_enabled) | |
746 | cx24120_msg_mpeg_output_global_config(state, 1); | |
747 | ||
748 | state->need_clock_set = 0; | |
749 | } | |
750 | ||
5afc9a25 JD |
751 | return 0; |
752 | } | |
753 | ||
fbdbab72 JD |
754 | /* |
755 | * FEC & modulation lookup table | |
5afc9a25 JD |
756 | * Used for decoding the REG_FECMODE register |
757 | * once tuned in. | |
758 | */ | |
ec8fe6c9 | 759 | struct cx24120_modfec { |
0df289a2 MCC |
760 | enum fe_delivery_system delsys; |
761 | enum fe_modulation mod; | |
762 | enum fe_code_rate fec; | |
5afc9a25 | 763 | u8 val; |
ec8fe6c9 MCC |
764 | }; |
765 | ||
766 | static const struct cx24120_modfec modfec_lookup_table[] = { | |
2e89a5e0 PB |
767 | /*delsys mod fec val */ |
768 | { SYS_DVBS, QPSK, FEC_1_2, 0x01 }, | |
769 | { SYS_DVBS, QPSK, FEC_2_3, 0x02 }, | |
770 | { SYS_DVBS, QPSK, FEC_3_4, 0x03 }, | |
771 | { SYS_DVBS, QPSK, FEC_4_5, 0x04 }, | |
772 | { SYS_DVBS, QPSK, FEC_5_6, 0x05 }, | |
773 | { SYS_DVBS, QPSK, FEC_6_7, 0x06 }, | |
774 | { SYS_DVBS, QPSK, FEC_7_8, 0x07 }, | |
775 | ||
776 | { SYS_DVBS2, QPSK, FEC_1_2, 0x04 }, | |
777 | { SYS_DVBS2, QPSK, FEC_3_5, 0x05 }, | |
778 | { SYS_DVBS2, QPSK, FEC_2_3, 0x06 }, | |
779 | { SYS_DVBS2, QPSK, FEC_3_4, 0x07 }, | |
780 | { SYS_DVBS2, QPSK, FEC_4_5, 0x08 }, | |
781 | { SYS_DVBS2, QPSK, FEC_5_6, 0x09 }, | |
782 | { SYS_DVBS2, QPSK, FEC_8_9, 0x0a }, | |
783 | { SYS_DVBS2, QPSK, FEC_9_10, 0x0b }, | |
784 | ||
785 | { SYS_DVBS2, PSK_8, FEC_3_5, 0x0c }, | |
786 | { SYS_DVBS2, PSK_8, FEC_2_3, 0x0d }, | |
787 | { SYS_DVBS2, PSK_8, FEC_3_4, 0x0e }, | |
788 | { SYS_DVBS2, PSK_8, FEC_5_6, 0x0f }, | |
789 | { SYS_DVBS2, PSK_8, FEC_8_9, 0x10 }, | |
790 | { SYS_DVBS2, PSK_8, FEC_9_10, 0x11 }, | |
5afc9a25 JD |
791 | }; |
792 | ||
5afc9a25 JD |
793 | /* Retrieve current fec, modulation & pilot values */ |
794 | static int cx24120_get_fec(struct dvb_frontend *fe) | |
795 | { | |
796 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; | |
797 | struct cx24120_state *state = fe->demodulator_priv; | |
798 | int idx; | |
799 | int ret; | |
1ff2e8ed | 800 | int fec; |
5afc9a25 | 801 | |
5afc9a25 | 802 | ret = cx24120_readreg(state, CX24120_REG_FECMODE); |
1ff2e8ed | 803 | fec = ret & 0x3f; /* Lower 6 bits */ |
5afc9a25 | 804 | |
2f3f07fb | 805 | dev_dbg(&state->i2c->dev, "raw fec = %d\n", fec); |
5afc9a25 JD |
806 | |
807 | for (idx = 0; idx < ARRAY_SIZE(modfec_lookup_table); idx++) { | |
808 | if (modfec_lookup_table[idx].delsys != state->dcur.delsys) | |
809 | continue; | |
1ff2e8ed | 810 | if (modfec_lookup_table[idx].val != fec) |
5afc9a25 JD |
811 | continue; |
812 | ||
2e89a5e0 | 813 | break; /* found */ |
5afc9a25 JD |
814 | } |
815 | ||
816 | if (idx >= ARRAY_SIZE(modfec_lookup_table)) { | |
2f3f07fb | 817 | dev_dbg(&state->i2c->dev, "couldn't find fec!\n"); |
5afc9a25 JD |
818 | return -EINVAL; |
819 | } | |
820 | ||
821 | /* save values back to cache */ | |
822 | c->modulation = modfec_lookup_table[idx].mod; | |
823 | c->fec_inner = modfec_lookup_table[idx].fec; | |
824 | c->pilot = (ret & 0x80) ? PILOT_ON : PILOT_OFF; | |
825 | ||
2f3f07fb | 826 | dev_dbg(&state->i2c->dev, "mod(%d), fec(%d), pilot(%d)\n", |
5afc9a25 JD |
827 | c->modulation, c->fec_inner, c->pilot); |
828 | ||
829 | return 0; | |
830 | } | |
831 | ||
ddcb252e | 832 | /* Calculate ber window time */ |
edff2bac | 833 | static void cx24120_calculate_ber_window(struct cx24120_state *state, u32 rate) |
ddcb252e JD |
834 | { |
835 | struct dvb_frontend *fe = &state->frontend; | |
836 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; | |
80e9710b | 837 | u64 tmp; |
ddcb252e JD |
838 | |
839 | /* | |
840 | * Calculate bitrate from rate in the clock ratios table. | |
841 | * This isn't *exactly* right but close enough. | |
842 | */ | |
80e9710b JD |
843 | tmp = (u64)c->symbol_rate * rate; |
844 | do_div(tmp, 256); | |
845 | state->bitrate = tmp; | |
ddcb252e JD |
846 | |
847 | /* usecs per ber window */ | |
848 | tmp = 1000000ULL * CX24120_BER_WSIZE; | |
80e9710b | 849 | do_div(tmp, state->bitrate); |
ddcb252e JD |
850 | state->berw_usecs = tmp; |
851 | ||
80e9710b JD |
852 | dev_dbg(&state->i2c->dev, "bitrate: %u, berw_usecs: %u\n", |
853 | state->bitrate, state->berw_usecs); | |
ddcb252e JD |
854 | } |
855 | ||
fbdbab72 JD |
856 | /* |
857 | * Clock ratios lookup table | |
5afc9a25 JD |
858 | * |
859 | * Values obtained from much larger table in old driver | |
860 | * which had numerous entries which would never match. | |
861 | * | |
862 | * There's probably some way of calculating these but I | |
863 | * can't determine the pattern | |
fbdbab72 | 864 | */ |
ec8fe6c9 | 865 | struct cx24120_clock_ratios_table { |
0df289a2 MCC |
866 | enum fe_delivery_system delsys; |
867 | enum fe_pilot pilot; | |
868 | enum fe_modulation mod; | |
869 | enum fe_code_rate fec; | |
5afc9a25 JD |
870 | u32 m_rat; |
871 | u32 n_rat; | |
872 | u32 rate; | |
ec8fe6c9 MCC |
873 | }; |
874 | ||
875 | static const struct cx24120_clock_ratios_table clock_ratios_table[] = { | |
2e89a5e0 PB |
876 | /*delsys pilot mod fec m_rat n_rat rate */ |
877 | { SYS_DVBS2, PILOT_OFF, QPSK, FEC_1_2, 273088, 254505, 274 }, | |
878 | { SYS_DVBS2, PILOT_OFF, QPSK, FEC_3_5, 17272, 13395, 330 }, | |
879 | { SYS_DVBS2, PILOT_OFF, QPSK, FEC_2_3, 24344, 16967, 367 }, | |
880 | { SYS_DVBS2, PILOT_OFF, QPSK, FEC_3_4, 410788, 254505, 413 }, | |
881 | { SYS_DVBS2, PILOT_OFF, QPSK, FEC_4_5, 438328, 254505, 440 }, | |
882 | { SYS_DVBS2, PILOT_OFF, QPSK, FEC_5_6, 30464, 16967, 459 }, | |
883 | { SYS_DVBS2, PILOT_OFF, QPSK, FEC_8_9, 487832, 254505, 490 }, | |
884 | { SYS_DVBS2, PILOT_OFF, QPSK, FEC_9_10, 493952, 254505, 496 }, | |
885 | { SYS_DVBS2, PILOT_OFF, PSK_8, FEC_3_5, 328168, 169905, 494 }, | |
886 | { SYS_DVBS2, PILOT_OFF, PSK_8, FEC_2_3, 24344, 11327, 550 }, | |
887 | { SYS_DVBS2, PILOT_OFF, PSK_8, FEC_3_4, 410788, 169905, 618 }, | |
888 | { SYS_DVBS2, PILOT_OFF, PSK_8, FEC_5_6, 30464, 11327, 688 }, | |
889 | { SYS_DVBS2, PILOT_OFF, PSK_8, FEC_8_9, 487832, 169905, 735 }, | |
890 | { SYS_DVBS2, PILOT_OFF, PSK_8, FEC_9_10, 493952, 169905, 744 }, | |
891 | { SYS_DVBS2, PILOT_ON, QPSK, FEC_1_2, 273088, 260709, 268 }, | |
892 | { SYS_DVBS2, PILOT_ON, QPSK, FEC_3_5, 328168, 260709, 322 }, | |
893 | { SYS_DVBS2, PILOT_ON, QPSK, FEC_2_3, 121720, 86903, 358 }, | |
894 | { SYS_DVBS2, PILOT_ON, QPSK, FEC_3_4, 410788, 260709, 403 }, | |
895 | { SYS_DVBS2, PILOT_ON, QPSK, FEC_4_5, 438328, 260709, 430 }, | |
896 | { SYS_DVBS2, PILOT_ON, QPSK, FEC_5_6, 152320, 86903, 448 }, | |
897 | { SYS_DVBS2, PILOT_ON, QPSK, FEC_8_9, 487832, 260709, 479 }, | |
898 | { SYS_DVBS2, PILOT_ON, QPSK, FEC_9_10, 493952, 260709, 485 }, | |
899 | { SYS_DVBS2, PILOT_ON, PSK_8, FEC_3_5, 328168, 173853, 483 }, | |
900 | { SYS_DVBS2, PILOT_ON, PSK_8, FEC_2_3, 121720, 57951, 537 }, | |
901 | { SYS_DVBS2, PILOT_ON, PSK_8, FEC_3_4, 410788, 173853, 604 }, | |
902 | { SYS_DVBS2, PILOT_ON, PSK_8, FEC_5_6, 152320, 57951, 672 }, | |
903 | { SYS_DVBS2, PILOT_ON, PSK_8, FEC_8_9, 487832, 173853, 718 }, | |
904 | { SYS_DVBS2, PILOT_ON, PSK_8, FEC_9_10, 493952, 173853, 727 }, | |
905 | { SYS_DVBS, PILOT_OFF, QPSK, FEC_1_2, 152592, 152592, 256 }, | |
906 | { SYS_DVBS, PILOT_OFF, QPSK, FEC_2_3, 305184, 228888, 341 }, | |
907 | { SYS_DVBS, PILOT_OFF, QPSK, FEC_3_4, 457776, 305184, 384 }, | |
908 | { SYS_DVBS, PILOT_OFF, QPSK, FEC_5_6, 762960, 457776, 427 }, | |
909 | { SYS_DVBS, PILOT_OFF, QPSK, FEC_7_8, 1068144, 610368, 448 }, | |
5afc9a25 JD |
910 | }; |
911 | ||
5afc9a25 JD |
912 | /* Set clock ratio from lookup table */ |
913 | static void cx24120_set_clock_ratios(struct dvb_frontend *fe) | |
914 | { | |
915 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; | |
916 | struct cx24120_state *state = fe->demodulator_priv; | |
917 | struct cx24120_cmd cmd; | |
918 | int ret, idx; | |
919 | ||
920 | /* Find fec, modulation, pilot */ | |
921 | ret = cx24120_get_fec(fe); | |
922 | if (ret != 0) | |
923 | return; | |
924 | ||
925 | /* Find the clock ratios in the lookup table */ | |
926 | for (idx = 0; idx < ARRAY_SIZE(clock_ratios_table); idx++) { | |
927 | if (clock_ratios_table[idx].delsys != state->dcur.delsys) | |
928 | continue; | |
929 | if (clock_ratios_table[idx].mod != c->modulation) | |
930 | continue; | |
931 | if (clock_ratios_table[idx].fec != c->fec_inner) | |
932 | continue; | |
933 | if (clock_ratios_table[idx].pilot != c->pilot) | |
934 | continue; | |
935 | ||
936 | break; /* found */ | |
937 | } | |
938 | ||
939 | if (idx >= ARRAY_SIZE(clock_ratios_table)) { | |
940 | info("Clock ratio not found - data reception in danger\n"); | |
941 | return; | |
942 | } | |
943 | ||
5afc9a25 JD |
944 | /* Read current values? */ |
945 | cmd.id = CMD_CLOCK_READ; | |
946 | cmd.len = 1; | |
947 | cmd.arg[0] = 0x00; | |
948 | ret = cx24120_message_sendrcv(state, &cmd, 6); | |
949 | if (ret != 0) | |
950 | return; | |
951 | /* in cmd[0]-[5] - result */ | |
952 | ||
2f3f07fb | 953 | dev_dbg(&state->i2c->dev, "m=%d, n=%d; idx: %d m=%d, n=%d, rate=%d\n", |
5afc9a25 JD |
954 | cmd.arg[2] | (cmd.arg[1] << 8) | (cmd.arg[0] << 16), |
955 | cmd.arg[5] | (cmd.arg[4] << 8) | (cmd.arg[3] << 16), | |
956 | idx, | |
957 | clock_ratios_table[idx].m_rat, | |
958 | clock_ratios_table[idx].n_rat, | |
959 | clock_ratios_table[idx].rate); | |
960 | ||
5afc9a25 JD |
961 | /* Set the clock */ |
962 | cmd.id = CMD_CLOCK_SET; | |
963 | cmd.len = 10; | |
964 | cmd.arg[0] = 0; | |
965 | cmd.arg[1] = 0x10; | |
966 | cmd.arg[2] = (clock_ratios_table[idx].m_rat >> 16) & 0xff; | |
967 | cmd.arg[3] = (clock_ratios_table[idx].m_rat >> 8) & 0xff; | |
968 | cmd.arg[4] = (clock_ratios_table[idx].m_rat >> 0) & 0xff; | |
969 | cmd.arg[5] = (clock_ratios_table[idx].n_rat >> 16) & 0xff; | |
970 | cmd.arg[6] = (clock_ratios_table[idx].n_rat >> 8) & 0xff; | |
971 | cmd.arg[7] = (clock_ratios_table[idx].n_rat >> 0) & 0xff; | |
972 | cmd.arg[8] = (clock_ratios_table[idx].rate >> 8) & 0xff; | |
973 | cmd.arg[9] = (clock_ratios_table[idx].rate >> 0) & 0xff; | |
974 | ||
975 | cx24120_message_send(state, &cmd); | |
ddcb252e JD |
976 | |
977 | /* Calculate ber window rates for stat work */ | |
978 | cx24120_calculate_ber_window(state, clock_ratios_table[idx].rate); | |
5afc9a25 JD |
979 | } |
980 | ||
5afc9a25 JD |
981 | /* Set inversion value */ |
982 | static int cx24120_set_inversion(struct cx24120_state *state, | |
0df289a2 | 983 | enum fe_spectral_inversion inversion) |
5afc9a25 | 984 | { |
2f3f07fb | 985 | dev_dbg(&state->i2c->dev, "(%d)\n", inversion); |
5afc9a25 JD |
986 | |
987 | switch (inversion) { | |
988 | case INVERSION_OFF: | |
989 | state->dnxt.inversion_val = 0x00; | |
990 | break; | |
991 | case INVERSION_ON: | |
992 | state->dnxt.inversion_val = 0x04; | |
993 | break; | |
994 | case INVERSION_AUTO: | |
995 | state->dnxt.inversion_val = 0x0c; | |
996 | break; | |
997 | default: | |
998 | return -EINVAL; | |
999 | } | |
1000 | ||
1001 | state->dnxt.inversion = inversion; | |
1002 | ||
1003 | return 0; | |
1004 | } | |
1005 | ||
5c0a1c28 | 1006 | /* FEC lookup table for tuning */ |
ec8fe6c9 | 1007 | struct cx24120_modfec_table { |
0df289a2 MCC |
1008 | enum fe_delivery_system delsys; |
1009 | enum fe_modulation mod; | |
1010 | enum fe_code_rate fec; | |
5afc9a25 | 1011 | u8 val; |
ec8fe6c9 MCC |
1012 | }; |
1013 | ||
1014 | static const struct cx24120_modfec_table modfec_table[] = { | |
5c0a1c28 JD |
1015 | /*delsys mod fec val */ |
1016 | { SYS_DVBS, QPSK, FEC_1_2, 0x2e }, | |
1017 | { SYS_DVBS, QPSK, FEC_2_3, 0x2f }, | |
1018 | { SYS_DVBS, QPSK, FEC_3_4, 0x30 }, | |
1019 | { SYS_DVBS, QPSK, FEC_5_6, 0x31 }, | |
1020 | { SYS_DVBS, QPSK, FEC_6_7, 0x32 }, | |
1021 | { SYS_DVBS, QPSK, FEC_7_8, 0x33 }, | |
1022 | ||
1023 | { SYS_DVBS2, QPSK, FEC_1_2, 0x04 }, | |
1024 | { SYS_DVBS2, QPSK, FEC_3_5, 0x05 }, | |
1025 | { SYS_DVBS2, QPSK, FEC_2_3, 0x06 }, | |
1026 | { SYS_DVBS2, QPSK, FEC_3_4, 0x07 }, | |
1027 | { SYS_DVBS2, QPSK, FEC_4_5, 0x08 }, | |
1028 | { SYS_DVBS2, QPSK, FEC_5_6, 0x09 }, | |
1029 | { SYS_DVBS2, QPSK, FEC_8_9, 0x0a }, | |
1030 | { SYS_DVBS2, QPSK, FEC_9_10, 0x0b }, | |
1031 | ||
1032 | { SYS_DVBS2, PSK_8, FEC_3_5, 0x0c }, | |
1033 | { SYS_DVBS2, PSK_8, FEC_2_3, 0x0d }, | |
1034 | { SYS_DVBS2, PSK_8, FEC_3_4, 0x0e }, | |
1035 | { SYS_DVBS2, PSK_8, FEC_5_6, 0x0f }, | |
1036 | { SYS_DVBS2, PSK_8, FEC_8_9, 0x10 }, | |
1037 | { SYS_DVBS2, PSK_8, FEC_9_10, 0x11 }, | |
5afc9a25 JD |
1038 | }; |
1039 | ||
1040 | /* Set fec_val & fec_mask values from delsys, modulation & fec */ | |
0df289a2 MCC |
1041 | static int cx24120_set_fec(struct cx24120_state *state, enum fe_modulation mod, |
1042 | enum fe_code_rate fec) | |
5afc9a25 JD |
1043 | { |
1044 | int idx; | |
1045 | ||
2f3f07fb | 1046 | dev_dbg(&state->i2c->dev, "(0x%02x,0x%02x)\n", mod, fec); |
5afc9a25 JD |
1047 | |
1048 | state->dnxt.fec = fec; | |
1049 | ||
1050 | /* Lookup fec_val from modfec table */ | |
1051 | for (idx = 0; idx < ARRAY_SIZE(modfec_table); idx++) { | |
1052 | if (modfec_table[idx].delsys != state->dnxt.delsys) | |
1053 | continue; | |
1054 | if (modfec_table[idx].mod != mod) | |
1055 | continue; | |
1056 | if (modfec_table[idx].fec != fec) | |
1057 | continue; | |
1058 | ||
1059 | /* found */ | |
1060 | state->dnxt.fec_mask = 0x00; | |
1061 | state->dnxt.fec_val = modfec_table[idx].val; | |
1062 | return 0; | |
1063 | } | |
1064 | ||
5afc9a25 JD |
1065 | if (state->dnxt.delsys == SYS_DVBS2) { |
1066 | /* DVBS2 auto is 0x00/0x00 */ | |
1067 | state->dnxt.fec_mask = 0x00; | |
1068 | state->dnxt.fec_val = 0x00; | |
1069 | } else { | |
1070 | /* Set DVB-S to auto */ | |
1071 | state->dnxt.fec_val = 0x2e; | |
1072 | state->dnxt.fec_mask = 0xac; | |
1073 | } | |
1074 | ||
1075 | return 0; | |
1076 | } | |
1077 | ||
5afc9a25 | 1078 | /* Set pilot */ |
0df289a2 | 1079 | static int cx24120_set_pilot(struct cx24120_state *state, enum fe_pilot pilot) |
1ff2e8ed | 1080 | { |
2f3f07fb | 1081 | dev_dbg(&state->i2c->dev, "(%d)\n", pilot); |
5afc9a25 JD |
1082 | |
1083 | /* Pilot only valid in DVBS2 */ | |
1084 | if (state->dnxt.delsys != SYS_DVBS2) { | |
1085 | state->dnxt.pilot_val = CX24120_PILOT_OFF; | |
1086 | return 0; | |
1087 | } | |
1088 | ||
5afc9a25 JD |
1089 | switch (pilot) { |
1090 | case PILOT_OFF: | |
1091 | state->dnxt.pilot_val = CX24120_PILOT_OFF; | |
1092 | break; | |
1093 | case PILOT_ON: | |
1094 | state->dnxt.pilot_val = CX24120_PILOT_ON; | |
1095 | break; | |
1096 | case PILOT_AUTO: | |
1097 | default: | |
1098 | state->dnxt.pilot_val = CX24120_PILOT_AUTO; | |
1099 | } | |
1100 | ||
1101 | return 0; | |
1102 | } | |
1103 | ||
1104 | /* Set symbol rate */ | |
1105 | static int cx24120_set_symbolrate(struct cx24120_state *state, u32 rate) | |
1106 | { | |
2f3f07fb | 1107 | dev_dbg(&state->i2c->dev, "(%d)\n", rate); |
5afc9a25 JD |
1108 | |
1109 | state->dnxt.symbol_rate = rate; | |
1110 | ||
1111 | /* Check symbol rate */ | |
1112 | if (rate > 31000000) { | |
1113 | state->dnxt.clkdiv = (-(rate < 31000001) & 3) + 2; | |
1114 | state->dnxt.ratediv = (-(rate < 31000001) & 6) + 4; | |
1115 | } else { | |
1116 | state->dnxt.clkdiv = 3; | |
1117 | state->dnxt.ratediv = 6; | |
1118 | } | |
1119 | ||
1120 | return 0; | |
1121 | } | |
1122 | ||
5afc9a25 JD |
1123 | /* Overwrite the current tuning params, we are about to tune */ |
1124 | static void cx24120_clone_params(struct dvb_frontend *fe) | |
1125 | { | |
1126 | struct cx24120_state *state = fe->demodulator_priv; | |
1127 | ||
1128 | state->dcur = state->dnxt; | |
1129 | } | |
1130 | ||
5afc9a25 JD |
1131 | static int cx24120_set_frontend(struct dvb_frontend *fe) |
1132 | { | |
1133 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; | |
1134 | struct cx24120_state *state = fe->demodulator_priv; | |
1135 | struct cx24120_cmd cmd; | |
1136 | int ret; | |
5afc9a25 JD |
1137 | |
1138 | switch (c->delivery_system) { | |
1139 | case SYS_DVBS2: | |
2f3f07fb | 1140 | dev_dbg(&state->i2c->dev, "DVB-S2\n"); |
5afc9a25 JD |
1141 | break; |
1142 | case SYS_DVBS: | |
2f3f07fb | 1143 | dev_dbg(&state->i2c->dev, "DVB-S\n"); |
5afc9a25 JD |
1144 | break; |
1145 | default: | |
1146 | dev_dbg(&state->i2c->dev, | |
2f3f07fb JD |
1147 | "delivery system(%d) not supported\n", |
1148 | c->delivery_system); | |
c0b34ab1 | 1149 | return -EINVAL; |
5afc9a25 JD |
1150 | } |
1151 | ||
5afc9a25 JD |
1152 | state->dnxt.delsys = c->delivery_system; |
1153 | state->dnxt.modulation = c->modulation; | |
1154 | state->dnxt.frequency = c->frequency; | |
1155 | state->dnxt.pilot = c->pilot; | |
1156 | ||
1157 | ret = cx24120_set_inversion(state, c->inversion); | |
1158 | if (ret != 0) | |
1159 | return ret; | |
1160 | ||
1161 | ret = cx24120_set_fec(state, c->modulation, c->fec_inner); | |
1162 | if (ret != 0) | |
1163 | return ret; | |
1164 | ||
1165 | ret = cx24120_set_pilot(state, c->pilot); | |
1166 | if (ret != 0) | |
1167 | return ret; | |
1168 | ||
1169 | ret = cx24120_set_symbolrate(state, c->symbol_rate); | |
1170 | if (ret != 0) | |
1171 | return ret; | |
1172 | ||
5afc9a25 JD |
1173 | /* discard the 'current' tuning parameters and prepare to tune */ |
1174 | cx24120_clone_params(fe); | |
1175 | ||
1176 | dev_dbg(&state->i2c->dev, | |
2f3f07fb | 1177 | "delsys = %d\n", state->dcur.delsys); |
5afc9a25 | 1178 | dev_dbg(&state->i2c->dev, |
2f3f07fb | 1179 | "modulation = %d\n", state->dcur.modulation); |
5afc9a25 | 1180 | dev_dbg(&state->i2c->dev, |
2f3f07fb | 1181 | "frequency = %d\n", state->dcur.frequency); |
5afc9a25 | 1182 | dev_dbg(&state->i2c->dev, |
2f3f07fb | 1183 | "pilot = %d (val = 0x%02x)\n", |
5afc9a25 JD |
1184 | state->dcur.pilot, state->dcur.pilot_val); |
1185 | dev_dbg(&state->i2c->dev, | |
2f3f07fb JD |
1186 | "symbol_rate = %d (clkdiv/ratediv = 0x%02x/0x%02x)\n", |
1187 | state->dcur.symbol_rate, | |
5afc9a25 JD |
1188 | state->dcur.clkdiv, state->dcur.ratediv); |
1189 | dev_dbg(&state->i2c->dev, | |
2f3f07fb | 1190 | "FEC = %d (mask/val = 0x%02x/0x%02x)\n", |
5afc9a25 JD |
1191 | state->dcur.fec, state->dcur.fec_mask, state->dcur.fec_val); |
1192 | dev_dbg(&state->i2c->dev, | |
2f3f07fb | 1193 | "Inversion = %d (val = 0x%02x)\n", |
5afc9a25 JD |
1194 | state->dcur.inversion, state->dcur.inversion_val); |
1195 | ||
6138dc2f JD |
1196 | /* Flag that clock needs to be set after tune */ |
1197 | state->need_clock_set = 1; | |
1198 | ||
5afc9a25 JD |
1199 | /* Tune in */ |
1200 | cmd.id = CMD_TUNEREQUEST; | |
1201 | cmd.len = 15; | |
1202 | cmd.arg[0] = 0; | |
1203 | cmd.arg[1] = (state->dcur.frequency & 0xff0000) >> 16; | |
1204 | cmd.arg[2] = (state->dcur.frequency & 0x00ff00) >> 8; | |
1205 | cmd.arg[3] = (state->dcur.frequency & 0x0000ff); | |
1ff2e8ed PB |
1206 | cmd.arg[4] = ((state->dcur.symbol_rate / 1000) & 0xff00) >> 8; |
1207 | cmd.arg[5] = ((state->dcur.symbol_rate / 1000) & 0x00ff); | |
5afc9a25 JD |
1208 | cmd.arg[6] = state->dcur.inversion; |
1209 | cmd.arg[7] = state->dcur.fec_val | state->dcur.pilot_val; | |
1210 | cmd.arg[8] = CX24120_SEARCH_RANGE_KHZ >> 8; | |
1211 | cmd.arg[9] = CX24120_SEARCH_RANGE_KHZ & 0xff; | |
1212 | cmd.arg[10] = 0; /* maybe rolloff? */ | |
1213 | cmd.arg[11] = state->dcur.fec_mask; | |
1214 | cmd.arg[12] = state->dcur.ratediv; | |
1215 | cmd.arg[13] = state->dcur.clkdiv; | |
1216 | cmd.arg[14] = 0; | |
1217 | ||
5afc9a25 JD |
1218 | /* Send tune command */ |
1219 | ret = cx24120_message_send(state, &cmd); | |
1220 | if (ret != 0) | |
1221 | return ret; | |
1222 | ||
1223 | /* Write symbol rate values */ | |
1224 | ret = cx24120_writereg(state, CX24120_REG_CLKDIV, state->dcur.clkdiv); | |
1225 | ret = cx24120_readreg(state, CX24120_REG_RATEDIV); | |
1226 | ret &= 0xfffffff0; | |
1227 | ret |= state->dcur.ratediv; | |
1228 | ret = cx24120_writereg(state, CX24120_REG_RATEDIV, ret); | |
1229 | ||
5afc9a25 JD |
1230 | return 0; |
1231 | } | |
1232 | ||
c84251bb JD |
1233 | /* Set vco from config */ |
1234 | static int cx24120_set_vco(struct cx24120_state *state) | |
5afc9a25 | 1235 | { |
c84251bb JD |
1236 | struct cx24120_cmd cmd; |
1237 | u32 nxtal_khz, vco; | |
1238 | u64 inv_vco; | |
5afc9a25 JD |
1239 | u32 xtal_khz = state->config->xtal_khz; |
1240 | ||
c84251bb JD |
1241 | nxtal_khz = xtal_khz * 4; |
1242 | vco = nxtal_khz * 10; | |
1243 | inv_vco = DIV_ROUND_CLOSEST_ULL(0x400000000ULL, vco); | |
5afc9a25 | 1244 | |
2f3f07fb JD |
1245 | dev_dbg(&state->i2c->dev, "xtal=%d, vco=%d, inv_vco=%lld\n", |
1246 | xtal_khz, vco, inv_vco); | |
5afc9a25 | 1247 | |
c84251bb JD |
1248 | cmd.id = CMD_VCO_SET; |
1249 | cmd.len = 12; | |
1250 | cmd.arg[0] = (vco >> 16) & 0xff; | |
1251 | cmd.arg[1] = (vco >> 8) & 0xff; | |
1252 | cmd.arg[2] = vco & 0xff; | |
1253 | cmd.arg[3] = (inv_vco >> 8) & 0xff; | |
1254 | cmd.arg[4] = (inv_vco) & 0xff; | |
1255 | cmd.arg[5] = 0x03; | |
1256 | cmd.arg[6] = (nxtal_khz >> 8) & 0xff; | |
1257 | cmd.arg[7] = nxtal_khz & 0xff; | |
1258 | cmd.arg[8] = 0x06; | |
1259 | cmd.arg[9] = 0x03; | |
1260 | cmd.arg[10] = (xtal_khz >> 16) & 0xff; | |
1261 | cmd.arg[11] = xtal_khz & 0xff; | |
1262 | ||
1263 | return cx24120_message_send(state, &cmd); | |
5afc9a25 JD |
1264 | } |
1265 | ||
5b8bc802 | 1266 | static int cx24120_init(struct dvb_frontend *fe) |
5afc9a25 JD |
1267 | { |
1268 | const struct firmware *fw; | |
d3cf06bb | 1269 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
5afc9a25 JD |
1270 | struct cx24120_state *state = fe->demodulator_priv; |
1271 | struct cx24120_cmd cmd; | |
4133601c JD |
1272 | u8 reg; |
1273 | int ret, i; | |
5afc9a25 JD |
1274 | unsigned char vers[4]; |
1275 | ||
1276 | if (state->cold_init) | |
1277 | return 0; | |
1278 | ||
1279 | /* ???? */ | |
92443cdb JD |
1280 | cx24120_writereg(state, 0xea, 0x00); |
1281 | cx24120_test_rom(state); | |
1668797d JD |
1282 | reg = cx24120_readreg(state, 0xfb) & 0xfe; |
1283 | cx24120_writereg(state, 0xfb, reg); | |
1284 | reg = cx24120_readreg(state, 0xfc) & 0xfe; | |
1285 | cx24120_writereg(state, 0xfc, reg); | |
92443cdb JD |
1286 | cx24120_writereg(state, 0xc3, 0x04); |
1287 | cx24120_writereg(state, 0xc4, 0x04); | |
1288 | cx24120_writereg(state, 0xce, 0x00); | |
1289 | cx24120_writereg(state, 0xcf, 0x00); | |
1668797d JD |
1290 | reg = cx24120_readreg(state, 0xea) & 0xfe; |
1291 | cx24120_writereg(state, 0xea, reg); | |
92443cdb JD |
1292 | cx24120_writereg(state, 0xeb, 0x0c); |
1293 | cx24120_writereg(state, 0xec, 0x06); | |
1294 | cx24120_writereg(state, 0xed, 0x05); | |
1295 | cx24120_writereg(state, 0xee, 0x03); | |
1296 | cx24120_writereg(state, 0xef, 0x05); | |
1297 | cx24120_writereg(state, 0xf3, 0x03); | |
1298 | cx24120_writereg(state, 0xf4, 0x44); | |
5afc9a25 | 1299 | |
4133601c JD |
1300 | for (i = 0; i < 3; i++) { |
1301 | cx24120_writereg(state, 0xf0 + i, 0x04); | |
1302 | cx24120_writereg(state, 0xe6 + i, 0x02); | |
5afc9a25 JD |
1303 | } |
1304 | ||
1668797d | 1305 | cx24120_writereg(state, 0xea, (reg | 0x01)); |
4133601c JD |
1306 | for (i = 0; i < 6; i += 2) { |
1307 | cx24120_writereg(state, 0xc5 + i, 0x00); | |
1308 | cx24120_writereg(state, 0xc6 + i, 0x00); | |
5afc9a25 JD |
1309 | } |
1310 | ||
92443cdb JD |
1311 | cx24120_writereg(state, 0xe4, 0x03); |
1312 | cx24120_writereg(state, 0xeb, 0x0a); | |
5afc9a25 | 1313 | |
2f3f07fb JD |
1314 | dev_dbg(&state->i2c->dev, "requesting firmware (%s) to download...\n", |
1315 | CX24120_FIRMWARE); | |
5afc9a25 JD |
1316 | |
1317 | ret = state->config->request_firmware(fe, &fw, CX24120_FIRMWARE); | |
1318 | if (ret) { | |
1ff2e8ed PB |
1319 | err("Could not load firmware (%s): %d\n", CX24120_FIRMWARE, |
1320 | ret); | |
5afc9a25 JD |
1321 | return ret; |
1322 | } | |
1323 | ||
1324 | dev_dbg(&state->i2c->dev, | |
2f3f07fb | 1325 | "Firmware found, size %d bytes (%02x %02x .. %02x %02x)\n", |
5afc9a25 JD |
1326 | (int)fw->size, /* firmware_size in bytes */ |
1327 | fw->data[0], /* fw 1st byte */ | |
1328 | fw->data[1], /* fw 2d byte */ | |
1329 | fw->data[fw->size - 2], /* fw before last byte */ | |
1330 | fw->data[fw->size - 1]); /* fw last byte */ | |
1331 | ||
92443cdb | 1332 | cx24120_test_rom(state); |
1668797d JD |
1333 | reg = cx24120_readreg(state, 0xfb) & 0xfe; |
1334 | cx24120_writereg(state, 0xfb, reg); | |
92443cdb JD |
1335 | cx24120_writereg(state, 0xe0, 0x76); |
1336 | cx24120_writereg(state, 0xf7, 0x81); | |
1337 | cx24120_writereg(state, 0xf8, 0x00); | |
1338 | cx24120_writereg(state, 0xf9, 0x00); | |
1339 | cx24120_writeregs(state, 0xfa, fw->data, (fw->size - 1), 0x00); | |
1340 | cx24120_writereg(state, 0xf7, 0xc0); | |
1341 | cx24120_writereg(state, 0xe0, 0x00); | |
1668797d JD |
1342 | reg = (fw->size - 2) & 0x00ff; |
1343 | cx24120_writereg(state, 0xf8, reg); | |
1344 | reg = ((fw->size - 2) >> 8) & 0x00ff; | |
1345 | cx24120_writereg(state, 0xf9, reg); | |
92443cdb JD |
1346 | cx24120_writereg(state, 0xf7, 0x00); |
1347 | cx24120_writereg(state, 0xdc, 0x00); | |
1348 | cx24120_writereg(state, 0xdc, 0x07); | |
5afc9a25 JD |
1349 | msleep(500); |
1350 | ||
1351 | /* Check final byte matches final byte of firmware */ | |
1668797d JD |
1352 | reg = cx24120_readreg(state, 0xe1); |
1353 | if (reg == fw->data[fw->size - 1]) { | |
2f3f07fb | 1354 | dev_dbg(&state->i2c->dev, "Firmware uploaded successfully\n"); |
4133601c | 1355 | ret = 0; |
5afc9a25 JD |
1356 | } else { |
1357 | err("Firmware upload failed. Last byte returned=0x%x\n", ret); | |
4133601c | 1358 | ret = -EREMOTEIO; |
5afc9a25 | 1359 | } |
92443cdb | 1360 | cx24120_writereg(state, 0xdc, 0x00); |
5afc9a25 | 1361 | release_firmware(fw); |
4133601c JD |
1362 | if (ret != 0) |
1363 | return ret; | |
5afc9a25 | 1364 | |
5afc9a25 JD |
1365 | /* Start tuner */ |
1366 | cmd.id = CMD_START_TUNER; | |
1367 | cmd.len = 3; | |
1368 | cmd.arg[0] = 0x00; | |
1369 | cmd.arg[1] = 0x00; | |
1370 | cmd.arg[2] = 0x00; | |
1371 | ||
1372 | if (cx24120_message_send(state, &cmd) != 0) { | |
1373 | err("Error tuner start! :(\n"); | |
1374 | return -EREMOTEIO; | |
1375 | } | |
1376 | ||
1377 | /* Set VCO */ | |
c84251bb JD |
1378 | ret = cx24120_set_vco(state); |
1379 | if (ret != 0) { | |
5afc9a25 | 1380 | err("Error set VCO! :(\n"); |
c84251bb | 1381 | return ret; |
5afc9a25 JD |
1382 | } |
1383 | ||
5afc9a25 JD |
1384 | /* set bandwidth */ |
1385 | cmd.id = CMD_BANDWIDTH; | |
1386 | cmd.len = 12; | |
1387 | cmd.arg[0] = 0x00; | |
1388 | cmd.arg[1] = 0x00; | |
1389 | cmd.arg[2] = 0x00; | |
1390 | cmd.arg[3] = 0x00; | |
1391 | cmd.arg[4] = 0x05; | |
1392 | cmd.arg[5] = 0x02; | |
1393 | cmd.arg[6] = 0x02; | |
1394 | cmd.arg[7] = 0x00; | |
1395 | cmd.arg[8] = 0x05; | |
1396 | cmd.arg[9] = 0x02; | |
1397 | cmd.arg[10] = 0x02; | |
1398 | cmd.arg[11] = 0x00; | |
1399 | ||
1400 | if (cx24120_message_send(state, &cmd)) { | |
1401 | err("Error set bandwidth!\n"); | |
1402 | return -EREMOTEIO; | |
1403 | } | |
1404 | ||
1668797d JD |
1405 | reg = cx24120_readreg(state, 0xba); |
1406 | if (reg > 3) { | |
2f3f07fb | 1407 | dev_dbg(&state->i2c->dev, "Reset-readreg 0xba: %x\n", ret); |
5afc9a25 JD |
1408 | err("Error initialising tuner!\n"); |
1409 | return -EREMOTEIO; | |
1410 | } | |
1411 | ||
2f3f07fb | 1412 | dev_dbg(&state->i2c->dev, "Tuner initialised correctly.\n"); |
5afc9a25 JD |
1413 | |
1414 | /* Initialise mpeg outputs */ | |
92443cdb | 1415 | cx24120_writereg(state, 0xeb, 0x0a); |
5afc9a25 JD |
1416 | if (cx24120_msg_mpeg_output_global_config(state, 0) || |
1417 | cx24120_msg_mpeg_output_config(state, 0) || | |
1418 | cx24120_msg_mpeg_output_config(state, 1) || | |
1419 | cx24120_msg_mpeg_output_config(state, 2)) { | |
1420 | err("Error initialising mpeg output. :(\n"); | |
1421 | return -EREMOTEIO; | |
1422 | } | |
1423 | ||
ddcb252e JD |
1424 | /* Set size of BER window */ |
1425 | cmd.id = CMD_BER_CTRL; | |
5afc9a25 JD |
1426 | cmd.len = 3; |
1427 | cmd.arg[0] = 0x00; | |
ddcb252e JD |
1428 | cmd.arg[1] = CX24120_BER_WINDOW; |
1429 | cmd.arg[2] = CX24120_BER_WINDOW; | |
5afc9a25 | 1430 | if (cx24120_message_send(state, &cmd)) { |
ddcb252e | 1431 | err("Error setting ber window\n"); |
5afc9a25 JD |
1432 | return -EREMOTEIO; |
1433 | } | |
1434 | ||
5afc9a25 JD |
1435 | /* Firmware CMD 35: Get firmware version */ |
1436 | cmd.id = CMD_FWVERSION; | |
1437 | cmd.len = 1; | |
1438 | for (i = 0; i < 4; i++) { | |
1439 | cmd.arg[0] = i; | |
1440 | ret = cx24120_message_send(state, &cmd); | |
1441 | if (ret != 0) | |
1442 | return ret; | |
1443 | vers[i] = cx24120_readreg(state, CX24120_REG_MAILBOX); | |
1444 | } | |
1445 | info("FW version %i.%i.%i.%i\n", vers[0], vers[1], vers[2], vers[3]); | |
1446 | ||
d3cf06bb JD |
1447 | /* init stats here in order signal app which stats are supported */ |
1448 | c->strength.len = 1; | |
1449 | c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; | |
3b5eb504 JD |
1450 | c->cnr.len = 1; |
1451 | c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; | |
ddcb252e JD |
1452 | c->post_bit_error.len = 1; |
1453 | c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; | |
1454 | c->post_bit_count.len = 1; | |
1455 | c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; | |
80e9710b JD |
1456 | c->block_error.len = 1; |
1457 | c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; | |
1458 | c->block_count.len = 1; | |
1459 | c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; | |
1460 | ||
5afc9a25 | 1461 | state->cold_init = 1; |
71df6731 | 1462 | |
5afc9a25 JD |
1463 | return 0; |
1464 | } | |
1465 | ||
5afc9a25 | 1466 | static int cx24120_tune(struct dvb_frontend *fe, bool re_tune, |
1ff2e8ed | 1467 | unsigned int mode_flags, unsigned int *delay, |
0df289a2 | 1468 | enum fe_status *status) |
5afc9a25 JD |
1469 | { |
1470 | struct cx24120_state *state = fe->demodulator_priv; | |
1471 | int ret; | |
1472 | ||
2f3f07fb | 1473 | dev_dbg(&state->i2c->dev, "(%d)\n", re_tune); |
5afc9a25 JD |
1474 | |
1475 | /* TODO: Do we need to set delay? */ | |
1476 | ||
1477 | if (re_tune) { | |
1478 | ret = cx24120_set_frontend(fe); | |
1479 | if (ret) | |
1480 | return ret; | |
1481 | } | |
1482 | ||
1483 | return cx24120_read_status(fe, status); | |
1484 | } | |
1485 | ||
8d718e53 | 1486 | static enum dvbfe_algo cx24120_get_algo(struct dvb_frontend *fe) |
5afc9a25 JD |
1487 | { |
1488 | return DVBFE_ALGO_HW; | |
1489 | } | |
1490 | ||
5afc9a25 JD |
1491 | static int cx24120_sleep(struct dvb_frontend *fe) |
1492 | { | |
1493 | return 0; | |
1494 | } | |
1495 | ||
7e3e68bc MCC |
1496 | static int cx24120_get_frontend(struct dvb_frontend *fe, |
1497 | struct dtv_frontend_properties *c) | |
5afc9a25 | 1498 | { |
5afc9a25 JD |
1499 | struct cx24120_state *state = fe->demodulator_priv; |
1500 | u8 freq1, freq2, freq3; | |
035cad57 | 1501 | int status; |
5afc9a25 | 1502 | |
2f3f07fb | 1503 | dev_dbg(&state->i2c->dev, "\n"); |
5afc9a25 JD |
1504 | |
1505 | /* don't return empty data if we're not tuned in */ | |
035cad57 JD |
1506 | status = cx24120_readreg(state, CX24120_REG_STATUS); |
1507 | if (!(status & CX24120_HAS_LOCK)) | |
5afc9a25 JD |
1508 | return 0; |
1509 | ||
1510 | /* Get frequency */ | |
1511 | freq1 = cx24120_readreg(state, CX24120_REG_FREQ1); | |
1512 | freq2 = cx24120_readreg(state, CX24120_REG_FREQ2); | |
1513 | freq3 = cx24120_readreg(state, CX24120_REG_FREQ3); | |
1514 | c->frequency = (freq3 << 16) | (freq2 << 8) | freq1; | |
2f3f07fb | 1515 | dev_dbg(&state->i2c->dev, "frequency = %d\n", c->frequency); |
5afc9a25 JD |
1516 | |
1517 | /* Get modulation, fec, pilot */ | |
1518 | cx24120_get_fec(fe); | |
1519 | ||
1520 | return 0; | |
1521 | } | |
1522 | ||
5afc9a25 JD |
1523 | static void cx24120_release(struct dvb_frontend *fe) |
1524 | { | |
1525 | struct cx24120_state *state = fe->demodulator_priv; | |
1526 | ||
2f3f07fb | 1527 | dev_dbg(&state->i2c->dev, "Clear state structure\n"); |
5afc9a25 JD |
1528 | kfree(state); |
1529 | } | |
1530 | ||
5afc9a25 JD |
1531 | static int cx24120_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) |
1532 | { | |
1533 | struct cx24120_state *state = fe->demodulator_priv; | |
80e9710b JD |
1534 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
1535 | ||
1536 | if (c->block_error.stat[0].scale != FE_SCALE_COUNTER) { | |
1537 | *ucblocks = 0; | |
1538 | return 0; | |
1539 | } | |
5afc9a25 | 1540 | |
bf8de2d3 | 1541 | *ucblocks = c->block_error.stat[0].uvalue - state->ucb_offset; |
5afc9a25 | 1542 | |
5afc9a25 JD |
1543 | return 0; |
1544 | } | |
1545 | ||
bd336e63 | 1546 | static const struct dvb_frontend_ops cx24120_ops = { |
5afc9a25 JD |
1547 | .delsys = { SYS_DVBS, SYS_DVBS2 }, |
1548 | .info = { | |
1549 | .name = "Conexant CX24120/CX24118", | |
f1b1eabf MCC |
1550 | .frequency_min_hz = 950 * MHz, |
1551 | .frequency_max_hz = 2150 * MHz, | |
1552 | .frequency_stepsize_hz = 1011 * kHz, | |
1553 | .frequency_tolerance_hz = 5 * MHz, | |
5afc9a25 JD |
1554 | .symbol_rate_min = 1000000, |
1555 | .symbol_rate_max = 45000000, | |
1556 | .caps = FE_CAN_INVERSION_AUTO | | |
1557 | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | | |
1558 | FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | | |
1559 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | | |
1560 | FE_CAN_2G_MODULATION | | |
1561 | FE_CAN_QPSK | FE_CAN_RECOVER | |
1562 | }, | |
1563 | .release = cx24120_release, | |
1564 | ||
1565 | .init = cx24120_init, | |
1566 | .sleep = cx24120_sleep, | |
1567 | ||
1568 | .tune = cx24120_tune, | |
1569 | .get_frontend_algo = cx24120_get_algo, | |
1570 | .set_frontend = cx24120_set_frontend, | |
1571 | ||
1572 | .get_frontend = cx24120_get_frontend, | |
1573 | .read_status = cx24120_read_status, | |
1574 | .read_ber = cx24120_read_ber, | |
1575 | .read_signal_strength = cx24120_read_signal_strength, | |
1576 | .read_snr = cx24120_read_snr, | |
1577 | .read_ucblocks = cx24120_read_ucblocks, | |
1578 | ||
1579 | .diseqc_send_master_cmd = cx24120_send_diseqc_msg, | |
1580 | ||
1581 | .diseqc_send_burst = cx24120_diseqc_send_burst, | |
1582 | .set_tone = cx24120_set_tone, | |
1583 | .set_voltage = cx24120_set_voltage, | |
1584 | }; | |
1585 | ||
1586 | MODULE_DESCRIPTION("DVB Frontend module for Conexant CX24120/CX24118 hardware"); | |
1587 | MODULE_AUTHOR("Jemma Denson"); | |
1588 | MODULE_LICENSE("GPL"); |