]>
Commit | Line | Data |
---|---|---|
dadb5bb4 AP |
1 | /* |
2 | * Panasonic MN88473 DVB-T/T2/C demodulator driver | |
3 | * | |
4 | * Copyright (C) 2014 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 | ||
17 | #include "mn88473_priv.h" | |
18 | ||
dadb5bb4 | 19 | static int mn88473_get_tune_settings(struct dvb_frontend *fe, |
b4c2c314 | 20 | struct dvb_frontend_tune_settings *s) |
dadb5bb4 AP |
21 | { |
22 | s->min_delay_ms = 1000; | |
23 | return 0; | |
24 | } | |
25 | ||
26 | static int mn88473_set_frontend(struct dvb_frontend *fe) | |
27 | { | |
01b4be14 AP |
28 | struct i2c_client *client = fe->demodulator_priv; |
29 | struct mn88473_dev *dev = i2c_get_clientdata(client); | |
dadb5bb4 | 30 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
c00a6b9f | 31 | int ret, i; |
7908fad9 | 32 | unsigned int uitmp; |
df810e8a | 33 | u32 if_frequency; |
7908fad9 AP |
34 | u8 delivery_system_val, if_val[3], *conf_val_ptr; |
35 | u8 reg_bank2_2d_val, reg_bank0_d2_val; | |
dadb5bb4 | 36 | |
01b4be14 | 37 | dev_dbg(&client->dev, |
b4c2c314 | 38 | "delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%d stream_id=%d\n", |
7908fad9 AP |
39 | c->delivery_system, c->modulation, c->frequency, |
40 | c->bandwidth_hz, c->symbol_rate, c->inversion, c->stream_id); | |
41 | ||
42 | if (!dev->active) { | |
dadb5bb4 AP |
43 | ret = -EAGAIN; |
44 | goto err; | |
45 | } | |
46 | ||
c00a6b9f | 47 | switch (c->delivery_system) { |
6ebbe22d | 48 | case SYS_DVBT: |
df810e8a | 49 | delivery_system_val = 0x02; |
7908fad9 AP |
50 | reg_bank2_2d_val = 0x23; |
51 | reg_bank0_d2_val = 0x2a; | |
6ebbe22d | 52 | break; |
c00a6b9f | 53 | case SYS_DVBT2: |
df810e8a | 54 | delivery_system_val = 0x03; |
7908fad9 AP |
55 | reg_bank2_2d_val = 0x3b; |
56 | reg_bank0_d2_val = 0x29; | |
c00a6b9f AP |
57 | break; |
58 | case SYS_DVBC_ANNEX_A: | |
df810e8a | 59 | delivery_system_val = 0x04; |
7908fad9 AP |
60 | reg_bank2_2d_val = 0x3b; |
61 | reg_bank0_d2_val = 0x29; | |
df810e8a AP |
62 | break; |
63 | default: | |
64 | ret = -EINVAL; | |
65 | goto err; | |
66 | } | |
67 | ||
7908fad9 AP |
68 | switch (c->delivery_system) { |
69 | case SYS_DVBT: | |
70 | case SYS_DVBT2: | |
71 | switch (c->bandwidth_hz) { | |
72 | case 6000000: | |
73 | conf_val_ptr = "\xe9\x55\x55\x1c\x29\x1c\x29"; | |
74 | break; | |
75 | case 7000000: | |
76 | conf_val_ptr = "\xc8\x00\x00\x17\x0a\x17\x0a"; | |
77 | break; | |
78 | case 8000000: | |
79 | conf_val_ptr = "\xaf\x00\x00\x11\xec\x11\xec"; | |
80 | break; | |
81 | default: | |
82 | ret = -EINVAL; | |
83 | goto err; | |
84 | } | |
85 | break; | |
86 | case SYS_DVBC_ANNEX_A: | |
87 | conf_val_ptr = "\x10\xab\x0d\xae\x1d\x9d"; | |
88 | break; | |
89 | default: | |
90 | break; | |
c00a6b9f AP |
91 | } |
92 | ||
7908fad9 | 93 | /* Program tuner */ |
dadb5bb4 AP |
94 | if (fe->ops.tuner_ops.set_params) { |
95 | ret = fe->ops.tuner_ops.set_params(fe); | |
96 | if (ret) | |
97 | goto err; | |
98 | } | |
99 | ||
100 | if (fe->ops.tuner_ops.get_if_frequency) { | |
101 | ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency); | |
102 | if (ret) | |
103 | goto err; | |
104 | ||
7908fad9 | 105 | dev_dbg(&client->dev, "get_if_frequency=%u\n", if_frequency); |
df810e8a | 106 | } else { |
7908fad9 AP |
107 | ret = -EINVAL; |
108 | goto err; | |
dadb5bb4 AP |
109 | } |
110 | ||
7908fad9 AP |
111 | /* Calculate IF registers */ |
112 | uitmp = DIV_ROUND_CLOSEST_ULL((u64) if_frequency * 0x1000000, dev->clk); | |
113 | if_val[0] = (uitmp >> 16) & 0xff; | |
114 | if_val[1] = (uitmp >> 8) & 0xff; | |
115 | if_val[2] = (uitmp >> 0) & 0xff; | |
dadb5bb4 | 116 | |
97de6e89 | 117 | ret = regmap_write(dev->regmap[2], 0x05, 0x00); |
7908fad9 AP |
118 | if (ret) |
119 | goto err; | |
97de6e89 | 120 | ret = regmap_write(dev->regmap[2], 0xfb, 0x13); |
7908fad9 AP |
121 | if (ret) |
122 | goto err; | |
97de6e89 | 123 | ret = regmap_write(dev->regmap[2], 0xef, 0x13); |
7908fad9 AP |
124 | if (ret) |
125 | goto err; | |
97de6e89 | 126 | ret = regmap_write(dev->regmap[2], 0xf9, 0x13); |
7908fad9 AP |
127 | if (ret) |
128 | goto err; | |
97de6e89 | 129 | ret = regmap_write(dev->regmap[2], 0x00, 0x18); |
7908fad9 AP |
130 | if (ret) |
131 | goto err; | |
97de6e89 | 132 | ret = regmap_write(dev->regmap[2], 0x01, 0x01); |
7908fad9 AP |
133 | if (ret) |
134 | goto err; | |
97de6e89 | 135 | ret = regmap_write(dev->regmap[2], 0x02, 0x21); |
7908fad9 AP |
136 | if (ret) |
137 | goto err; | |
97de6e89 | 138 | ret = regmap_write(dev->regmap[2], 0x03, delivery_system_val); |
7908fad9 AP |
139 | if (ret) |
140 | goto err; | |
97de6e89 | 141 | ret = regmap_write(dev->regmap[2], 0x0b, 0x00); |
7908fad9 AP |
142 | if (ret) |
143 | goto err; | |
c00a6b9f | 144 | |
df810e8a | 145 | for (i = 0; i < sizeof(if_val); i++) { |
97de6e89 | 146 | ret = regmap_write(dev->regmap[2], 0x10 + i, if_val[i]); |
df810e8a AP |
147 | if (ret) |
148 | goto err; | |
149 | } | |
150 | ||
7908fad9 AP |
151 | switch (c->delivery_system) { |
152 | case SYS_DVBT: | |
153 | case SYS_DVBT2: | |
154 | for (i = 0; i < 7; i++) { | |
155 | ret = regmap_write(dev->regmap[2], 0x13 + i, | |
156 | conf_val_ptr[i]); | |
157 | if (ret) | |
158 | goto err; | |
159 | } | |
160 | break; | |
161 | case SYS_DVBC_ANNEX_A: | |
162 | ret = regmap_bulk_write(dev->regmap[1], 0x10, conf_val_ptr, 6); | |
c00a6b9f AP |
163 | if (ret) |
164 | goto err; | |
7908fad9 AP |
165 | break; |
166 | default: | |
167 | break; | |
c00a6b9f AP |
168 | } |
169 | ||
7908fad9 AP |
170 | ret = regmap_write(dev->regmap[2], 0x2d, reg_bank2_2d_val); |
171 | if (ret) | |
172 | goto err; | |
97de6e89 | 173 | ret = regmap_write(dev->regmap[2], 0x2e, 0x00); |
7908fad9 AP |
174 | if (ret) |
175 | goto err; | |
97de6e89 | 176 | ret = regmap_write(dev->regmap[2], 0x56, 0x0d); |
7908fad9 AP |
177 | if (ret) |
178 | goto err; | |
179 | ret = regmap_bulk_write(dev->regmap[0], 0x01, | |
180 | "\xba\x13\x80\xba\x91\xdd\xe7\x28", 8); | |
181 | if (ret) | |
182 | goto err; | |
97de6e89 | 183 | ret = regmap_write(dev->regmap[0], 0x0a, 0x1a); |
7908fad9 AP |
184 | if (ret) |
185 | goto err; | |
97de6e89 | 186 | ret = regmap_write(dev->regmap[0], 0x13, 0x1f); |
7908fad9 AP |
187 | if (ret) |
188 | goto err; | |
97de6e89 | 189 | ret = regmap_write(dev->regmap[0], 0x19, 0x03); |
7908fad9 AP |
190 | if (ret) |
191 | goto err; | |
97de6e89 | 192 | ret = regmap_write(dev->regmap[0], 0x1d, 0xb0); |
7908fad9 AP |
193 | if (ret) |
194 | goto err; | |
97de6e89 | 195 | ret = regmap_write(dev->regmap[0], 0x2a, 0x72); |
7908fad9 AP |
196 | if (ret) |
197 | goto err; | |
97de6e89 | 198 | ret = regmap_write(dev->regmap[0], 0x2d, 0x00); |
7908fad9 AP |
199 | if (ret) |
200 | goto err; | |
97de6e89 | 201 | ret = regmap_write(dev->regmap[0], 0x3c, 0x00); |
7908fad9 AP |
202 | if (ret) |
203 | goto err; | |
97de6e89 | 204 | ret = regmap_write(dev->regmap[0], 0x3f, 0xf8); |
7908fad9 AP |
205 | if (ret) |
206 | goto err; | |
207 | ret = regmap_bulk_write(dev->regmap[0], 0x40, "\xf4\x08", 2); | |
208 | if (ret) | |
209 | goto err; | |
210 | ret = regmap_write(dev->regmap[0], 0xd2, reg_bank0_d2_val); | |
211 | if (ret) | |
212 | goto err; | |
97de6e89 | 213 | ret = regmap_write(dev->regmap[0], 0xd4, 0x55); |
7908fad9 AP |
214 | if (ret) |
215 | goto err; | |
97de6e89 | 216 | ret = regmap_write(dev->regmap[1], 0xbe, 0x08); |
7908fad9 AP |
217 | if (ret) |
218 | goto err; | |
97de6e89 | 219 | ret = regmap_write(dev->regmap[0], 0xb2, 0x37); |
7908fad9 AP |
220 | if (ret) |
221 | goto err; | |
97de6e89 | 222 | ret = regmap_write(dev->regmap[0], 0xd7, 0x04); |
dadb5bb4 AP |
223 | if (ret) |
224 | goto err; | |
225 | ||
40eca140 AP |
226 | /* PLP */ |
227 | if (c->delivery_system == SYS_DVBT2) { | |
228 | ret = regmap_write(dev->regmap[2], 0x36, c->stream_id); | |
229 | if (ret) | |
230 | goto err; | |
231 | } | |
232 | ||
7908fad9 AP |
233 | /* Reset FSM */ |
234 | ret = regmap_write(dev->regmap[2], 0xf8, 0x9f); | |
235 | if (ret) | |
236 | goto err; | |
dadb5bb4 AP |
237 | |
238 | return 0; | |
239 | err: | |
01b4be14 | 240 | dev_dbg(&client->dev, "failed=%d\n", ret); |
dadb5bb4 AP |
241 | return ret; |
242 | } | |
243 | ||
69ace6ee | 244 | static int mn88473_read_status(struct dvb_frontend *fe, enum fe_status *status) |
61393b07 MB |
245 | { |
246 | struct i2c_client *client = fe->demodulator_priv; | |
247 | struct mn88473_dev *dev = i2c_get_clientdata(client); | |
248 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; | |
69ace6ee AP |
249 | int ret, i, stmp; |
250 | unsigned int utmp, utmp1, utmp2; | |
251 | u8 buf[5]; | |
61393b07 | 252 | |
69ace6ee AP |
253 | if (!dev->active) { |
254 | ret = -EAGAIN; | |
255 | goto err; | |
61393b07 MB |
256 | } |
257 | ||
69ace6ee AP |
258 | /* Lock detection */ |
259 | switch (c->delivery_system) { | |
260 | case SYS_DVBT: | |
261 | ret = regmap_read(dev->regmap[0], 0x62, &utmp); | |
61393b07 MB |
262 | if (ret) |
263 | goto err; | |
264 | ||
69ace6ee AP |
265 | if (!(utmp & 0xa0)) { |
266 | if ((utmp & 0x0f) >= 0x09) | |
267 | *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | | |
268 | FE_HAS_VITERBI | FE_HAS_SYNC | | |
269 | FE_HAS_LOCK; | |
270 | else if ((utmp & 0x0f) >= 0x03) | |
271 | *status = FE_HAS_SIGNAL | FE_HAS_CARRIER; | |
272 | } else { | |
273 | *status = 0; | |
274 | } | |
275 | break; | |
276 | case SYS_DVBT2: | |
277 | ret = regmap_read(dev->regmap[2], 0x8b, &utmp); | |
278 | if (ret) | |
279 | goto err; | |
280 | ||
281 | if (!(utmp & 0x40)) { | |
282 | if ((utmp & 0x0f) >= 0x0d) | |
283 | *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | | |
284 | FE_HAS_VITERBI | FE_HAS_SYNC | | |
285 | FE_HAS_LOCK; | |
286 | else if ((utmp & 0x0f) >= 0x0a) | |
287 | *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | | |
288 | FE_HAS_VITERBI; | |
289 | else if ((utmp & 0x0f) >= 0x07) | |
290 | *status = FE_HAS_SIGNAL | FE_HAS_CARRIER; | |
291 | } else { | |
292 | *status = 0; | |
293 | } | |
294 | break; | |
295 | case SYS_DVBC_ANNEX_A: | |
296 | ret = regmap_read(dev->regmap[1], 0x85, &utmp); | |
61393b07 MB |
297 | if (ret) |
298 | goto err; | |
299 | ||
69ace6ee AP |
300 | if (!(utmp & 0x40)) { |
301 | ret = regmap_read(dev->regmap[1], 0x89, &utmp); | |
302 | if (ret) | |
303 | goto err; | |
61393b07 | 304 | |
69ace6ee AP |
305 | if (utmp & 0x01) |
306 | *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | | |
307 | FE_HAS_VITERBI | FE_HAS_SYNC | | |
308 | FE_HAS_LOCK; | |
309 | } else { | |
310 | *status = 0; | |
311 | } | |
312 | break; | |
313 | default: | |
314 | ret = -EINVAL; | |
61393b07 | 315 | goto err; |
61393b07 MB |
316 | } |
317 | ||
69ace6ee AP |
318 | /* Signal strength */ |
319 | if (*status & FE_HAS_SIGNAL) { | |
320 | for (i = 0; i < 2; i++) { | |
321 | ret = regmap_bulk_read(dev->regmap[2], 0x86 + i, | |
322 | &buf[i], 1); | |
323 | if (ret) | |
324 | goto err; | |
325 | } | |
61393b07 | 326 | |
69ace6ee AP |
327 | /* AGCRD[15:6] gives us a 10bit value ([5:0] are always 0) */ |
328 | utmp1 = buf[0] << 8 | buf[1] << 0 | buf[0] >> 2; | |
329 | dev_dbg(&client->dev, "strength=%u\n", utmp1); | |
61393b07 | 330 | |
69ace6ee AP |
331 | c->strength.stat[0].scale = FE_SCALE_RELATIVE; |
332 | c->strength.stat[0].uvalue = utmp1; | |
61393b07 | 333 | } else { |
69ace6ee | 334 | c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; |
61393b07 MB |
335 | } |
336 | ||
337 | /* CNR */ | |
69ace6ee AP |
338 | if (*status & FE_HAS_VITERBI && c->delivery_system == SYS_DVBT) { |
339 | /* DVB-T CNR */ | |
340 | ret = regmap_bulk_read(dev->regmap[0], 0x8f, buf, 2); | |
61393b07 MB |
341 | if (ret) |
342 | goto err; | |
343 | ||
69ace6ee AP |
344 | utmp = buf[0] << 8 | buf[1] << 0; |
345 | if (utmp) { | |
346 | /* CNR[dB]: 10 * (log10(65536 / value) + 0.2) */ | |
347 | /* log10(65536) = 80807124, 0.2 = 3355443 */ | |
348 | stmp = div_u64(((u64)80807124 - intlog10(utmp) | |
349 | + 3355443) * 10000, 1 << 24); | |
350 | dev_dbg(&client->dev, "cnr=%d value=%u\n", stmp, utmp); | |
351 | } else { | |
352 | stmp = 0; | |
353 | } | |
61393b07 | 354 | |
69ace6ee AP |
355 | c->cnr.stat[0].svalue = stmp; |
356 | c->cnr.stat[0].scale = FE_SCALE_DECIBEL; | |
357 | } else if (*status & FE_HAS_VITERBI && | |
358 | c->delivery_system == SYS_DVBT2) { | |
359 | /* DVB-T2 CNR */ | |
360 | for (i = 0; i < 3; i++) { | |
361 | ret = regmap_bulk_read(dev->regmap[2], 0xb7 + i, | |
362 | &buf[i], 1); | |
363 | if (ret) | |
364 | goto err; | |
365 | } | |
61393b07 | 366 | |
69ace6ee AP |
367 | utmp = buf[1] << 8 | buf[2] << 0; |
368 | utmp1 = (buf[0] >> 2) & 0x01; /* 0=SISO, 1=MISO */ | |
369 | if (utmp) { | |
370 | if (utmp1) { | |
371 | /* CNR[dB]: 10 * (log10(16384 / value) - 0.6) */ | |
372 | /* log10(16384) = 70706234, 0.6 = 10066330 */ | |
373 | stmp = div_u64(((u64)70706234 - intlog10(utmp) | |
374 | - 10066330) * 10000, 1 << 24); | |
375 | dev_dbg(&client->dev, "cnr=%d value=%u MISO\n", | |
376 | stmp, utmp); | |
61393b07 | 377 | } else { |
69ace6ee AP |
378 | /* CNR[dB]: 10 * (log10(65536 / value) + 0.2) */ |
379 | /* log10(65536) = 80807124, 0.2 = 3355443 */ | |
380 | stmp = div_u64(((u64)80807124 - intlog10(utmp) | |
381 | + 3355443) * 10000, 1 << 24); | |
382 | dev_dbg(&client->dev, "cnr=%d value=%u SISO\n", | |
383 | stmp, utmp); | |
61393b07 | 384 | } |
69ace6ee AP |
385 | } else { |
386 | stmp = 0; | |
387 | } | |
61393b07 | 388 | |
69ace6ee | 389 | c->cnr.stat[0].svalue = stmp; |
61393b07 | 390 | c->cnr.stat[0].scale = FE_SCALE_DECIBEL; |
69ace6ee AP |
391 | } else if (*status & FE_HAS_VITERBI && |
392 | c->delivery_system == SYS_DVBC_ANNEX_A) { | |
393 | /* DVB-C CNR */ | |
394 | ret = regmap_bulk_read(dev->regmap[1], 0xa1, buf, 4); | |
395 | if (ret) | |
396 | goto err; | |
397 | ||
398 | utmp1 = buf[0] << 8 | buf[1] << 0; /* signal */ | |
399 | utmp2 = buf[2] << 8 | buf[3] << 0; /* noise */ | |
400 | if (utmp1 && utmp2) { | |
401 | /* CNR[dB]: 10 * log10(8 * (signal / noise)) */ | |
402 | /* log10(8) = 15151336 */ | |
403 | stmp = div_u64(((u64)15151336 + intlog10(utmp1) | |
404 | - intlog10(utmp2)) * 10000, 1 << 24); | |
405 | dev_dbg(&client->dev, "cnr=%d signal=%u noise=%u\n", | |
406 | stmp, utmp1, utmp2); | |
407 | } else { | |
408 | stmp = 0; | |
409 | } | |
61393b07 | 410 | |
69ace6ee | 411 | c->cnr.stat[0].svalue = stmp; |
61393b07 MB |
412 | c->cnr.stat[0].scale = FE_SCALE_DECIBEL; |
413 | } else { | |
414 | c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; | |
415 | } | |
416 | ||
417 | /* BER */ | |
69ace6ee AP |
418 | if (*status & FE_HAS_LOCK && (c->delivery_system == SYS_DVBT || |
419 | c->delivery_system == SYS_DVBC_ANNEX_A)) { | |
420 | /* DVB-T & DVB-C BER */ | |
421 | ret = regmap_bulk_read(dev->regmap[0], 0x92, buf, 5); | |
0aa83bb1 BL |
422 | if (ret) |
423 | goto err; | |
7908fad9 | 424 | |
69ace6ee AP |
425 | utmp1 = buf[0] << 16 | buf[1] << 8 | buf[2] << 0; |
426 | utmp2 = buf[3] << 8 | buf[4] << 0; | |
427 | utmp2 = utmp2 * 8 * 204; | |
428 | dev_dbg(&client->dev, "post_bit_error=%u post_bit_count=%u\n", | |
429 | utmp1, utmp2); | |
7908fad9 | 430 | |
69ace6ee AP |
431 | c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; |
432 | c->post_bit_error.stat[0].uvalue += utmp1; | |
433 | c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; | |
434 | c->post_bit_count.stat[0].uvalue += utmp2; | |
61393b07 | 435 | } else { |
69ace6ee AP |
436 | c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; |
437 | c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; | |
61393b07 MB |
438 | } |
439 | ||
440 | /* PER */ | |
441 | if (*status & FE_HAS_LOCK) { | |
69ace6ee | 442 | ret = regmap_bulk_read(dev->regmap[0], 0xdd, buf, 4); |
61393b07 MB |
443 | if (ret) |
444 | goto err; | |
445 | ||
69ace6ee AP |
446 | utmp1 = buf[0] << 8 | buf[1] << 0; |
447 | utmp2 = buf[2] << 8 | buf[3] << 0; | |
448 | dev_dbg(&client->dev, "block_error=%u block_count=%u\n", | |
449 | utmp1, utmp2); | |
61393b07 MB |
450 | |
451 | c->block_error.stat[0].scale = FE_SCALE_COUNTER; | |
69ace6ee | 452 | c->block_error.stat[0].uvalue += utmp1; |
61393b07 | 453 | c->block_count.stat[0].scale = FE_SCALE_COUNTER; |
69ace6ee | 454 | c->block_count.stat[0].uvalue += utmp2; |
61393b07 | 455 | } else { |
69ace6ee AP |
456 | c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; |
457 | c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; | |
0aa83bb1 BL |
458 | } |
459 | ||
dadb5bb4 AP |
460 | return 0; |
461 | err: | |
69ace6ee | 462 | dev_dbg(&client->dev, "failed=%d\n", ret); |
dadb5bb4 AP |
463 | return ret; |
464 | } | |
465 | ||
466 | static int mn88473_init(struct dvb_frontend *fe) | |
467 | { | |
01b4be14 AP |
468 | struct i2c_client *client = fe->demodulator_priv; |
469 | struct mn88473_dev *dev = i2c_get_clientdata(client); | |
61393b07 | 470 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
7908fad9 AP |
471 | int ret, len, remain; |
472 | unsigned int uitmp; | |
473 | const struct firmware *fw; | |
474 | const char *name = MN88473_FIRMWARE; | |
dadb5bb4 | 475 | |
01b4be14 | 476 | dev_dbg(&client->dev, "\n"); |
dadb5bb4 | 477 | |
7908fad9 AP |
478 | /* Check if firmware is already running */ |
479 | ret = regmap_read(dev->regmap[0], 0xf5, &uitmp); | |
567627bf BL |
480 | if (ret) |
481 | goto err; | |
482 | ||
7908fad9 AP |
483 | if (!(uitmp & 0x01)) |
484 | goto warm; | |
dadb5bb4 | 485 | |
7908fad9 AP |
486 | /* Request the firmware, this will block and timeout */ |
487 | ret = request_firmware(&fw, name, &client->dev); | |
dadb5bb4 | 488 | if (ret) { |
c5ee19c8 | 489 | dev_err(&client->dev, "firmware file '%s' not found\n", name); |
7908fad9 | 490 | goto err; |
dadb5bb4 AP |
491 | } |
492 | ||
7908fad9 | 493 | dev_info(&client->dev, "downloading firmware from file '%s'\n", name); |
dadb5bb4 | 494 | |
97de6e89 | 495 | ret = regmap_write(dev->regmap[0], 0xf5, 0x03); |
dadb5bb4 | 496 | if (ret) |
7908fad9 | 497 | goto err_release_firmware; |
dadb5bb4 | 498 | |
7908fad9 AP |
499 | for (remain = fw->size; remain > 0; remain -= (dev->i2c_wr_max - 1)) { |
500 | len = min(dev->i2c_wr_max - 1, remain); | |
97de6e89 | 501 | ret = regmap_bulk_write(dev->regmap[0], 0xf6, |
7908fad9 | 502 | &fw->data[fw->size - remain], len); |
dadb5bb4 | 503 | if (ret) { |
7908fad9 | 504 | dev_err(&client->dev, "firmware download failed %d\n", |
b4c2c314 | 505 | ret); |
7908fad9 | 506 | goto err_release_firmware; |
dadb5bb4 AP |
507 | } |
508 | } | |
509 | ||
7908fad9 AP |
510 | release_firmware(fw); |
511 | ||
512 | /* Parity check of firmware */ | |
513 | ret = regmap_read(dev->regmap[0], 0xf8, &uitmp); | |
514 | if (ret) | |
0f21ac7f | 515 | goto err; |
7908fad9 AP |
516 | |
517 | if (uitmp & 0x10) { | |
518 | dev_err(&client->dev, "firmware parity check failed\n"); | |
519 | ret = -EINVAL; | |
0f21ac7f BL |
520 | goto err; |
521 | } | |
0f21ac7f | 522 | |
97de6e89 | 523 | ret = regmap_write(dev->regmap[0], 0xf5, 0x00); |
dadb5bb4 AP |
524 | if (ret) |
525 | goto err; | |
7908fad9 AP |
526 | warm: |
527 | /* TS config */ | |
528 | ret = regmap_write(dev->regmap[2], 0x09, 0x08); | |
529 | if (ret) | |
530 | goto err; | |
531 | ret = regmap_write(dev->regmap[2], 0x08, 0x1d); | |
532 | if (ret) | |
533 | goto err; | |
dadb5bb4 | 534 | |
7908fad9 | 535 | dev->active = true; |
dadb5bb4 | 536 | |
61393b07 MB |
537 | /* init stats here to indicate which stats are supported */ |
538 | c->strength.len = 1; | |
539 | c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; | |
540 | c->cnr.len = 1; | |
541 | c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; | |
542 | c->post_bit_error.len = 1; | |
543 | c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; | |
544 | c->post_bit_count.len = 1; | |
545 | c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; | |
546 | c->block_error.len = 1; | |
547 | c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; | |
548 | c->block_count.len = 1; | |
549 | c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; | |
550 | ||
dadb5bb4 | 551 | return 0; |
7908fad9 | 552 | err_release_firmware: |
b5911384 | 553 | release_firmware(fw); |
7908fad9 | 554 | err: |
01b4be14 | 555 | dev_dbg(&client->dev, "failed=%d\n", ret); |
dadb5bb4 AP |
556 | return ret; |
557 | } | |
558 | ||
559 | static int mn88473_sleep(struct dvb_frontend *fe) | |
560 | { | |
01b4be14 AP |
561 | struct i2c_client *client = fe->demodulator_priv; |
562 | struct mn88473_dev *dev = i2c_get_clientdata(client); | |
dadb5bb4 AP |
563 | int ret; |
564 | ||
01b4be14 | 565 | dev_dbg(&client->dev, "\n"); |
dadb5bb4 | 566 | |
7908fad9 AP |
567 | dev->active = false; |
568 | ||
97de6e89 | 569 | ret = regmap_write(dev->regmap[2], 0x05, 0x3e); |
dadb5bb4 AP |
570 | if (ret) |
571 | goto err; | |
572 | ||
dadb5bb4 AP |
573 | return 0; |
574 | err: | |
01b4be14 | 575 | dev_dbg(&client->dev, "failed=%d\n", ret); |
dadb5bb4 AP |
576 | return ret; |
577 | } | |
578 | ||
7908fad9 AP |
579 | static const struct dvb_frontend_ops mn88473_ops = { |
580 | .delsys = {SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A}, | |
01b4be14 AP |
581 | .info = { |
582 | .name = "Panasonic MN88473", | |
8459e41d AP |
583 | .symbol_rate_min = 1000000, |
584 | .symbol_rate_max = 7200000, | |
01b4be14 AP |
585 | .caps = FE_CAN_FEC_1_2 | |
586 | FE_CAN_FEC_2_3 | | |
587 | FE_CAN_FEC_3_4 | | |
588 | FE_CAN_FEC_5_6 | | |
589 | FE_CAN_FEC_7_8 | | |
590 | FE_CAN_FEC_AUTO | | |
591 | FE_CAN_QPSK | | |
592 | FE_CAN_QAM_16 | | |
593 | FE_CAN_QAM_32 | | |
594 | FE_CAN_QAM_64 | | |
595 | FE_CAN_QAM_128 | | |
596 | FE_CAN_QAM_256 | | |
597 | FE_CAN_QAM_AUTO | | |
598 | FE_CAN_TRANSMISSION_MODE_AUTO | | |
599 | FE_CAN_GUARD_INTERVAL_AUTO | | |
600 | FE_CAN_HIERARCHY_AUTO | | |
601 | FE_CAN_MUTE_TS | | |
40eca140 AP |
602 | FE_CAN_2G_MODULATION | |
603 | FE_CAN_MULTISTREAM | |
01b4be14 AP |
604 | }, |
605 | ||
606 | .get_tune_settings = mn88473_get_tune_settings, | |
607 | ||
608 | .init = mn88473_init, | |
609 | .sleep = mn88473_sleep, | |
dadb5bb4 | 610 | |
01b4be14 AP |
611 | .set_frontend = mn88473_set_frontend, |
612 | ||
613 | .read_status = mn88473_read_status, | |
614 | }; | |
615 | ||
616 | static int mn88473_probe(struct i2c_client *client, | |
b4c2c314 | 617 | const struct i2c_device_id *id) |
dadb5bb4 | 618 | { |
01b4be14 | 619 | struct mn88473_config *config = client->dev.platform_data; |
dadb5bb4 | 620 | struct mn88473_dev *dev; |
01b4be14 | 621 | int ret; |
7908fad9 | 622 | unsigned int uitmp; |
97de6e89 AP |
623 | static const struct regmap_config regmap_config = { |
624 | .reg_bits = 8, | |
625 | .val_bits = 8, | |
626 | }; | |
dadb5bb4 | 627 | |
01b4be14 | 628 | dev_dbg(&client->dev, "\n"); |
dadb5bb4 | 629 | |
7908fad9 | 630 | /* Caller really need to provide pointer for frontend we create */ |
01b4be14 AP |
631 | if (config->fe == NULL) { |
632 | dev_err(&client->dev, "frontend pointer not defined\n"); | |
633 | ret = -EINVAL; | |
634 | goto err; | |
635 | } | |
636 | ||
637 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); | |
638 | if (dev == NULL) { | |
dadb5bb4 | 639 | ret = -ENOMEM; |
dadb5bb4 AP |
640 | goto err; |
641 | } | |
642 | ||
7908fad9 AP |
643 | if (config->i2c_wr_max) |
644 | dev->i2c_wr_max = config->i2c_wr_max; | |
3b786f13 | 645 | else |
7908fad9 AP |
646 | dev->i2c_wr_max = ~0; |
647 | ||
648 | if (config->xtal) | |
649 | dev->clk = config->xtal; | |
650 | else | |
651 | dev->clk = 25000000; | |
97de6e89 AP |
652 | dev->client[0] = client; |
653 | dev->regmap[0] = regmap_init_i2c(dev->client[0], ®map_config); | |
654 | if (IS_ERR(dev->regmap[0])) { | |
655 | ret = PTR_ERR(dev->regmap[0]); | |
656 | goto err_kfree; | |
657 | } | |
dadb5bb4 | 658 | |
01b4be14 | 659 | /* |
7908fad9 | 660 | * Chip has three I2C addresses for different register banks. Used |
01b4be14 | 661 | * addresses are 0x18, 0x1a and 0x1c. We register two dummy clients, |
7908fad9 AP |
662 | * 0x1a and 0x1c, in order to get own I2C client for each register bank. |
663 | * | |
664 | * Also, register bank 2 do not support sequential I/O. Only single | |
665 | * register write or read is allowed to that bank. | |
01b4be14 AP |
666 | */ |
667 | dev->client[1] = i2c_new_dummy(client->adapter, 0x1a); | |
668 | if (dev->client[1] == NULL) { | |
669 | ret = -ENODEV; | |
670 | dev_err(&client->dev, "I2C registration failed\n"); | |
671 | if (ret) | |
97de6e89 AP |
672 | goto err_regmap_0_regmap_exit; |
673 | } | |
674 | dev->regmap[1] = regmap_init_i2c(dev->client[1], ®map_config); | |
675 | if (IS_ERR(dev->regmap[1])) { | |
676 | ret = PTR_ERR(dev->regmap[1]); | |
677 | goto err_client_1_i2c_unregister_device; | |
01b4be14 AP |
678 | } |
679 | i2c_set_clientdata(dev->client[1], dev); | |
680 | ||
681 | dev->client[2] = i2c_new_dummy(client->adapter, 0x1c); | |
682 | if (dev->client[2] == NULL) { | |
683 | ret = -ENODEV; | |
684 | dev_err(&client->dev, "2nd I2C registration failed\n"); | |
685 | if (ret) | |
97de6e89 AP |
686 | goto err_regmap_1_regmap_exit; |
687 | } | |
688 | dev->regmap[2] = regmap_init_i2c(dev->client[2], ®map_config); | |
689 | if (IS_ERR(dev->regmap[2])) { | |
690 | ret = PTR_ERR(dev->regmap[2]); | |
691 | goto err_client_2_i2c_unregister_device; | |
01b4be14 AP |
692 | } |
693 | i2c_set_clientdata(dev->client[2], dev); | |
dadb5bb4 | 694 | |
d930b5b5 AP |
695 | /* Check demod answers with correct chip id */ |
696 | ret = regmap_read(dev->regmap[2], 0xff, &uitmp); | |
697 | if (ret) | |
698 | goto err_regmap_2_regmap_exit; | |
699 | ||
700 | dev_dbg(&client->dev, "chip id=%02x\n", uitmp); | |
701 | ||
702 | if (uitmp != 0x03) { | |
703 | ret = -ENODEV; | |
704 | goto err_regmap_2_regmap_exit; | |
705 | } | |
706 | ||
7908fad9 AP |
707 | /* Sleep because chip is active by default */ |
708 | ret = regmap_write(dev->regmap[2], 0x05, 0x3e); | |
709 | if (ret) | |
37cf9b2d | 710 | goto err_regmap_2_regmap_exit; |
7908fad9 AP |
711 | |
712 | /* Create dvb frontend */ | |
713 | memcpy(&dev->frontend.ops, &mn88473_ops, sizeof(dev->frontend.ops)); | |
714 | dev->frontend.demodulator_priv = client; | |
715 | *config->fe = &dev->frontend; | |
01b4be14 | 716 | i2c_set_clientdata(client, dev); |
dadb5bb4 | 717 | |
7908fad9 AP |
718 | dev_info(&client->dev, "Panasonic MN88473 successfully identified\n"); |
719 | ||
01b4be14 | 720 | return 0; |
37cf9b2d AP |
721 | err_regmap_2_regmap_exit: |
722 | regmap_exit(dev->regmap[2]); | |
97de6e89 AP |
723 | err_client_2_i2c_unregister_device: |
724 | i2c_unregister_device(dev->client[2]); | |
725 | err_regmap_1_regmap_exit: | |
726 | regmap_exit(dev->regmap[1]); | |
01b4be14 AP |
727 | err_client_1_i2c_unregister_device: |
728 | i2c_unregister_device(dev->client[1]); | |
97de6e89 AP |
729 | err_regmap_0_regmap_exit: |
730 | regmap_exit(dev->regmap[0]); | |
01b4be14 | 731 | err_kfree: |
dadb5bb4 | 732 | kfree(dev); |
01b4be14 AP |
733 | err: |
734 | dev_dbg(&client->dev, "failed=%d\n", ret); | |
735 | return ret; | |
dadb5bb4 | 736 | } |
dadb5bb4 | 737 | |
01b4be14 AP |
738 | static int mn88473_remove(struct i2c_client *client) |
739 | { | |
740 | struct mn88473_dev *dev = i2c_get_clientdata(client); | |
dadb5bb4 | 741 | |
01b4be14 | 742 | dev_dbg(&client->dev, "\n"); |
dadb5bb4 | 743 | |
97de6e89 | 744 | regmap_exit(dev->regmap[2]); |
01b4be14 | 745 | i2c_unregister_device(dev->client[2]); |
97de6e89 AP |
746 | |
747 | regmap_exit(dev->regmap[1]); | |
01b4be14 | 748 | i2c_unregister_device(dev->client[1]); |
dadb5bb4 | 749 | |
97de6e89 AP |
750 | regmap_exit(dev->regmap[0]); |
751 | ||
01b4be14 | 752 | kfree(dev); |
dadb5bb4 | 753 | |
01b4be14 AP |
754 | return 0; |
755 | } | |
dadb5bb4 | 756 | |
01b4be14 AP |
757 | static const struct i2c_device_id mn88473_id_table[] = { |
758 | {"mn88473", 0}, | |
759 | {} | |
760 | }; | |
761 | MODULE_DEVICE_TABLE(i2c, mn88473_id_table); | |
762 | ||
763 | static struct i2c_driver mn88473_driver = { | |
764 | .driver = { | |
7908fad9 AP |
765 | .name = "mn88473", |
766 | .suppress_bind_attrs = true, | |
01b4be14 AP |
767 | }, |
768 | .probe = mn88473_probe, | |
769 | .remove = mn88473_remove, | |
770 | .id_table = mn88473_id_table, | |
dadb5bb4 AP |
771 | }; |
772 | ||
01b4be14 AP |
773 | module_i2c_driver(mn88473_driver); |
774 | ||
dadb5bb4 AP |
775 | MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); |
776 | MODULE_DESCRIPTION("Panasonic MN88473 DVB-T/T2/C demodulator driver"); | |
777 | MODULE_LICENSE("GPL"); | |
778 | MODULE_FIRMWARE(MN88473_FIRMWARE); |