1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * mxl111sf-tuner.c - driver for the MaxLinear MXL111SF CMOS tuner |
4 | * |
5 | * Copyright (C) 2010-2014 Michael Krufky <mkrufky@linuxtv.org> |
6 | */ |
7 | |
8 | #include "mxl111sf-tuner.h" |
9 | #include "mxl111sf-phy.h" |
10 | #include "mxl111sf-reg.h" |
11 | |
12 | /* debug */ |
13 | static int mxl111sf_tuner_debug; |
14 | module_param_named(debug, mxl111sf_tuner_debug, int, 0644); |
15 | MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))." ); |
16 | |
17 | #define mxl_dbg(fmt, arg...) \ |
18 | if (mxl111sf_tuner_debug) \ |
19 | mxl_printk(KERN_DEBUG, fmt, ##arg) |
20 | |
21 | /* ------------------------------------------------------------------------ */ |
22 | |
23 | struct mxl111sf_tuner_state { |
24 | struct mxl111sf_state *mxl_state; |
25 | |
26 | const struct mxl111sf_tuner_config *cfg; |
27 | |
28 | enum mxl_if_freq if_freq; |
29 | |
30 | u32 frequency; |
31 | u32 bandwidth; |
32 | }; |
33 | |
34 | static int mxl111sf_tuner_read_reg(struct mxl111sf_tuner_state *state, |
35 | u8 addr, u8 *data) |
36 | { |
37 | return (state->cfg->read_reg) ? |
38 | state->cfg->read_reg(state->mxl_state, addr, data) : |
39 | -EINVAL; |
40 | } |
41 | |
42 | static int mxl111sf_tuner_write_reg(struct mxl111sf_tuner_state *state, |
43 | u8 addr, u8 data) |
44 | { |
45 | return (state->cfg->write_reg) ? |
46 | state->cfg->write_reg(state->mxl_state, addr, data) : |
47 | -EINVAL; |
48 | } |
49 | |
50 | static int mxl111sf_tuner_program_regs(struct mxl111sf_tuner_state *state, |
51 | struct mxl111sf_reg_ctrl_info *ctrl_reg_info) |
52 | { |
53 | return (state->cfg->program_regs) ? |
54 | state->cfg->program_regs(state->mxl_state, ctrl_reg_info) : |
55 | -EINVAL; |
56 | } |
57 | |
58 | static int mxl1x1sf_tuner_top_master_ctrl(struct mxl111sf_tuner_state *state, |
59 | int onoff) |
60 | { |
61 | return (state->cfg->top_master_ctrl) ? |
62 | state->cfg->top_master_ctrl(state->mxl_state, onoff) : |
63 | -EINVAL; |
64 | } |
65 | |
66 | /* ------------------------------------------------------------------------ */ |
67 | |
68 | static struct mxl111sf_reg_ctrl_info mxl_phy_tune_rf[] = { |
69 | {0x1d, 0x7f, 0x00}, /* channel bandwidth section 1/2/3, |
70 | DIG_MODEINDEX, _A, _CSF, */ |
71 | {0x1e, 0xff, 0x00}, /* channel frequency (lo and fractional) */ |
72 | {0x1f, 0xff, 0x00}, /* channel frequency (hi for integer portion) */ |
73 | {0, 0, 0} |
74 | }; |
75 | |
76 | /* ------------------------------------------------------------------------ */ |
77 | |
78 | static struct mxl111sf_reg_ctrl_info *mxl111sf_calc_phy_tune_regs(u32 freq, |
79 | u8 bw) |
80 | { |
81 | u8 filt_bw; |
82 | |
83 | /* set channel bandwidth */ |
84 | switch (bw) { |
85 | case 0: /* ATSC */ |
86 | filt_bw = 25; |
87 | break; |
88 | case 1: /* QAM */ |
89 | filt_bw = 69; |
90 | break; |
91 | case 6: |
92 | filt_bw = 21; |
93 | break; |
94 | case 7: |
95 | filt_bw = 42; |
96 | break; |
97 | case 8: |
98 | filt_bw = 63; |
99 | break; |
100 | default: |
101 | pr_err("%s: invalid bandwidth setting!" , __func__); |
102 | return NULL; |
103 | } |
104 | |
105 | /* calculate RF channel */ |
106 | freq /= 1000000; |
107 | |
108 | freq *= 64; |
109 | #if 0 |
110 | /* do round */ |
111 | freq += 0.5; |
112 | #endif |
113 | /* set bandwidth */ |
114 | mxl_phy_tune_rf[0].data = filt_bw; |
115 | |
116 | /* set RF */ |
117 | mxl_phy_tune_rf[1].data = (freq & 0xff); |
118 | mxl_phy_tune_rf[2].data = (freq >> 8) & 0xff; |
119 | |
120 | /* start tune */ |
121 | return mxl_phy_tune_rf; |
122 | } |
123 | |
124 | static int mxl1x1sf_tuner_set_if_output_freq(struct mxl111sf_tuner_state *state) |
125 | { |
126 | int ret; |
127 | u8 ctrl; |
128 | #if 0 |
129 | u16 iffcw; |
130 | u32 if_freq; |
131 | #endif |
132 | mxl_dbg("(IF polarity = %d, IF freq = 0x%02x)" , |
133 | state->cfg->invert_spectrum, state->cfg->if_freq); |
134 | |
135 | /* set IF polarity */ |
136 | ctrl = state->cfg->invert_spectrum; |
137 | |
138 | ctrl |= state->cfg->if_freq; |
139 | |
140 | ret = mxl111sf_tuner_write_reg(state, V6_TUNER_IF_SEL_REG, data: ctrl); |
141 | if (mxl_fail(ret)) |
142 | goto fail; |
143 | |
144 | #if 0 |
145 | if_freq /= 1000000; |
146 | |
147 | /* do round */ |
148 | if_freq += 0.5; |
149 | |
150 | if (MXL_IF_LO == state->cfg->if_freq) { |
151 | ctrl = 0x08; |
152 | iffcw = (u16)(if_freq / (108 * 4096)); |
153 | } else if (MXL_IF_HI == state->cfg->if_freq) { |
154 | ctrl = 0x08; |
155 | iffcw = (u16)(if_freq / (216 * 4096)); |
156 | } else { |
157 | ctrl = 0; |
158 | iffcw = 0; |
159 | } |
160 | |
161 | ctrl |= (iffcw >> 8); |
162 | #endif |
163 | ret = mxl111sf_tuner_read_reg(state, V6_TUNER_IF_FCW_BYP_REG, data: &ctrl); |
164 | if (mxl_fail(ret)) |
165 | goto fail; |
166 | |
167 | ctrl &= 0xf0; |
168 | ctrl |= 0x90; |
169 | |
170 | ret = mxl111sf_tuner_write_reg(state, V6_TUNER_IF_FCW_BYP_REG, data: ctrl); |
171 | if (mxl_fail(ret)) |
172 | goto fail; |
173 | |
174 | #if 0 |
175 | ctrl = iffcw & 0x00ff; |
176 | #endif |
177 | ret = mxl111sf_tuner_write_reg(state, V6_TUNER_IF_FCW_REG, data: ctrl); |
178 | if (mxl_fail(ret)) |
179 | goto fail; |
180 | |
181 | state->if_freq = state->cfg->if_freq; |
182 | fail: |
183 | return ret; |
184 | } |
185 | |
186 | static int mxl1x1sf_tune_rf(struct dvb_frontend *fe, u32 freq, u8 bw) |
187 | { |
188 | struct mxl111sf_tuner_state *state = fe->tuner_priv; |
189 | static struct mxl111sf_reg_ctrl_info *reg_ctrl_array; |
190 | int ret; |
191 | u8 mxl_mode; |
192 | |
193 | mxl_dbg("(freq = %d, bw = 0x%x)" , freq, bw); |
194 | |
195 | /* stop tune */ |
196 | ret = mxl111sf_tuner_write_reg(state, START_TUNE_REG, data: 0); |
197 | if (mxl_fail(ret)) |
198 | goto fail; |
199 | |
200 | /* check device mode */ |
201 | ret = mxl111sf_tuner_read_reg(state, MXL_MODE_REG, data: &mxl_mode); |
202 | if (mxl_fail(ret)) |
203 | goto fail; |
204 | |
205 | /* Fill out registers for channel tune */ |
206 | reg_ctrl_array = mxl111sf_calc_phy_tune_regs(freq, bw); |
207 | if (!reg_ctrl_array) |
208 | return -EINVAL; |
209 | |
210 | ret = mxl111sf_tuner_program_regs(state, ctrl_reg_info: reg_ctrl_array); |
211 | if (mxl_fail(ret)) |
212 | goto fail; |
213 | |
214 | if ((mxl_mode & MXL_DEV_MODE_MASK) == MXL_TUNER_MODE) { |
215 | /* IF tuner mode only */ |
216 | mxl1x1sf_tuner_top_master_ctrl(state, onoff: 0); |
217 | mxl1x1sf_tuner_top_master_ctrl(state, onoff: 1); |
218 | mxl1x1sf_tuner_set_if_output_freq(state); |
219 | } |
220 | |
221 | ret = mxl111sf_tuner_write_reg(state, START_TUNE_REG, data: 1); |
222 | if (mxl_fail(ret)) |
223 | goto fail; |
224 | |
225 | if (state->cfg->ant_hunt) |
226 | state->cfg->ant_hunt(fe); |
227 | fail: |
228 | return ret; |
229 | } |
230 | |
231 | static int mxl1x1sf_tuner_get_lock_status(struct mxl111sf_tuner_state *state, |
232 | int *rf_synth_lock, |
233 | int *ref_synth_lock) |
234 | { |
235 | int ret; |
236 | u8 data; |
237 | |
238 | *rf_synth_lock = 0; |
239 | *ref_synth_lock = 0; |
240 | |
241 | ret = mxl111sf_tuner_read_reg(state, V6_RF_LOCK_STATUS_REG, data: &data); |
242 | if (mxl_fail(ret)) |
243 | goto fail; |
244 | |
245 | *ref_synth_lock = ((data & 0x03) == 0x03) ? 1 : 0; |
246 | *rf_synth_lock = ((data & 0x0c) == 0x0c) ? 1 : 0; |
247 | fail: |
248 | return ret; |
249 | } |
250 | |
251 | #if 0 |
252 | static int mxl1x1sf_tuner_loop_thru_ctrl(struct mxl111sf_tuner_state *state, |
253 | int onoff) |
254 | { |
255 | return mxl111sf_tuner_write_reg(state, V6_TUNER_LOOP_THRU_CTRL_REG, |
256 | onoff ? 1 : 0); |
257 | } |
258 | #endif |
259 | |
260 | /* ------------------------------------------------------------------------ */ |
261 | |
262 | static int mxl111sf_tuner_set_params(struct dvb_frontend *fe) |
263 | { |
264 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
265 | u32 delsys = c->delivery_system; |
266 | struct mxl111sf_tuner_state *state = fe->tuner_priv; |
267 | int ret; |
268 | u8 bw; |
269 | |
270 | mxl_dbg("()" ); |
271 | |
272 | switch (delsys) { |
273 | case SYS_ATSC: |
274 | case SYS_ATSCMH: |
275 | bw = 0; /* ATSC */ |
276 | break; |
277 | case SYS_DVBC_ANNEX_B: |
278 | bw = 1; /* US CABLE */ |
279 | break; |
280 | case SYS_DVBT: |
281 | switch (c->bandwidth_hz) { |
282 | case 6000000: |
283 | bw = 6; |
284 | break; |
285 | case 7000000: |
286 | bw = 7; |
287 | break; |
288 | case 8000000: |
289 | bw = 8; |
290 | break; |
291 | default: |
292 | pr_err("%s: bandwidth not set!" , __func__); |
293 | return -EINVAL; |
294 | } |
295 | break; |
296 | default: |
297 | pr_err("%s: modulation type not supported!" , __func__); |
298 | return -EINVAL; |
299 | } |
300 | ret = mxl1x1sf_tune_rf(fe, freq: c->frequency, bw); |
301 | if (mxl_fail(ret)) |
302 | goto fail; |
303 | |
304 | state->frequency = c->frequency; |
305 | state->bandwidth = c->bandwidth_hz; |
306 | fail: |
307 | return ret; |
308 | } |
309 | |
310 | /* ------------------------------------------------------------------------ */ |
311 | |
312 | #if 0 |
313 | static int mxl111sf_tuner_init(struct dvb_frontend *fe) |
314 | { |
315 | struct mxl111sf_tuner_state *state = fe->tuner_priv; |
316 | int ret; |
317 | |
318 | /* wake from standby handled by usb driver */ |
319 | |
320 | return ret; |
321 | } |
322 | |
323 | static int mxl111sf_tuner_sleep(struct dvb_frontend *fe) |
324 | { |
325 | struct mxl111sf_tuner_state *state = fe->tuner_priv; |
326 | int ret; |
327 | |
328 | /* enter standby mode handled by usb driver */ |
329 | |
330 | return ret; |
331 | } |
332 | #endif |
333 | |
334 | /* ------------------------------------------------------------------------ */ |
335 | |
336 | static int mxl111sf_tuner_get_status(struct dvb_frontend *fe, u32 *status) |
337 | { |
338 | struct mxl111sf_tuner_state *state = fe->tuner_priv; |
339 | int rf_locked, ref_locked, ret; |
340 | |
341 | *status = 0; |
342 | |
343 | ret = mxl1x1sf_tuner_get_lock_status(state, rf_synth_lock: &rf_locked, ref_synth_lock: &ref_locked); |
344 | if (mxl_fail(ret)) |
345 | goto fail; |
346 | mxl_info("%s%s" , rf_locked ? "rf locked " : "" , |
347 | ref_locked ? "ref locked" : "" ); |
348 | |
349 | if ((rf_locked) || (ref_locked)) |
350 | *status |= TUNER_STATUS_LOCKED; |
351 | fail: |
352 | return ret; |
353 | } |
354 | |
355 | static int mxl111sf_get_rf_strength(struct dvb_frontend *fe, u16 *strength) |
356 | { |
357 | struct mxl111sf_tuner_state *state = fe->tuner_priv; |
358 | u8 val1, val2; |
359 | int ret; |
360 | |
361 | *strength = 0; |
362 | |
363 | ret = mxl111sf_tuner_write_reg(state, addr: 0x00, data: 0x02); |
364 | if (mxl_fail(ret)) |
365 | goto fail; |
366 | ret = mxl111sf_tuner_read_reg(state, V6_DIG_RF_PWR_LSB_REG, data: &val1); |
367 | if (mxl_fail(ret)) |
368 | goto fail; |
369 | ret = mxl111sf_tuner_read_reg(state, V6_DIG_RF_PWR_MSB_REG, data: &val2); |
370 | if (mxl_fail(ret)) |
371 | goto fail; |
372 | |
373 | *strength = val1 | ((val2 & 0x07) << 8); |
374 | fail: |
375 | ret = mxl111sf_tuner_write_reg(state, addr: 0x00, data: 0x00); |
376 | mxl_fail(ret); |
377 | |
378 | return ret; |
379 | } |
380 | |
381 | /* ------------------------------------------------------------------------ */ |
382 | |
383 | static int mxl111sf_tuner_get_frequency(struct dvb_frontend *fe, u32 *frequency) |
384 | { |
385 | struct mxl111sf_tuner_state *state = fe->tuner_priv; |
386 | *frequency = state->frequency; |
387 | return 0; |
388 | } |
389 | |
390 | static int mxl111sf_tuner_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) |
391 | { |
392 | struct mxl111sf_tuner_state *state = fe->tuner_priv; |
393 | *bandwidth = state->bandwidth; |
394 | return 0; |
395 | } |
396 | |
397 | static int mxl111sf_tuner_get_if_frequency(struct dvb_frontend *fe, |
398 | u32 *frequency) |
399 | { |
400 | struct mxl111sf_tuner_state *state = fe->tuner_priv; |
401 | |
402 | *frequency = 0; |
403 | |
404 | switch (state->if_freq) { |
405 | case MXL_IF_4_0: /* 4.0 MHz */ |
406 | *frequency = 4000000; |
407 | break; |
408 | case MXL_IF_4_5: /* 4.5 MHz */ |
409 | *frequency = 4500000; |
410 | break; |
411 | case MXL_IF_4_57: /* 4.57 MHz */ |
412 | *frequency = 4570000; |
413 | break; |
414 | case MXL_IF_5_0: /* 5.0 MHz */ |
415 | *frequency = 5000000; |
416 | break; |
417 | case MXL_IF_5_38: /* 5.38 MHz */ |
418 | *frequency = 5380000; |
419 | break; |
420 | case MXL_IF_6_0: /* 6.0 MHz */ |
421 | *frequency = 6000000; |
422 | break; |
423 | case MXL_IF_6_28: /* 6.28 MHz */ |
424 | *frequency = 6280000; |
425 | break; |
426 | case MXL_IF_7_2: /* 7.2 MHz */ |
427 | *frequency = 7200000; |
428 | break; |
429 | case MXL_IF_35_25: /* 35.25 MHz */ |
430 | *frequency = 35250000; |
431 | break; |
432 | case MXL_IF_36: /* 36 MHz */ |
433 | *frequency = 36000000; |
434 | break; |
435 | case MXL_IF_36_15: /* 36.15 MHz */ |
436 | *frequency = 36150000; |
437 | break; |
438 | case MXL_IF_44: /* 44 MHz */ |
439 | *frequency = 44000000; |
440 | break; |
441 | } |
442 | return 0; |
443 | } |
444 | |
445 | static void mxl111sf_tuner_release(struct dvb_frontend *fe) |
446 | { |
447 | struct mxl111sf_tuner_state *state = fe->tuner_priv; |
448 | mxl_dbg("()" ); |
449 | kfree(objp: state); |
450 | fe->tuner_priv = NULL; |
451 | } |
452 | |
453 | /* ------------------------------------------------------------------------- */ |
454 | |
455 | static const struct dvb_tuner_ops mxl111sf_tuner_tuner_ops = { |
456 | .info = { |
457 | .name = "MaxLinear MxL111SF" , |
458 | #if 0 |
459 | .frequency_min_hz = , |
460 | .frequency_max_hz = , |
461 | .frequency_step_hz = , |
462 | #endif |
463 | }, |
464 | #if 0 |
465 | .init = mxl111sf_tuner_init, |
466 | .sleep = mxl111sf_tuner_sleep, |
467 | #endif |
468 | .set_params = mxl111sf_tuner_set_params, |
469 | .get_status = mxl111sf_tuner_get_status, |
470 | .get_rf_strength = mxl111sf_get_rf_strength, |
471 | .get_frequency = mxl111sf_tuner_get_frequency, |
472 | .get_bandwidth = mxl111sf_tuner_get_bandwidth, |
473 | .get_if_frequency = mxl111sf_tuner_get_if_frequency, |
474 | .release = mxl111sf_tuner_release, |
475 | }; |
476 | |
477 | struct dvb_frontend *mxl111sf_tuner_attach(struct dvb_frontend *fe, |
478 | struct mxl111sf_state *mxl_state, |
479 | const struct mxl111sf_tuner_config *cfg) |
480 | { |
481 | struct mxl111sf_tuner_state *state = NULL; |
482 | |
483 | mxl_dbg("()" ); |
484 | |
485 | state = kzalloc(size: sizeof(struct mxl111sf_tuner_state), GFP_KERNEL); |
486 | if (state == NULL) |
487 | return NULL; |
488 | |
489 | state->mxl_state = mxl_state; |
490 | state->cfg = cfg; |
491 | |
492 | memcpy(&fe->ops.tuner_ops, &mxl111sf_tuner_tuner_ops, |
493 | sizeof(struct dvb_tuner_ops)); |
494 | |
495 | fe->tuner_priv = state; |
496 | return fe; |
497 | } |
498 | EXPORT_SYMBOL_GPL(mxl111sf_tuner_attach); |
499 | |
500 | MODULE_DESCRIPTION("MaxLinear MxL111SF CMOS tuner driver" ); |
501 | MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>" ); |
502 | MODULE_LICENSE("GPL" ); |
503 | MODULE_VERSION("0.1" ); |
504 | |