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