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