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 | ||
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 | ||
0df289a2 | 237 | static int mn88473_read_status(struct dvb_frontend *fe, enum fe_status *status) |
dadb5bb4 | 238 | { |
01b4be14 AP |
239 | struct i2c_client *client = fe->demodulator_priv; |
240 | struct mn88473_dev *dev = i2c_get_clientdata(client); | |
0aa83bb1 | 241 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
dadb5bb4 | 242 | int ret; |
7908fad9 | 243 | unsigned int uitmp; |
dadb5bb4 | 244 | |
7908fad9 | 245 | if (!dev->active) { |
dadb5bb4 AP |
246 | ret = -EAGAIN; |
247 | goto err; | |
248 | } | |
249 | ||
7908fad9 AP |
250 | *status = 0; |
251 | ||
0aa83bb1 BL |
252 | switch (c->delivery_system) { |
253 | case SYS_DVBT: | |
7908fad9 | 254 | ret = regmap_read(dev->regmap[0], 0x62, &uitmp); |
0aa83bb1 BL |
255 | if (ret) |
256 | goto err; | |
7908fad9 AP |
257 | |
258 | if (!(uitmp & 0xa0)) { | |
259 | if ((uitmp & 0x0f) >= 0x09) | |
260 | *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | | |
261 | FE_HAS_VITERBI | FE_HAS_SYNC | | |
262 | FE_HAS_LOCK; | |
263 | else if ((uitmp & 0x0f) >= 0x03) | |
264 | *status = FE_HAS_SIGNAL | FE_HAS_CARRIER; | |
0aa83bb1 BL |
265 | } |
266 | break; | |
267 | case SYS_DVBT2: | |
7908fad9 | 268 | ret = regmap_read(dev->regmap[2], 0x8b, &uitmp); |
0aa83bb1 BL |
269 | if (ret) |
270 | goto err; | |
7908fad9 AP |
271 | |
272 | if (!(uitmp & 0x40)) { | |
273 | if ((uitmp & 0x0f) >= 0x0d) | |
274 | *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | | |
275 | FE_HAS_VITERBI | FE_HAS_SYNC | | |
276 | FE_HAS_LOCK; | |
277 | else if ((uitmp & 0x0f) >= 0x0a) | |
278 | *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | | |
279 | FE_HAS_VITERBI; | |
280 | else if ((uitmp & 0x0f) >= 0x07) | |
281 | *status = FE_HAS_SIGNAL | FE_HAS_CARRIER; | |
0aa83bb1 BL |
282 | } |
283 | break; | |
284 | case SYS_DVBC_ANNEX_A: | |
7908fad9 | 285 | ret = regmap_read(dev->regmap[1], 0x85, &uitmp); |
0aa83bb1 BL |
286 | if (ret) |
287 | goto err; | |
7908fad9 AP |
288 | |
289 | if (!(uitmp & 0x40)) { | |
290 | ret = regmap_read(dev->regmap[1], 0x89, &uitmp); | |
0aa83bb1 BL |
291 | if (ret) |
292 | goto err; | |
7908fad9 AP |
293 | |
294 | if (uitmp & 0x01) | |
295 | *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | | |
296 | FE_HAS_VITERBI | FE_HAS_SYNC | | |
297 | FE_HAS_LOCK; | |
0aa83bb1 BL |
298 | } |
299 | break; | |
300 | default: | |
301 | ret = -EINVAL; | |
302 | goto err; | |
303 | } | |
304 | ||
dadb5bb4 AP |
305 | return 0; |
306 | err: | |
01b4be14 | 307 | dev_dbg(&client->dev, "failed=%d\n", ret); |
dadb5bb4 AP |
308 | return ret; |
309 | } | |
310 | ||
311 | static int mn88473_init(struct dvb_frontend *fe) | |
312 | { | |
01b4be14 AP |
313 | struct i2c_client *client = fe->demodulator_priv; |
314 | struct mn88473_dev *dev = i2c_get_clientdata(client); | |
7908fad9 AP |
315 | int ret, len, remain; |
316 | unsigned int uitmp; | |
317 | const struct firmware *fw; | |
318 | const char *name = MN88473_FIRMWARE; | |
dadb5bb4 | 319 | |
01b4be14 | 320 | dev_dbg(&client->dev, "\n"); |
dadb5bb4 | 321 | |
7908fad9 AP |
322 | /* Check if firmware is already running */ |
323 | ret = regmap_read(dev->regmap[0], 0xf5, &uitmp); | |
567627bf BL |
324 | if (ret) |
325 | goto err; | |
326 | ||
7908fad9 AP |
327 | if (!(uitmp & 0x01)) |
328 | goto warm; | |
dadb5bb4 | 329 | |
7908fad9 AP |
330 | /* Request the firmware, this will block and timeout */ |
331 | ret = request_firmware(&fw, name, &client->dev); | |
dadb5bb4 | 332 | if (ret) { |
7908fad9 AP |
333 | dev_err(&client->dev, "firmare file '%s' not found\n", name); |
334 | goto err; | |
dadb5bb4 AP |
335 | } |
336 | ||
7908fad9 | 337 | dev_info(&client->dev, "downloading firmware from file '%s'\n", name); |
dadb5bb4 | 338 | |
97de6e89 | 339 | ret = regmap_write(dev->regmap[0], 0xf5, 0x03); |
dadb5bb4 | 340 | if (ret) |
7908fad9 | 341 | goto err_release_firmware; |
dadb5bb4 | 342 | |
7908fad9 AP |
343 | for (remain = fw->size; remain > 0; remain -= (dev->i2c_wr_max - 1)) { |
344 | len = min(dev->i2c_wr_max - 1, remain); | |
97de6e89 | 345 | ret = regmap_bulk_write(dev->regmap[0], 0xf6, |
7908fad9 | 346 | &fw->data[fw->size - remain], len); |
dadb5bb4 | 347 | if (ret) { |
7908fad9 | 348 | dev_err(&client->dev, "firmware download failed %d\n", |
b4c2c314 | 349 | ret); |
7908fad9 | 350 | goto err_release_firmware; |
dadb5bb4 AP |
351 | } |
352 | } | |
353 | ||
7908fad9 AP |
354 | release_firmware(fw); |
355 | ||
356 | /* Parity check of firmware */ | |
357 | ret = regmap_read(dev->regmap[0], 0xf8, &uitmp); | |
358 | if (ret) | |
0f21ac7f | 359 | goto err; |
7908fad9 AP |
360 | |
361 | if (uitmp & 0x10) { | |
362 | dev_err(&client->dev, "firmware parity check failed\n"); | |
363 | ret = -EINVAL; | |
0f21ac7f BL |
364 | goto err; |
365 | } | |
0f21ac7f | 366 | |
97de6e89 | 367 | ret = regmap_write(dev->regmap[0], 0xf5, 0x00); |
dadb5bb4 AP |
368 | if (ret) |
369 | goto err; | |
7908fad9 AP |
370 | warm: |
371 | /* TS config */ | |
372 | ret = regmap_write(dev->regmap[2], 0x09, 0x08); | |
373 | if (ret) | |
374 | goto err; | |
375 | ret = regmap_write(dev->regmap[2], 0x08, 0x1d); | |
376 | if (ret) | |
377 | goto err; | |
dadb5bb4 | 378 | |
7908fad9 | 379 | dev->active = true; |
dadb5bb4 AP |
380 | |
381 | return 0; | |
7908fad9 | 382 | err_release_firmware: |
b5911384 | 383 | release_firmware(fw); |
7908fad9 | 384 | err: |
01b4be14 | 385 | dev_dbg(&client->dev, "failed=%d\n", ret); |
dadb5bb4 AP |
386 | return ret; |
387 | } | |
388 | ||
389 | static int mn88473_sleep(struct dvb_frontend *fe) | |
390 | { | |
01b4be14 AP |
391 | struct i2c_client *client = fe->demodulator_priv; |
392 | struct mn88473_dev *dev = i2c_get_clientdata(client); | |
dadb5bb4 AP |
393 | int ret; |
394 | ||
01b4be14 | 395 | dev_dbg(&client->dev, "\n"); |
dadb5bb4 | 396 | |
7908fad9 AP |
397 | dev->active = false; |
398 | ||
97de6e89 | 399 | ret = regmap_write(dev->regmap[2], 0x05, 0x3e); |
dadb5bb4 AP |
400 | if (ret) |
401 | goto err; | |
402 | ||
dadb5bb4 AP |
403 | return 0; |
404 | err: | |
01b4be14 | 405 | dev_dbg(&client->dev, "failed=%d\n", ret); |
dadb5bb4 AP |
406 | return ret; |
407 | } | |
408 | ||
7908fad9 AP |
409 | static const struct dvb_frontend_ops mn88473_ops = { |
410 | .delsys = {SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A}, | |
01b4be14 AP |
411 | .info = { |
412 | .name = "Panasonic MN88473", | |
8459e41d AP |
413 | .symbol_rate_min = 1000000, |
414 | .symbol_rate_max = 7200000, | |
01b4be14 AP |
415 | .caps = FE_CAN_FEC_1_2 | |
416 | FE_CAN_FEC_2_3 | | |
417 | FE_CAN_FEC_3_4 | | |
418 | FE_CAN_FEC_5_6 | | |
419 | FE_CAN_FEC_7_8 | | |
420 | FE_CAN_FEC_AUTO | | |
421 | FE_CAN_QPSK | | |
422 | FE_CAN_QAM_16 | | |
423 | FE_CAN_QAM_32 | | |
424 | FE_CAN_QAM_64 | | |
425 | FE_CAN_QAM_128 | | |
426 | FE_CAN_QAM_256 | | |
427 | FE_CAN_QAM_AUTO | | |
428 | FE_CAN_TRANSMISSION_MODE_AUTO | | |
429 | FE_CAN_GUARD_INTERVAL_AUTO | | |
430 | FE_CAN_HIERARCHY_AUTO | | |
431 | FE_CAN_MUTE_TS | | |
7908fad9 | 432 | FE_CAN_2G_MODULATION |
01b4be14 AP |
433 | }, |
434 | ||
435 | .get_tune_settings = mn88473_get_tune_settings, | |
436 | ||
437 | .init = mn88473_init, | |
438 | .sleep = mn88473_sleep, | |
dadb5bb4 | 439 | |
01b4be14 AP |
440 | .set_frontend = mn88473_set_frontend, |
441 | ||
442 | .read_status = mn88473_read_status, | |
443 | }; | |
444 | ||
445 | static int mn88473_probe(struct i2c_client *client, | |
b4c2c314 | 446 | const struct i2c_device_id *id) |
dadb5bb4 | 447 | { |
01b4be14 | 448 | struct mn88473_config *config = client->dev.platform_data; |
dadb5bb4 | 449 | struct mn88473_dev *dev; |
01b4be14 | 450 | int ret; |
7908fad9 | 451 | unsigned int uitmp; |
97de6e89 AP |
452 | static const struct regmap_config regmap_config = { |
453 | .reg_bits = 8, | |
454 | .val_bits = 8, | |
455 | }; | |
dadb5bb4 | 456 | |
01b4be14 | 457 | dev_dbg(&client->dev, "\n"); |
dadb5bb4 | 458 | |
7908fad9 | 459 | /* Caller really need to provide pointer for frontend we create */ |
01b4be14 AP |
460 | if (config->fe == NULL) { |
461 | dev_err(&client->dev, "frontend pointer not defined\n"); | |
462 | ret = -EINVAL; | |
463 | goto err; | |
464 | } | |
465 | ||
466 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); | |
467 | if (dev == NULL) { | |
dadb5bb4 | 468 | ret = -ENOMEM; |
dadb5bb4 AP |
469 | goto err; |
470 | } | |
471 | ||
7908fad9 AP |
472 | if (config->i2c_wr_max) |
473 | dev->i2c_wr_max = config->i2c_wr_max; | |
3b786f13 | 474 | else |
7908fad9 AP |
475 | dev->i2c_wr_max = ~0; |
476 | ||
477 | if (config->xtal) | |
478 | dev->clk = config->xtal; | |
479 | else | |
480 | dev->clk = 25000000; | |
97de6e89 AP |
481 | dev->client[0] = client; |
482 | dev->regmap[0] = regmap_init_i2c(dev->client[0], ®map_config); | |
483 | if (IS_ERR(dev->regmap[0])) { | |
484 | ret = PTR_ERR(dev->regmap[0]); | |
485 | goto err_kfree; | |
486 | } | |
dadb5bb4 | 487 | |
7908fad9 AP |
488 | /* Check demod answers with correct chip id */ |
489 | ret = regmap_read(dev->regmap[0], 0xff, &uitmp); | |
dadb5bb4 | 490 | if (ret) |
97de6e89 | 491 | goto err_regmap_0_regmap_exit; |
01b4be14 | 492 | |
7908fad9 AP |
493 | dev_dbg(&client->dev, "chip id=%02x\n", uitmp); |
494 | ||
495 | if (uitmp != 0x03) { | |
496 | ret = -ENODEV; | |
497 | goto err_regmap_0_regmap_exit; | |
498 | } | |
499 | ||
01b4be14 | 500 | /* |
7908fad9 | 501 | * Chip has three I2C addresses for different register banks. Used |
01b4be14 | 502 | * addresses are 0x18, 0x1a and 0x1c. We register two dummy clients, |
7908fad9 AP |
503 | * 0x1a and 0x1c, in order to get own I2C client for each register bank. |
504 | * | |
505 | * Also, register bank 2 do not support sequential I/O. Only single | |
506 | * register write or read is allowed to that bank. | |
01b4be14 AP |
507 | */ |
508 | dev->client[1] = i2c_new_dummy(client->adapter, 0x1a); | |
509 | if (dev->client[1] == NULL) { | |
510 | ret = -ENODEV; | |
511 | dev_err(&client->dev, "I2C registration failed\n"); | |
512 | if (ret) | |
97de6e89 AP |
513 | goto err_regmap_0_regmap_exit; |
514 | } | |
515 | dev->regmap[1] = regmap_init_i2c(dev->client[1], ®map_config); | |
516 | if (IS_ERR(dev->regmap[1])) { | |
517 | ret = PTR_ERR(dev->regmap[1]); | |
518 | goto err_client_1_i2c_unregister_device; | |
01b4be14 AP |
519 | } |
520 | i2c_set_clientdata(dev->client[1], dev); | |
521 | ||
522 | dev->client[2] = i2c_new_dummy(client->adapter, 0x1c); | |
523 | if (dev->client[2] == NULL) { | |
524 | ret = -ENODEV; | |
525 | dev_err(&client->dev, "2nd I2C registration failed\n"); | |
526 | if (ret) | |
97de6e89 AP |
527 | goto err_regmap_1_regmap_exit; |
528 | } | |
529 | dev->regmap[2] = regmap_init_i2c(dev->client[2], ®map_config); | |
530 | if (IS_ERR(dev->regmap[2])) { | |
531 | ret = PTR_ERR(dev->regmap[2]); | |
532 | goto err_client_2_i2c_unregister_device; | |
01b4be14 AP |
533 | } |
534 | i2c_set_clientdata(dev->client[2], dev); | |
dadb5bb4 | 535 | |
7908fad9 AP |
536 | /* Sleep because chip is active by default */ |
537 | ret = regmap_write(dev->regmap[2], 0x05, 0x3e); | |
538 | if (ret) | |
539 | goto err_client_2_i2c_unregister_device; | |
540 | ||
541 | /* Create dvb frontend */ | |
542 | memcpy(&dev->frontend.ops, &mn88473_ops, sizeof(dev->frontend.ops)); | |
543 | dev->frontend.demodulator_priv = client; | |
544 | *config->fe = &dev->frontend; | |
01b4be14 | 545 | i2c_set_clientdata(client, dev); |
dadb5bb4 | 546 | |
7908fad9 AP |
547 | dev_info(&client->dev, "Panasonic MN88473 successfully identified\n"); |
548 | ||
01b4be14 AP |
549 | return 0; |
550 | ||
97de6e89 AP |
551 | err_client_2_i2c_unregister_device: |
552 | i2c_unregister_device(dev->client[2]); | |
553 | err_regmap_1_regmap_exit: | |
554 | regmap_exit(dev->regmap[1]); | |
01b4be14 AP |
555 | err_client_1_i2c_unregister_device: |
556 | i2c_unregister_device(dev->client[1]); | |
97de6e89 AP |
557 | err_regmap_0_regmap_exit: |
558 | regmap_exit(dev->regmap[0]); | |
01b4be14 | 559 | err_kfree: |
dadb5bb4 | 560 | kfree(dev); |
01b4be14 AP |
561 | err: |
562 | dev_dbg(&client->dev, "failed=%d\n", ret); | |
563 | return ret; | |
dadb5bb4 | 564 | } |
dadb5bb4 | 565 | |
01b4be14 AP |
566 | static int mn88473_remove(struct i2c_client *client) |
567 | { | |
568 | struct mn88473_dev *dev = i2c_get_clientdata(client); | |
dadb5bb4 | 569 | |
01b4be14 | 570 | dev_dbg(&client->dev, "\n"); |
dadb5bb4 | 571 | |
97de6e89 | 572 | regmap_exit(dev->regmap[2]); |
01b4be14 | 573 | i2c_unregister_device(dev->client[2]); |
97de6e89 AP |
574 | |
575 | regmap_exit(dev->regmap[1]); | |
01b4be14 | 576 | i2c_unregister_device(dev->client[1]); |
dadb5bb4 | 577 | |
97de6e89 AP |
578 | regmap_exit(dev->regmap[0]); |
579 | ||
01b4be14 | 580 | kfree(dev); |
dadb5bb4 | 581 | |
01b4be14 AP |
582 | return 0; |
583 | } | |
dadb5bb4 | 584 | |
01b4be14 AP |
585 | static const struct i2c_device_id mn88473_id_table[] = { |
586 | {"mn88473", 0}, | |
587 | {} | |
588 | }; | |
589 | MODULE_DEVICE_TABLE(i2c, mn88473_id_table); | |
590 | ||
591 | static struct i2c_driver mn88473_driver = { | |
592 | .driver = { | |
7908fad9 AP |
593 | .name = "mn88473", |
594 | .suppress_bind_attrs = true, | |
01b4be14 AP |
595 | }, |
596 | .probe = mn88473_probe, | |
597 | .remove = mn88473_remove, | |
598 | .id_table = mn88473_id_table, | |
dadb5bb4 AP |
599 | }; |
600 | ||
01b4be14 AP |
601 | module_i2c_driver(mn88473_driver); |
602 | ||
dadb5bb4 AP |
603 | MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); |
604 | MODULE_DESCRIPTION("Panasonic MN88473 DVB-T/T2/C demodulator driver"); | |
605 | MODULE_LICENSE("GPL"); | |
606 | MODULE_FIRMWARE(MN88473_FIRMWARE); |