diff options
Diffstat (limited to 'drivers/media/dvb/frontends/cxd2820r_t.c')
-rw-r--r-- | drivers/media/dvb/frontends/cxd2820r_t.c | 449 |
1 files changed, 449 insertions, 0 deletions
diff --git a/drivers/media/dvb/frontends/cxd2820r_t.c b/drivers/media/dvb/frontends/cxd2820r_t.c new file mode 100644 index 000000000000..6582564c930c --- /dev/null +++ b/drivers/media/dvb/frontends/cxd2820r_t.c | |||
@@ -0,0 +1,449 @@ | |||
1 | /* | ||
2 | * Sony CXD2820R demodulator driver | ||
3 | * | ||
4 | * Copyright (C) 2010 Antti Palosaari <crope@iki.fi> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along | ||
17 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | */ | ||
20 | |||
21 | |||
22 | #include "cxd2820r_priv.h" | ||
23 | |||
24 | int cxd2820r_set_frontend_t(struct dvb_frontend *fe, | ||
25 | struct dvb_frontend_parameters *p) | ||
26 | { | ||
27 | struct cxd2820r_priv *priv = fe->demodulator_priv; | ||
28 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; | ||
29 | int ret, i; | ||
30 | u32 if_khz, if_ctl; | ||
31 | u64 num; | ||
32 | u8 buf[3], bw_param; | ||
33 | u8 bw_params1[][5] = { | ||
34 | { 0x17, 0xea, 0xaa, 0xaa, 0xaa }, /* 6 MHz */ | ||
35 | { 0x14, 0x80, 0x00, 0x00, 0x00 }, /* 7 MHz */ | ||
36 | { 0x11, 0xf0, 0x00, 0x00, 0x00 }, /* 8 MHz */ | ||
37 | }; | ||
38 | u8 bw_params2[][2] = { | ||
39 | { 0x1f, 0xdc }, /* 6 MHz */ | ||
40 | { 0x12, 0xf8 }, /* 7 MHz */ | ||
41 | { 0x01, 0xe0 }, /* 8 MHz */ | ||
42 | }; | ||
43 | struct reg_val_mask tab[] = { | ||
44 | { 0x00080, 0x00, 0xff }, | ||
45 | { 0x00081, 0x03, 0xff }, | ||
46 | { 0x00085, 0x07, 0xff }, | ||
47 | { 0x00088, 0x01, 0xff }, | ||
48 | |||
49 | { 0x00070, priv->cfg.ts_mode, 0xff }, | ||
50 | { 0x000cb, priv->cfg.if_agc_polarity << 6, 0x40 }, | ||
51 | { 0x000a5, 0x00, 0x01 }, | ||
52 | { 0x00082, 0x20, 0x60 }, | ||
53 | { 0x000c2, 0xc3, 0xff }, | ||
54 | { 0x0016a, 0x50, 0xff }, | ||
55 | { 0x00427, 0x41, 0xff }, | ||
56 | }; | ||
57 | |||
58 | dbg("%s: RF=%d BW=%d", __func__, c->frequency, c->bandwidth_hz); | ||
59 | |||
60 | /* update GPIOs */ | ||
61 | ret = cxd2820r_gpio(fe); | ||
62 | if (ret) | ||
63 | goto error; | ||
64 | |||
65 | /* program tuner */ | ||
66 | if (fe->ops.tuner_ops.set_params) | ||
67 | fe->ops.tuner_ops.set_params(fe, p); | ||
68 | |||
69 | if (priv->delivery_system != SYS_DVBT) { | ||
70 | for (i = 0; i < ARRAY_SIZE(tab); i++) { | ||
71 | ret = cxd2820r_wr_reg_mask(priv, tab[i].reg, | ||
72 | tab[i].val, tab[i].mask); | ||
73 | if (ret) | ||
74 | goto error; | ||
75 | } | ||
76 | } | ||
77 | |||
78 | priv->delivery_system = SYS_DVBT; | ||
79 | priv->ber_running = 0; /* tune stops BER counter */ | ||
80 | |||
81 | switch (c->bandwidth_hz) { | ||
82 | case 6000000: | ||
83 | if_khz = priv->cfg.if_dvbt_6; | ||
84 | i = 0; | ||
85 | bw_param = 2; | ||
86 | break; | ||
87 | case 7000000: | ||
88 | if_khz = priv->cfg.if_dvbt_7; | ||
89 | i = 1; | ||
90 | bw_param = 1; | ||
91 | break; | ||
92 | case 8000000: | ||
93 | if_khz = priv->cfg.if_dvbt_8; | ||
94 | i = 2; | ||
95 | bw_param = 0; | ||
96 | break; | ||
97 | default: | ||
98 | return -EINVAL; | ||
99 | } | ||
100 | |||
101 | num = if_khz; | ||
102 | num *= 0x1000000; | ||
103 | if_ctl = cxd2820r_div_u64_round_closest(num, 41000); | ||
104 | buf[0] = ((if_ctl >> 16) & 0xff); | ||
105 | buf[1] = ((if_ctl >> 8) & 0xff); | ||
106 | buf[2] = ((if_ctl >> 0) & 0xff); | ||
107 | |||
108 | ret = cxd2820r_wr_regs(priv, 0x000b6, buf, 3); | ||
109 | if (ret) | ||
110 | goto error; | ||
111 | |||
112 | ret = cxd2820r_wr_regs(priv, 0x0009f, bw_params1[i], 5); | ||
113 | if (ret) | ||
114 | goto error; | ||
115 | |||
116 | ret = cxd2820r_wr_reg_mask(priv, 0x000d7, bw_param << 6, 0xc0); | ||
117 | if (ret) | ||
118 | goto error; | ||
119 | |||
120 | ret = cxd2820r_wr_regs(priv, 0x000d9, bw_params2[i], 2); | ||
121 | if (ret) | ||
122 | goto error; | ||
123 | |||
124 | ret = cxd2820r_wr_reg(priv, 0x000ff, 0x08); | ||
125 | if (ret) | ||
126 | goto error; | ||
127 | |||
128 | ret = cxd2820r_wr_reg(priv, 0x000fe, 0x01); | ||
129 | if (ret) | ||
130 | goto error; | ||
131 | |||
132 | return ret; | ||
133 | error: | ||
134 | dbg("%s: failed:%d", __func__, ret); | ||
135 | return ret; | ||
136 | } | ||
137 | |||
138 | int cxd2820r_get_frontend_t(struct dvb_frontend *fe, | ||
139 | struct dvb_frontend_parameters *p) | ||
140 | { | ||
141 | struct cxd2820r_priv *priv = fe->demodulator_priv; | ||
142 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; | ||
143 | int ret; | ||
144 | u8 buf[2]; | ||
145 | |||
146 | ret = cxd2820r_rd_regs(priv, 0x0002f, buf, sizeof(buf)); | ||
147 | if (ret) | ||
148 | goto error; | ||
149 | |||
150 | switch ((buf[0] >> 6) & 0x03) { | ||
151 | case 0: | ||
152 | c->modulation = QPSK; | ||
153 | break; | ||
154 | case 1: | ||
155 | c->modulation = QAM_16; | ||
156 | break; | ||
157 | case 2: | ||
158 | c->modulation = QAM_64; | ||
159 | break; | ||
160 | } | ||
161 | |||
162 | switch ((buf[1] >> 1) & 0x03) { | ||
163 | case 0: | ||
164 | c->transmission_mode = TRANSMISSION_MODE_2K; | ||
165 | break; | ||
166 | case 1: | ||
167 | c->transmission_mode = TRANSMISSION_MODE_8K; | ||
168 | break; | ||
169 | } | ||
170 | |||
171 | switch ((buf[1] >> 3) & 0x03) { | ||
172 | case 0: | ||
173 | c->guard_interval = GUARD_INTERVAL_1_32; | ||
174 | break; | ||
175 | case 1: | ||
176 | c->guard_interval = GUARD_INTERVAL_1_16; | ||
177 | break; | ||
178 | case 2: | ||
179 | c->guard_interval = GUARD_INTERVAL_1_8; | ||
180 | break; | ||
181 | case 3: | ||
182 | c->guard_interval = GUARD_INTERVAL_1_4; | ||
183 | break; | ||
184 | } | ||
185 | |||
186 | switch ((buf[0] >> 3) & 0x07) { | ||
187 | case 0: | ||
188 | c->hierarchy = HIERARCHY_NONE; | ||
189 | break; | ||
190 | case 1: | ||
191 | c->hierarchy = HIERARCHY_1; | ||
192 | break; | ||
193 | case 2: | ||
194 | c->hierarchy = HIERARCHY_2; | ||
195 | break; | ||
196 | case 3: | ||
197 | c->hierarchy = HIERARCHY_4; | ||
198 | break; | ||
199 | } | ||
200 | |||
201 | switch ((buf[0] >> 0) & 0x07) { | ||
202 | case 0: | ||
203 | c->code_rate_HP = FEC_1_2; | ||
204 | break; | ||
205 | case 1: | ||
206 | c->code_rate_HP = FEC_2_3; | ||
207 | break; | ||
208 | case 2: | ||
209 | c->code_rate_HP = FEC_3_4; | ||
210 | break; | ||
211 | case 3: | ||
212 | c->code_rate_HP = FEC_5_6; | ||
213 | break; | ||
214 | case 4: | ||
215 | c->code_rate_HP = FEC_7_8; | ||
216 | break; | ||
217 | } | ||
218 | |||
219 | switch ((buf[1] >> 5) & 0x07) { | ||
220 | case 0: | ||
221 | c->code_rate_LP = FEC_1_2; | ||
222 | break; | ||
223 | case 1: | ||
224 | c->code_rate_LP = FEC_2_3; | ||
225 | break; | ||
226 | case 2: | ||
227 | c->code_rate_LP = FEC_3_4; | ||
228 | break; | ||
229 | case 3: | ||
230 | c->code_rate_LP = FEC_5_6; | ||
231 | break; | ||
232 | case 4: | ||
233 | c->code_rate_LP = FEC_7_8; | ||
234 | break; | ||
235 | } | ||
236 | |||
237 | ret = cxd2820r_rd_reg(priv, 0x007c6, &buf[0]); | ||
238 | if (ret) | ||
239 | goto error; | ||
240 | |||
241 | switch ((buf[0] >> 0) & 0x01) { | ||
242 | case 0: | ||
243 | c->inversion = INVERSION_OFF; | ||
244 | break; | ||
245 | case 1: | ||
246 | c->inversion = INVERSION_ON; | ||
247 | break; | ||
248 | } | ||
249 | |||
250 | return ret; | ||
251 | error: | ||
252 | dbg("%s: failed:%d", __func__, ret); | ||
253 | return ret; | ||
254 | } | ||
255 | |||
256 | int cxd2820r_read_ber_t(struct dvb_frontend *fe, u32 *ber) | ||
257 | { | ||
258 | struct cxd2820r_priv *priv = fe->demodulator_priv; | ||
259 | int ret; | ||
260 | u8 buf[3], start_ber = 0; | ||
261 | *ber = 0; | ||
262 | |||
263 | if (priv->ber_running) { | ||
264 | ret = cxd2820r_rd_regs(priv, 0x00076, buf, sizeof(buf)); | ||
265 | if (ret) | ||
266 | goto error; | ||
267 | |||
268 | if ((buf[2] >> 7) & 0x01 || (buf[2] >> 4) & 0x01) { | ||
269 | *ber = (buf[2] & 0x0f) << 16 | buf[1] << 8 | buf[0]; | ||
270 | start_ber = 1; | ||
271 | } | ||
272 | } else { | ||
273 | priv->ber_running = 1; | ||
274 | start_ber = 1; | ||
275 | } | ||
276 | |||
277 | if (start_ber) { | ||
278 | /* (re)start BER */ | ||
279 | ret = cxd2820r_wr_reg(priv, 0x00079, 0x01); | ||
280 | if (ret) | ||
281 | goto error; | ||
282 | } | ||
283 | |||
284 | return ret; | ||
285 | error: | ||
286 | dbg("%s: failed:%d", __func__, ret); | ||
287 | return ret; | ||
288 | } | ||
289 | |||
290 | int cxd2820r_read_signal_strength_t(struct dvb_frontend *fe, | ||
291 | u16 *strength) | ||
292 | { | ||
293 | struct cxd2820r_priv *priv = fe->demodulator_priv; | ||
294 | int ret; | ||
295 | u8 buf[2]; | ||
296 | u16 tmp; | ||
297 | |||
298 | ret = cxd2820r_rd_regs(priv, 0x00026, buf, sizeof(buf)); | ||
299 | if (ret) | ||
300 | goto error; | ||
301 | |||
302 | tmp = (buf[0] & 0x0f) << 8 | buf[1]; | ||
303 | tmp = ~tmp & 0x0fff; | ||
304 | |||
305 | /* scale value to 0x0000-0xffff from 0x0000-0x0fff */ | ||
306 | *strength = tmp * 0xffff / 0x0fff; | ||
307 | |||
308 | return ret; | ||
309 | error: | ||
310 | dbg("%s: failed:%d", __func__, ret); | ||
311 | return ret; | ||
312 | } | ||
313 | |||
314 | int cxd2820r_read_snr_t(struct dvb_frontend *fe, u16 *snr) | ||
315 | { | ||
316 | struct cxd2820r_priv *priv = fe->demodulator_priv; | ||
317 | int ret; | ||
318 | u8 buf[2]; | ||
319 | u16 tmp; | ||
320 | /* report SNR in dB * 10 */ | ||
321 | |||
322 | ret = cxd2820r_rd_regs(priv, 0x00028, buf, sizeof(buf)); | ||
323 | if (ret) | ||
324 | goto error; | ||
325 | |||
326 | tmp = (buf[0] & 0x1f) << 8 | buf[1]; | ||
327 | #define CXD2820R_LOG10_8_24 15151336 /* log10(8) << 24 */ | ||
328 | if (tmp) | ||
329 | *snr = (intlog10(tmp) - CXD2820R_LOG10_8_24) / ((1 << 24) | ||
330 | / 100); | ||
331 | else | ||
332 | *snr = 0; | ||
333 | |||
334 | dbg("%s: dBx10=%d val=%04x", __func__, *snr, tmp); | ||
335 | |||
336 | return ret; | ||
337 | error: | ||
338 | dbg("%s: failed:%d", __func__, ret); | ||
339 | return ret; | ||
340 | } | ||
341 | |||
342 | int cxd2820r_read_ucblocks_t(struct dvb_frontend *fe, u32 *ucblocks) | ||
343 | { | ||
344 | *ucblocks = 0; | ||
345 | /* no way to read ? */ | ||
346 | return 0; | ||
347 | } | ||
348 | |||
349 | int cxd2820r_read_status_t(struct dvb_frontend *fe, fe_status_t *status) | ||
350 | { | ||
351 | struct cxd2820r_priv *priv = fe->demodulator_priv; | ||
352 | int ret; | ||
353 | u8 buf[4]; | ||
354 | *status = 0; | ||
355 | |||
356 | ret = cxd2820r_rd_reg(priv, 0x00010, &buf[0]); | ||
357 | if (ret) | ||
358 | goto error; | ||
359 | |||
360 | if ((buf[0] & 0x07) == 6) { | ||
361 | ret = cxd2820r_rd_reg(priv, 0x00073, &buf[1]); | ||
362 | if (ret) | ||
363 | goto error; | ||
364 | |||
365 | if (((buf[1] >> 3) & 0x01) == 1) { | ||
366 | *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | | ||
367 | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; | ||
368 | } else { | ||
369 | *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | | ||
370 | FE_HAS_VITERBI | FE_HAS_SYNC; | ||
371 | } | ||
372 | } else { | ||
373 | ret = cxd2820r_rd_reg(priv, 0x00014, &buf[2]); | ||
374 | if (ret) | ||
375 | goto error; | ||
376 | |||
377 | if ((buf[2] & 0x0f) >= 4) { | ||
378 | ret = cxd2820r_rd_reg(priv, 0x00a14, &buf[3]); | ||
379 | if (ret) | ||
380 | goto error; | ||
381 | |||
382 | if (((buf[3] >> 4) & 0x01) == 1) | ||
383 | *status |= FE_HAS_SIGNAL; | ||
384 | } | ||
385 | } | ||
386 | |||
387 | dbg("%s: lock=%02x %02x %02x %02x", __func__, | ||
388 | buf[0], buf[1], buf[2], buf[3]); | ||
389 | |||
390 | return ret; | ||
391 | error: | ||
392 | dbg("%s: failed:%d", __func__, ret); | ||
393 | return ret; | ||
394 | } | ||
395 | |||
396 | int cxd2820r_init_t(struct dvb_frontend *fe) | ||
397 | { | ||
398 | struct cxd2820r_priv *priv = fe->demodulator_priv; | ||
399 | int ret; | ||
400 | |||
401 | ret = cxd2820r_wr_reg(priv, 0x00085, 0x07); | ||
402 | if (ret) | ||
403 | goto error; | ||
404 | |||
405 | return ret; | ||
406 | error: | ||
407 | dbg("%s: failed:%d", __func__, ret); | ||
408 | return ret; | ||
409 | } | ||
410 | |||
411 | int cxd2820r_sleep_t(struct dvb_frontend *fe) | ||
412 | { | ||
413 | struct cxd2820r_priv *priv = fe->demodulator_priv; | ||
414 | int ret, i; | ||
415 | struct reg_val_mask tab[] = { | ||
416 | { 0x000ff, 0x1f, 0xff }, | ||
417 | { 0x00085, 0x00, 0xff }, | ||
418 | { 0x00088, 0x01, 0xff }, | ||
419 | { 0x00081, 0x00, 0xff }, | ||
420 | { 0x00080, 0x00, 0xff }, | ||
421 | }; | ||
422 | |||
423 | dbg("%s", __func__); | ||
424 | |||
425 | priv->delivery_system = SYS_UNDEFINED; | ||
426 | |||
427 | for (i = 0; i < ARRAY_SIZE(tab); i++) { | ||
428 | ret = cxd2820r_wr_reg_mask(priv, tab[i].reg, tab[i].val, | ||
429 | tab[i].mask); | ||
430 | if (ret) | ||
431 | goto error; | ||
432 | } | ||
433 | |||
434 | return ret; | ||
435 | error: | ||
436 | dbg("%s: failed:%d", __func__, ret); | ||
437 | return ret; | ||
438 | } | ||
439 | |||
440 | int cxd2820r_get_tune_settings_t(struct dvb_frontend *fe, | ||
441 | struct dvb_frontend_tune_settings *s) | ||
442 | { | ||
443 | s->min_delay_ms = 500; | ||
444 | s->step_size = fe->ops.info.frequency_stepsize * 2; | ||
445 | s->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1; | ||
446 | |||
447 | return 0; | ||
448 | } | ||
449 | |||