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