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