1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2877ba50bSAntti Palosaari /*
3877ba50bSAntti Palosaari  * Panasonic MN88473 DVB-T/T2/C demodulator driver
4877ba50bSAntti Palosaari  *
5877ba50bSAntti Palosaari  * Copyright (C) 2014 Antti Palosaari <crope@iki.fi>
6877ba50bSAntti Palosaari  */
7877ba50bSAntti Palosaari 
8877ba50bSAntti Palosaari #include "mn88473_priv.h"
9877ba50bSAntti Palosaari 
mn88473_get_tune_settings(struct dvb_frontend * fe,struct dvb_frontend_tune_settings * s)10877ba50bSAntti Palosaari static int mn88473_get_tune_settings(struct dvb_frontend *fe,
11877ba50bSAntti Palosaari 				     struct dvb_frontend_tune_settings *s)
12877ba50bSAntti Palosaari {
13877ba50bSAntti Palosaari 	s->min_delay_ms = 1000;
14877ba50bSAntti Palosaari 	return 0;
15877ba50bSAntti Palosaari }
16877ba50bSAntti Palosaari 
mn88473_set_frontend(struct dvb_frontend * fe)17877ba50bSAntti Palosaari static int mn88473_set_frontend(struct dvb_frontend *fe)
18877ba50bSAntti Palosaari {
19877ba50bSAntti Palosaari 	struct i2c_client *client = fe->demodulator_priv;
20877ba50bSAntti Palosaari 	struct mn88473_dev *dev = i2c_get_clientdata(client);
21877ba50bSAntti Palosaari 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
22877ba50bSAntti Palosaari 	int ret, i;
237908fad9SAntti Palosaari 	unsigned int uitmp;
24877ba50bSAntti Palosaari 	u32 if_frequency;
257908fad9SAntti Palosaari 	u8 delivery_system_val, if_val[3], *conf_val_ptr;
267908fad9SAntti Palosaari 	u8 reg_bank2_2d_val, reg_bank0_d2_val;
27877ba50bSAntti Palosaari 
28877ba50bSAntti Palosaari 	dev_dbg(&client->dev,
29877ba50bSAntti Palosaari 		"delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%d stream_id=%d\n",
307908fad9SAntti Palosaari 		c->delivery_system, c->modulation, c->frequency,
317908fad9SAntti Palosaari 		c->bandwidth_hz, c->symbol_rate, c->inversion, c->stream_id);
32877ba50bSAntti Palosaari 
337908fad9SAntti Palosaari 	if (!dev->active) {
34877ba50bSAntti Palosaari 		ret = -EAGAIN;
35877ba50bSAntti Palosaari 		goto err;
36877ba50bSAntti Palosaari 	}
37877ba50bSAntti Palosaari 
38877ba50bSAntti Palosaari 	switch (c->delivery_system) {
39877ba50bSAntti Palosaari 	case SYS_DVBT:
40877ba50bSAntti Palosaari 		delivery_system_val = 0x02;
417908fad9SAntti Palosaari 		reg_bank2_2d_val = 0x23;
427908fad9SAntti Palosaari 		reg_bank0_d2_val = 0x2a;
43877ba50bSAntti Palosaari 		break;
44877ba50bSAntti Palosaari 	case SYS_DVBT2:
45877ba50bSAntti Palosaari 		delivery_system_val = 0x03;
467908fad9SAntti Palosaari 		reg_bank2_2d_val = 0x3b;
477908fad9SAntti Palosaari 		reg_bank0_d2_val = 0x29;
48877ba50bSAntti Palosaari 		break;
49877ba50bSAntti Palosaari 	case SYS_DVBC_ANNEX_A:
50877ba50bSAntti Palosaari 		delivery_system_val = 0x04;
517908fad9SAntti Palosaari 		reg_bank2_2d_val = 0x3b;
527908fad9SAntti Palosaari 		reg_bank0_d2_val = 0x29;
53877ba50bSAntti Palosaari 		break;
54877ba50bSAntti Palosaari 	default:
55877ba50bSAntti Palosaari 		ret = -EINVAL;
56877ba50bSAntti Palosaari 		goto err;
57877ba50bSAntti Palosaari 	}
58877ba50bSAntti Palosaari 
597908fad9SAntti Palosaari 	switch (c->delivery_system) {
607908fad9SAntti Palosaari 	case SYS_DVBT:
617908fad9SAntti Palosaari 	case SYS_DVBT2:
627908fad9SAntti Palosaari 		switch (c->bandwidth_hz) {
637908fad9SAntti Palosaari 		case 6000000:
647908fad9SAntti Palosaari 			conf_val_ptr = "\xe9\x55\x55\x1c\x29\x1c\x29";
657908fad9SAntti Palosaari 			break;
667908fad9SAntti Palosaari 		case 7000000:
677908fad9SAntti Palosaari 			conf_val_ptr = "\xc8\x00\x00\x17\x0a\x17\x0a";
687908fad9SAntti Palosaari 			break;
697908fad9SAntti Palosaari 		case 8000000:
707908fad9SAntti Palosaari 			conf_val_ptr = "\xaf\x00\x00\x11\xec\x11\xec";
717908fad9SAntti Palosaari 			break;
727908fad9SAntti Palosaari 		default:
73877ba50bSAntti Palosaari 			ret = -EINVAL;
74877ba50bSAntti Palosaari 			goto err;
75877ba50bSAntti Palosaari 		}
767908fad9SAntti Palosaari 		break;
777908fad9SAntti Palosaari 	case SYS_DVBC_ANNEX_A:
787908fad9SAntti Palosaari 		conf_val_ptr = "\x10\xab\x0d\xae\x1d\x9d";
797908fad9SAntti Palosaari 		break;
807908fad9SAntti Palosaari 	default:
817908fad9SAntti Palosaari 		break;
827908fad9SAntti Palosaari 	}
83877ba50bSAntti Palosaari 
847908fad9SAntti Palosaari 	/* Program tuner */
85877ba50bSAntti Palosaari 	if (fe->ops.tuner_ops.set_params) {
86877ba50bSAntti Palosaari 		ret = fe->ops.tuner_ops.set_params(fe);
87877ba50bSAntti Palosaari 		if (ret)
88877ba50bSAntti Palosaari 			goto err;
89877ba50bSAntti Palosaari 	}
90877ba50bSAntti Palosaari 
91877ba50bSAntti Palosaari 	if (fe->ops.tuner_ops.get_if_frequency) {
92877ba50bSAntti Palosaari 		ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency);
93877ba50bSAntti Palosaari 		if (ret)
94877ba50bSAntti Palosaari 			goto err;
95877ba50bSAntti Palosaari 
967908fad9SAntti Palosaari 		dev_dbg(&client->dev, "get_if_frequency=%u\n", if_frequency);
97877ba50bSAntti Palosaari 	} else {
987908fad9SAntti Palosaari 		ret = -EINVAL;
997908fad9SAntti Palosaari 		goto err;
100877ba50bSAntti Palosaari 	}
101877ba50bSAntti Palosaari 
1027908fad9SAntti Palosaari 	/* Calculate IF registers */
1037908fad9SAntti Palosaari 	uitmp = DIV_ROUND_CLOSEST_ULL((u64) if_frequency * 0x1000000, dev->clk);
1047908fad9SAntti Palosaari 	if_val[0] = (uitmp >> 16) & 0xff;
1057908fad9SAntti Palosaari 	if_val[1] = (uitmp >>  8) & 0xff;
1067908fad9SAntti Palosaari 	if_val[2] = (uitmp >>  0) & 0xff;
107877ba50bSAntti Palosaari 
108877ba50bSAntti Palosaari 	ret = regmap_write(dev->regmap[2], 0x05, 0x00);
1097908fad9SAntti Palosaari 	if (ret)
1107908fad9SAntti Palosaari 		goto err;
111877ba50bSAntti Palosaari 	ret = regmap_write(dev->regmap[2], 0xfb, 0x13);
1127908fad9SAntti Palosaari 	if (ret)
1137908fad9SAntti Palosaari 		goto err;
114877ba50bSAntti Palosaari 	ret = regmap_write(dev->regmap[2], 0xef, 0x13);
1157908fad9SAntti Palosaari 	if (ret)
1167908fad9SAntti Palosaari 		goto err;
117877ba50bSAntti Palosaari 	ret = regmap_write(dev->regmap[2], 0xf9, 0x13);
1187908fad9SAntti Palosaari 	if (ret)
1197908fad9SAntti Palosaari 		goto err;
120877ba50bSAntti Palosaari 	ret = regmap_write(dev->regmap[2], 0x00, 0x18);
1217908fad9SAntti Palosaari 	if (ret)
1227908fad9SAntti Palosaari 		goto err;
123877ba50bSAntti Palosaari 	ret = regmap_write(dev->regmap[2], 0x01, 0x01);
1247908fad9SAntti Palosaari 	if (ret)
1257908fad9SAntti Palosaari 		goto err;
126877ba50bSAntti Palosaari 	ret = regmap_write(dev->regmap[2], 0x02, 0x21);
1277908fad9SAntti Palosaari 	if (ret)
1287908fad9SAntti Palosaari 		goto err;
129877ba50bSAntti Palosaari 	ret = regmap_write(dev->regmap[2], 0x03, delivery_system_val);
1307908fad9SAntti Palosaari 	if (ret)
1317908fad9SAntti Palosaari 		goto err;
132877ba50bSAntti Palosaari 	ret = regmap_write(dev->regmap[2], 0x0b, 0x00);
1337908fad9SAntti Palosaari 	if (ret)
1347908fad9SAntti Palosaari 		goto err;
135877ba50bSAntti Palosaari 
136877ba50bSAntti Palosaari 	for (i = 0; i < sizeof(if_val); i++) {
137877ba50bSAntti Palosaari 		ret = regmap_write(dev->regmap[2], 0x10 + i, if_val[i]);
138877ba50bSAntti Palosaari 		if (ret)
139877ba50bSAntti Palosaari 			goto err;
140877ba50bSAntti Palosaari 	}
141877ba50bSAntti Palosaari 
1427908fad9SAntti Palosaari 	switch (c->delivery_system) {
1437908fad9SAntti Palosaari 	case SYS_DVBT:
1447908fad9SAntti Palosaari 	case SYS_DVBT2:
1457908fad9SAntti Palosaari 		for (i = 0; i < 7; i++) {
1467908fad9SAntti Palosaari 			ret = regmap_write(dev->regmap[2], 0x13 + i,
1477908fad9SAntti Palosaari 					   conf_val_ptr[i]);
148877ba50bSAntti Palosaari 			if (ret)
149877ba50bSAntti Palosaari 				goto err;
150877ba50bSAntti Palosaari 		}
1517908fad9SAntti Palosaari 		break;
1527908fad9SAntti Palosaari 	case SYS_DVBC_ANNEX_A:
1537908fad9SAntti Palosaari 		ret = regmap_bulk_write(dev->regmap[1], 0x10, conf_val_ptr, 6);
1547908fad9SAntti Palosaari 		if (ret)
1557908fad9SAntti Palosaari 			goto err;
1567908fad9SAntti Palosaari 		break;
1577908fad9SAntti Palosaari 	default:
1587908fad9SAntti Palosaari 		break;
1597908fad9SAntti Palosaari 	}
160877ba50bSAntti Palosaari 
1617908fad9SAntti Palosaari 	ret = regmap_write(dev->regmap[2], 0x2d, reg_bank2_2d_val);
1627908fad9SAntti Palosaari 	if (ret)
1637908fad9SAntti Palosaari 		goto err;
164877ba50bSAntti Palosaari 	ret = regmap_write(dev->regmap[2], 0x2e, 0x00);
1657908fad9SAntti Palosaari 	if (ret)
1667908fad9SAntti Palosaari 		goto err;
167877ba50bSAntti Palosaari 	ret = regmap_write(dev->regmap[2], 0x56, 0x0d);
1687908fad9SAntti Palosaari 	if (ret)
1697908fad9SAntti Palosaari 		goto err;
1707908fad9SAntti Palosaari 	ret = regmap_bulk_write(dev->regmap[0], 0x01,
1717908fad9SAntti Palosaari 				"\xba\x13\x80\xba\x91\xdd\xe7\x28", 8);
1727908fad9SAntti Palosaari 	if (ret)
1737908fad9SAntti Palosaari 		goto err;
174877ba50bSAntti Palosaari 	ret = regmap_write(dev->regmap[0], 0x0a, 0x1a);
1757908fad9SAntti Palosaari 	if (ret)
1767908fad9SAntti Palosaari 		goto err;
177877ba50bSAntti Palosaari 	ret = regmap_write(dev->regmap[0], 0x13, 0x1f);
1787908fad9SAntti Palosaari 	if (ret)
1797908fad9SAntti Palosaari 		goto err;
180877ba50bSAntti Palosaari 	ret = regmap_write(dev->regmap[0], 0x19, 0x03);
1817908fad9SAntti Palosaari 	if (ret)
1827908fad9SAntti Palosaari 		goto err;
183877ba50bSAntti Palosaari 	ret = regmap_write(dev->regmap[0], 0x1d, 0xb0);
1847908fad9SAntti Palosaari 	if (ret)
1857908fad9SAntti Palosaari 		goto err;
186877ba50bSAntti Palosaari 	ret = regmap_write(dev->regmap[0], 0x2a, 0x72);
1877908fad9SAntti Palosaari 	if (ret)
1887908fad9SAntti Palosaari 		goto err;
189877ba50bSAntti Palosaari 	ret = regmap_write(dev->regmap[0], 0x2d, 0x00);
1907908fad9SAntti Palosaari 	if (ret)
1917908fad9SAntti Palosaari 		goto err;
192877ba50bSAntti Palosaari 	ret = regmap_write(dev->regmap[0], 0x3c, 0x00);
1937908fad9SAntti Palosaari 	if (ret)
1947908fad9SAntti Palosaari 		goto err;
195877ba50bSAntti Palosaari 	ret = regmap_write(dev->regmap[0], 0x3f, 0xf8);
1967908fad9SAntti Palosaari 	if (ret)
1977908fad9SAntti Palosaari 		goto err;
1987908fad9SAntti Palosaari 	ret = regmap_bulk_write(dev->regmap[0], 0x40, "\xf4\x08", 2);
1997908fad9SAntti Palosaari 	if (ret)
2007908fad9SAntti Palosaari 		goto err;
2017908fad9SAntti Palosaari 	ret = regmap_write(dev->regmap[0], 0xd2, reg_bank0_d2_val);
2027908fad9SAntti Palosaari 	if (ret)
2037908fad9SAntti Palosaari 		goto err;
204877ba50bSAntti Palosaari 	ret = regmap_write(dev->regmap[0], 0xd4, 0x55);
2057908fad9SAntti Palosaari 	if (ret)
2067908fad9SAntti Palosaari 		goto err;
207877ba50bSAntti Palosaari 	ret = regmap_write(dev->regmap[1], 0xbe, 0x08);
2087908fad9SAntti Palosaari 	if (ret)
2097908fad9SAntti Palosaari 		goto err;
210877ba50bSAntti Palosaari 	ret = regmap_write(dev->regmap[0], 0xb2, 0x37);
2117908fad9SAntti Palosaari 	if (ret)
2127908fad9SAntti Palosaari 		goto err;
213877ba50bSAntti Palosaari 	ret = regmap_write(dev->regmap[0], 0xd7, 0x04);
214877ba50bSAntti Palosaari 	if (ret)
215877ba50bSAntti Palosaari 		goto err;
216877ba50bSAntti Palosaari 
21740eca140SAntti Palosaari 	/* PLP */
21840eca140SAntti Palosaari 	if (c->delivery_system == SYS_DVBT2) {
2192fb0e047SOlli Salonen 		ret = regmap_write(dev->regmap[2], 0x36,
2202fb0e047SOlli Salonen 				(c->stream_id == NO_STREAM_ID_FILTER) ? 0 :
2212fb0e047SOlli Salonen 				c->stream_id );
22240eca140SAntti Palosaari 		if (ret)
22340eca140SAntti Palosaari 			goto err;
22440eca140SAntti Palosaari 	}
22540eca140SAntti Palosaari 
2267908fad9SAntti Palosaari 	/* Reset FSM */
2277908fad9SAntti Palosaari 	ret = regmap_write(dev->regmap[2], 0xf8, 0x9f);
2287908fad9SAntti Palosaari 	if (ret)
2297908fad9SAntti Palosaari 		goto err;
230877ba50bSAntti Palosaari 
231877ba50bSAntti Palosaari 	return 0;
232877ba50bSAntti Palosaari err:
233877ba50bSAntti Palosaari 	dev_dbg(&client->dev, "failed=%d\n", ret);
234877ba50bSAntti Palosaari 	return ret;
235877ba50bSAntti Palosaari }
236877ba50bSAntti Palosaari 
mn88473_read_status(struct dvb_frontend * fe,enum fe_status * status)23761393b07SMartin Blumenstingl static int mn88473_read_status(struct dvb_frontend *fe, enum fe_status *status)
23861393b07SMartin Blumenstingl {
23961393b07SMartin Blumenstingl 	struct i2c_client *client = fe->demodulator_priv;
24061393b07SMartin Blumenstingl 	struct mn88473_dev *dev = i2c_get_clientdata(client);
24161393b07SMartin Blumenstingl 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
24269ace6eeSAntti Palosaari 	int ret, i, stmp;
24369ace6eeSAntti Palosaari 	unsigned int utmp, utmp1, utmp2;
24469ace6eeSAntti Palosaari 	u8 buf[5];
24561393b07SMartin Blumenstingl 
24661393b07SMartin Blumenstingl 	if (!dev->active) {
24761393b07SMartin Blumenstingl 		ret = -EAGAIN;
24861393b07SMartin Blumenstingl 		goto err;
24961393b07SMartin Blumenstingl 	}
25061393b07SMartin Blumenstingl 
25169ace6eeSAntti Palosaari 	/* Lock detection */
25261393b07SMartin Blumenstingl 	switch (c->delivery_system) {
25361393b07SMartin Blumenstingl 	case SYS_DVBT:
25469ace6eeSAntti Palosaari 		ret = regmap_read(dev->regmap[0], 0x62, &utmp);
25569ace6eeSAntti Palosaari 		if (ret)
25669ace6eeSAntti Palosaari 			goto err;
25769ace6eeSAntti Palosaari 
25869ace6eeSAntti Palosaari 		if (!(utmp & 0xa0)) {
25969ace6eeSAntti Palosaari 			if ((utmp & 0x0f) >= 0x09)
26069ace6eeSAntti Palosaari 				*status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
26169ace6eeSAntti Palosaari 					  FE_HAS_VITERBI | FE_HAS_SYNC |
26269ace6eeSAntti Palosaari 					  FE_HAS_LOCK;
26369ace6eeSAntti Palosaari 			else if ((utmp & 0x0f) >= 0x03)
26469ace6eeSAntti Palosaari 				*status = FE_HAS_SIGNAL | FE_HAS_CARRIER;
26569ace6eeSAntti Palosaari 		} else {
26669ace6eeSAntti Palosaari 			*status = 0;
26769ace6eeSAntti Palosaari 		}
26861393b07SMartin Blumenstingl 		break;
26961393b07SMartin Blumenstingl 	case SYS_DVBT2:
27069ace6eeSAntti Palosaari 		ret = regmap_read(dev->regmap[2], 0x8b, &utmp);
27169ace6eeSAntti Palosaari 		if (ret)
27269ace6eeSAntti Palosaari 			goto err;
27369ace6eeSAntti Palosaari 
27469ace6eeSAntti Palosaari 		if (!(utmp & 0x40)) {
27569ace6eeSAntti Palosaari 			if ((utmp & 0x0f) >= 0x0d)
27669ace6eeSAntti Palosaari 				*status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
27769ace6eeSAntti Palosaari 					  FE_HAS_VITERBI | FE_HAS_SYNC |
27869ace6eeSAntti Palosaari 					  FE_HAS_LOCK;
27969ace6eeSAntti Palosaari 			else if ((utmp & 0x0f) >= 0x0a)
28069ace6eeSAntti Palosaari 				*status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
28169ace6eeSAntti Palosaari 					  FE_HAS_VITERBI;
28269ace6eeSAntti Palosaari 			else if ((utmp & 0x0f) >= 0x07)
28369ace6eeSAntti Palosaari 				*status = FE_HAS_SIGNAL | FE_HAS_CARRIER;
28469ace6eeSAntti Palosaari 		} else {
28569ace6eeSAntti Palosaari 			*status = 0;
28669ace6eeSAntti Palosaari 		}
28761393b07SMartin Blumenstingl 		break;
28861393b07SMartin Blumenstingl 	case SYS_DVBC_ANNEX_A:
28969ace6eeSAntti Palosaari 		ret = regmap_read(dev->regmap[1], 0x85, &utmp);
29069ace6eeSAntti Palosaari 		if (ret)
29169ace6eeSAntti Palosaari 			goto err;
29269ace6eeSAntti Palosaari 
29369ace6eeSAntti Palosaari 		if (!(utmp & 0x40)) {
29469ace6eeSAntti Palosaari 			ret = regmap_read(dev->regmap[1], 0x89, &utmp);
29569ace6eeSAntti Palosaari 			if (ret)
29669ace6eeSAntti Palosaari 				goto err;
29769ace6eeSAntti Palosaari 
29869ace6eeSAntti Palosaari 			if (utmp & 0x01)
29969ace6eeSAntti Palosaari 				*status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
30069ace6eeSAntti Palosaari 						FE_HAS_VITERBI | FE_HAS_SYNC |
30169ace6eeSAntti Palosaari 						FE_HAS_LOCK;
30269ace6eeSAntti Palosaari 		} else {
30369ace6eeSAntti Palosaari 			*status = 0;
30469ace6eeSAntti Palosaari 		}
305877ba50bSAntti Palosaari 		break;
306877ba50bSAntti Palosaari 	default:
307877ba50bSAntti Palosaari 		ret = -EINVAL;
30869ace6eeSAntti Palosaari 		goto err;
30961393b07SMartin Blumenstingl 	}
31061393b07SMartin Blumenstingl 
31169ace6eeSAntti Palosaari 	/* Signal strength */
31261393b07SMartin Blumenstingl 	if (*status & FE_HAS_SIGNAL) {
31369ace6eeSAntti Palosaari 		for (i = 0; i < 2; i++) {
31469ace6eeSAntti Palosaari 			ret = regmap_bulk_read(dev->regmap[2], 0x86 + i,
31569ace6eeSAntti Palosaari 					       &buf[i], 1);
31661393b07SMartin Blumenstingl 			if (ret)
31761393b07SMartin Blumenstingl 				goto err;
31869ace6eeSAntti Palosaari 		}
31961393b07SMartin Blumenstingl 
32061393b07SMartin Blumenstingl 		/* AGCRD[15:6] gives us a 10bit value ([5:0] are always 0) */
32169ace6eeSAntti Palosaari 		utmp1 = buf[0] << 8 | buf[1] << 0 | buf[0] >> 2;
32269ace6eeSAntti Palosaari 		dev_dbg(&client->dev, "strength=%u\n", utmp1);
32369ace6eeSAntti Palosaari 
32461393b07SMartin Blumenstingl 		c->strength.stat[0].scale = FE_SCALE_RELATIVE;
32569ace6eeSAntti Palosaari 		c->strength.stat[0].uvalue = utmp1;
32661393b07SMartin Blumenstingl 	} else {
32761393b07SMartin Blumenstingl 		c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
32861393b07SMartin Blumenstingl 	}
32961393b07SMartin Blumenstingl 
33069ace6eeSAntti Palosaari 	/* CNR */
33169ace6eeSAntti Palosaari 	if (*status & FE_HAS_VITERBI && c->delivery_system == SYS_DVBT) {
33269ace6eeSAntti Palosaari 		/* DVB-T CNR */
33369ace6eeSAntti Palosaari 		ret = regmap_bulk_read(dev->regmap[0], 0x8f, buf, 2);
33469ace6eeSAntti Palosaari 		if (ret)
33569ace6eeSAntti Palosaari 			goto err;
33669ace6eeSAntti Palosaari 
33769ace6eeSAntti Palosaari 		utmp = buf[0] << 8 | buf[1] << 0;
33869ace6eeSAntti Palosaari 		if (utmp) {
33969ace6eeSAntti Palosaari 			/* CNR[dB]: 10 * (log10(65536 / value) + 0.2) */
34069ace6eeSAntti Palosaari 			/* log10(65536) = 80807124, 0.2 = 3355443 */
34169ace6eeSAntti Palosaari 			stmp = div_u64(((u64)80807124 - intlog10(utmp)
34269ace6eeSAntti Palosaari 					+ 3355443) * 10000, 1 << 24);
34369ace6eeSAntti Palosaari 			dev_dbg(&client->dev, "cnr=%d value=%u\n", stmp, utmp);
34469ace6eeSAntti Palosaari 		} else {
34569ace6eeSAntti Palosaari 			stmp = 0;
34669ace6eeSAntti Palosaari 		}
34769ace6eeSAntti Palosaari 
34869ace6eeSAntti Palosaari 		c->cnr.stat[0].svalue = stmp;
34969ace6eeSAntti Palosaari 		c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
35069ace6eeSAntti Palosaari 	} else if (*status & FE_HAS_VITERBI &&
35169ace6eeSAntti Palosaari 		   c->delivery_system == SYS_DVBT2) {
35269ace6eeSAntti Palosaari 		/* DVB-T2 CNR */
35369ace6eeSAntti Palosaari 		for (i = 0; i < 3; i++) {
35469ace6eeSAntti Palosaari 			ret = regmap_bulk_read(dev->regmap[2], 0xb7 + i,
35569ace6eeSAntti Palosaari 					       &buf[i], 1);
35669ace6eeSAntti Palosaari 			if (ret)
35769ace6eeSAntti Palosaari 				goto err;
35869ace6eeSAntti Palosaari 		}
35969ace6eeSAntti Palosaari 
36069ace6eeSAntti Palosaari 		utmp = buf[1] << 8 | buf[2] << 0;
36169ace6eeSAntti Palosaari 		utmp1 = (buf[0] >> 2) & 0x01; /* 0=SISO, 1=MISO */
36269ace6eeSAntti Palosaari 		if (utmp) {
36369ace6eeSAntti Palosaari 			if (utmp1) {
36469ace6eeSAntti Palosaari 				/* CNR[dB]: 10 * (log10(16384 / value) - 0.6) */
36569ace6eeSAntti Palosaari 				/* log10(16384) = 70706234, 0.6 = 10066330 */
36669ace6eeSAntti Palosaari 				stmp = div_u64(((u64)70706234 - intlog10(utmp)
36769ace6eeSAntti Palosaari 						- 10066330) * 10000, 1 << 24);
36869ace6eeSAntti Palosaari 				dev_dbg(&client->dev, "cnr=%d value=%u MISO\n",
36969ace6eeSAntti Palosaari 					stmp, utmp);
37069ace6eeSAntti Palosaari 			} else {
37169ace6eeSAntti Palosaari 				/* CNR[dB]: 10 * (log10(65536 / value) + 0.2) */
37269ace6eeSAntti Palosaari 				/* log10(65536) = 80807124, 0.2 = 3355443 */
37369ace6eeSAntti Palosaari 				stmp = div_u64(((u64)80807124 - intlog10(utmp)
37469ace6eeSAntti Palosaari 						+ 3355443) * 10000, 1 << 24);
37569ace6eeSAntti Palosaari 				dev_dbg(&client->dev, "cnr=%d value=%u SISO\n",
37669ace6eeSAntti Palosaari 					stmp, utmp);
37769ace6eeSAntti Palosaari 			}
37869ace6eeSAntti Palosaari 		} else {
37969ace6eeSAntti Palosaari 			stmp = 0;
38069ace6eeSAntti Palosaari 		}
38169ace6eeSAntti Palosaari 
38269ace6eeSAntti Palosaari 		c->cnr.stat[0].svalue = stmp;
38369ace6eeSAntti Palosaari 		c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
38469ace6eeSAntti Palosaari 	} else if (*status & FE_HAS_VITERBI &&
38569ace6eeSAntti Palosaari 		   c->delivery_system == SYS_DVBC_ANNEX_A) {
38669ace6eeSAntti Palosaari 		/* DVB-C CNR */
38769ace6eeSAntti Palosaari 		ret = regmap_bulk_read(dev->regmap[1], 0xa1, buf, 4);
38869ace6eeSAntti Palosaari 		if (ret)
38969ace6eeSAntti Palosaari 			goto err;
39069ace6eeSAntti Palosaari 
39169ace6eeSAntti Palosaari 		utmp1 = buf[0] << 8 | buf[1] << 0; /* signal */
39269ace6eeSAntti Palosaari 		utmp2 = buf[2] << 8 | buf[3] << 0; /* noise */
39369ace6eeSAntti Palosaari 		if (utmp1 && utmp2) {
39469ace6eeSAntti Palosaari 			/* CNR[dB]: 10 * log10(8 * (signal / noise)) */
39569ace6eeSAntti Palosaari 			/* log10(8) = 15151336 */
39669ace6eeSAntti Palosaari 			stmp = div_u64(((u64)15151336 + intlog10(utmp1)
39769ace6eeSAntti Palosaari 					- intlog10(utmp2)) * 10000, 1 << 24);
39869ace6eeSAntti Palosaari 			dev_dbg(&client->dev, "cnr=%d signal=%u noise=%u\n",
39969ace6eeSAntti Palosaari 				stmp, utmp1, utmp2);
40069ace6eeSAntti Palosaari 		} else {
40169ace6eeSAntti Palosaari 			stmp = 0;
40269ace6eeSAntti Palosaari 		}
40369ace6eeSAntti Palosaari 
40469ace6eeSAntti Palosaari 		c->cnr.stat[0].svalue = stmp;
40569ace6eeSAntti Palosaari 		c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
40669ace6eeSAntti Palosaari 	} else {
40769ace6eeSAntti Palosaari 		c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
40869ace6eeSAntti Palosaari 	}
40969ace6eeSAntti Palosaari 
41069ace6eeSAntti Palosaari 	/* BER */
41169ace6eeSAntti Palosaari 	if (*status & FE_HAS_LOCK && (c->delivery_system == SYS_DVBT ||
41269ace6eeSAntti Palosaari 				      c->delivery_system == SYS_DVBC_ANNEX_A)) {
41369ace6eeSAntti Palosaari 		/* DVB-T & DVB-C BER */
41469ace6eeSAntti Palosaari 		ret = regmap_bulk_read(dev->regmap[0], 0x92, buf, 5);
41569ace6eeSAntti Palosaari 		if (ret)
41669ace6eeSAntti Palosaari 			goto err;
41769ace6eeSAntti Palosaari 
41869ace6eeSAntti Palosaari 		utmp1 = buf[0] << 16 | buf[1] << 8 | buf[2] << 0;
41969ace6eeSAntti Palosaari 		utmp2 = buf[3] << 8 | buf[4] << 0;
42069ace6eeSAntti Palosaari 		utmp2 = utmp2 * 8 * 204;
42169ace6eeSAntti Palosaari 		dev_dbg(&client->dev, "post_bit_error=%u post_bit_count=%u\n",
42269ace6eeSAntti Palosaari 			utmp1, utmp2);
42369ace6eeSAntti Palosaari 
42469ace6eeSAntti Palosaari 		c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
42569ace6eeSAntti Palosaari 		c->post_bit_error.stat[0].uvalue += utmp1;
42669ace6eeSAntti Palosaari 		c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
42769ace6eeSAntti Palosaari 		c->post_bit_count.stat[0].uvalue += utmp2;
42869ace6eeSAntti Palosaari 	} else {
42969ace6eeSAntti Palosaari 		c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
43069ace6eeSAntti Palosaari 		c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
43169ace6eeSAntti Palosaari 	}
43269ace6eeSAntti Palosaari 
43361393b07SMartin Blumenstingl 	/* PER */
43461393b07SMartin Blumenstingl 	if (*status & FE_HAS_LOCK) {
43569ace6eeSAntti Palosaari 		ret = regmap_bulk_read(dev->regmap[0], 0xdd, buf, 4);
43661393b07SMartin Blumenstingl 		if (ret)
43761393b07SMartin Blumenstingl 			goto err;
43861393b07SMartin Blumenstingl 
43969ace6eeSAntti Palosaari 		utmp1 = buf[0] << 8 | buf[1] << 0;
44069ace6eeSAntti Palosaari 		utmp2 = buf[2] << 8 | buf[3] << 0;
44169ace6eeSAntti Palosaari 		dev_dbg(&client->dev, "block_error=%u block_count=%u\n",
44269ace6eeSAntti Palosaari 			utmp1, utmp2);
44361393b07SMartin Blumenstingl 
44461393b07SMartin Blumenstingl 		c->block_error.stat[0].scale = FE_SCALE_COUNTER;
44569ace6eeSAntti Palosaari 		c->block_error.stat[0].uvalue += utmp1;
44661393b07SMartin Blumenstingl 		c->block_count.stat[0].scale = FE_SCALE_COUNTER;
44769ace6eeSAntti Palosaari 		c->block_count.stat[0].uvalue += utmp2;
44861393b07SMartin Blumenstingl 	} else {
44969ace6eeSAntti Palosaari 		c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
45069ace6eeSAntti Palosaari 		c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
451877ba50bSAntti Palosaari 	}
452877ba50bSAntti Palosaari 
453877ba50bSAntti Palosaari 	return 0;
454877ba50bSAntti Palosaari err:
45569ace6eeSAntti Palosaari 	dev_dbg(&client->dev, "failed=%d\n", ret);
456877ba50bSAntti Palosaari 	return ret;
457877ba50bSAntti Palosaari }
458877ba50bSAntti Palosaari 
mn88473_init(struct dvb_frontend * fe)459877ba50bSAntti Palosaari static int mn88473_init(struct dvb_frontend *fe)
460877ba50bSAntti Palosaari {
461877ba50bSAntti Palosaari 	struct i2c_client *client = fe->demodulator_priv;
462877ba50bSAntti Palosaari 	struct mn88473_dev *dev = i2c_get_clientdata(client);
46361393b07SMartin Blumenstingl 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
4647908fad9SAntti Palosaari 	int ret, len, remain;
4657908fad9SAntti Palosaari 	unsigned int uitmp;
4667908fad9SAntti Palosaari 	const struct firmware *fw;
4677908fad9SAntti Palosaari 	const char *name = MN88473_FIRMWARE;
468877ba50bSAntti Palosaari 
469877ba50bSAntti Palosaari 	dev_dbg(&client->dev, "\n");
470877ba50bSAntti Palosaari 
4717908fad9SAntti Palosaari 	/* Check if firmware is already running */
4727908fad9SAntti Palosaari 	ret = regmap_read(dev->regmap[0], 0xf5, &uitmp);
473877ba50bSAntti Palosaari 	if (ret)
474877ba50bSAntti Palosaari 		goto err;
475877ba50bSAntti Palosaari 
4767908fad9SAntti Palosaari 	if (!(uitmp & 0x01))
4777908fad9SAntti Palosaari 		goto warm;
478877ba50bSAntti Palosaari 
4797908fad9SAntti Palosaari 	/* Request the firmware, this will block and timeout */
4807908fad9SAntti Palosaari 	ret = request_firmware(&fw, name, &client->dev);
481877ba50bSAntti Palosaari 	if (ret) {
482c5ee19c8SJulia Lawall 		dev_err(&client->dev, "firmware file '%s' not found\n", name);
4837908fad9SAntti Palosaari 		goto err;
484877ba50bSAntti Palosaari 	}
485877ba50bSAntti Palosaari 
4867908fad9SAntti Palosaari 	dev_info(&client->dev, "downloading firmware from file '%s'\n", name);
487877ba50bSAntti Palosaari 
488877ba50bSAntti Palosaari 	ret = regmap_write(dev->regmap[0], 0xf5, 0x03);
489877ba50bSAntti Palosaari 	if (ret)
4907908fad9SAntti Palosaari 		goto err_release_firmware;
491877ba50bSAntti Palosaari 
4927908fad9SAntti Palosaari 	for (remain = fw->size; remain > 0; remain -= (dev->i2c_wr_max - 1)) {
4937908fad9SAntti Palosaari 		len = min(dev->i2c_wr_max - 1, remain);
494877ba50bSAntti Palosaari 		ret = regmap_bulk_write(dev->regmap[0], 0xf6,
4957908fad9SAntti Palosaari 					&fw->data[fw->size - remain], len);
496877ba50bSAntti Palosaari 		if (ret) {
4977908fad9SAntti Palosaari 			dev_err(&client->dev, "firmware download failed %d\n",
498877ba50bSAntti Palosaari 				ret);
4997908fad9SAntti Palosaari 			goto err_release_firmware;
500877ba50bSAntti Palosaari 		}
501877ba50bSAntti Palosaari 	}
502877ba50bSAntti Palosaari 
5037908fad9SAntti Palosaari 	release_firmware(fw);
5047908fad9SAntti Palosaari 
5057908fad9SAntti Palosaari 	/* Parity check of firmware */
5067908fad9SAntti Palosaari 	ret = regmap_read(dev->regmap[0], 0xf8, &uitmp);
5077908fad9SAntti Palosaari 	if (ret)
5087908fad9SAntti Palosaari 		goto err;
5097908fad9SAntti Palosaari 
5107908fad9SAntti Palosaari 	if (uitmp & 0x10) {
5117908fad9SAntti Palosaari 		dev_err(&client->dev, "firmware parity check failed\n");
5127908fad9SAntti Palosaari 		ret = -EINVAL;
513877ba50bSAntti Palosaari 		goto err;
514877ba50bSAntti Palosaari 	}
515877ba50bSAntti Palosaari 
516877ba50bSAntti Palosaari 	ret = regmap_write(dev->regmap[0], 0xf5, 0x00);
517877ba50bSAntti Palosaari 	if (ret)
518877ba50bSAntti Palosaari 		goto err;
5197908fad9SAntti Palosaari warm:
5207908fad9SAntti Palosaari 	/* TS config */
5217908fad9SAntti Palosaari 	ret = regmap_write(dev->regmap[2], 0x09, 0x08);
5227908fad9SAntti Palosaari 	if (ret)
5237908fad9SAntti Palosaari 		goto err;
5247908fad9SAntti Palosaari 	ret = regmap_write(dev->regmap[2], 0x08, 0x1d);
5257908fad9SAntti Palosaari 	if (ret)
5267908fad9SAntti Palosaari 		goto err;
527877ba50bSAntti Palosaari 
5287908fad9SAntti Palosaari 	dev->active = true;
529877ba50bSAntti Palosaari 
53061393b07SMartin Blumenstingl 	/* init stats here to indicate which stats are supported */
53161393b07SMartin Blumenstingl 	c->strength.len = 1;
53261393b07SMartin Blumenstingl 	c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
53361393b07SMartin Blumenstingl 	c->cnr.len = 1;
53461393b07SMartin Blumenstingl 	c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
53561393b07SMartin Blumenstingl 	c->post_bit_error.len = 1;
53661393b07SMartin Blumenstingl 	c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
53761393b07SMartin Blumenstingl 	c->post_bit_count.len = 1;
53861393b07SMartin Blumenstingl 	c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
53961393b07SMartin Blumenstingl 	c->block_error.len = 1;
54061393b07SMartin Blumenstingl 	c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
54161393b07SMartin Blumenstingl 	c->block_count.len = 1;
54261393b07SMartin Blumenstingl 	c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
54361393b07SMartin Blumenstingl 
544877ba50bSAntti Palosaari 	return 0;
5457908fad9SAntti Palosaari err_release_firmware:
546877ba50bSAntti Palosaari 	release_firmware(fw);
5477908fad9SAntti Palosaari err:
548877ba50bSAntti Palosaari 	dev_dbg(&client->dev, "failed=%d\n", ret);
549877ba50bSAntti Palosaari 	return ret;
550877ba50bSAntti Palosaari }
551877ba50bSAntti Palosaari 
mn88473_sleep(struct dvb_frontend * fe)552877ba50bSAntti Palosaari static int mn88473_sleep(struct dvb_frontend *fe)
553877ba50bSAntti Palosaari {
554877ba50bSAntti Palosaari 	struct i2c_client *client = fe->demodulator_priv;
555877ba50bSAntti Palosaari 	struct mn88473_dev *dev = i2c_get_clientdata(client);
556877ba50bSAntti Palosaari 	int ret;
557877ba50bSAntti Palosaari 
558877ba50bSAntti Palosaari 	dev_dbg(&client->dev, "\n");
559877ba50bSAntti Palosaari 
5607908fad9SAntti Palosaari 	dev->active = false;
5617908fad9SAntti Palosaari 
562877ba50bSAntti Palosaari 	ret = regmap_write(dev->regmap[2], 0x05, 0x3e);
563877ba50bSAntti Palosaari 	if (ret)
564877ba50bSAntti Palosaari 		goto err;
565877ba50bSAntti Palosaari 
566877ba50bSAntti Palosaari 	return 0;
567877ba50bSAntti Palosaari err:
568877ba50bSAntti Palosaari 	dev_dbg(&client->dev, "failed=%d\n", ret);
569877ba50bSAntti Palosaari 	return ret;
570877ba50bSAntti Palosaari }
571877ba50bSAntti Palosaari 
5727908fad9SAntti Palosaari static const struct dvb_frontend_ops mn88473_ops = {
5737908fad9SAntti Palosaari 	.delsys = {SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A},
574877ba50bSAntti Palosaari 	.info = {
575877ba50bSAntti Palosaari 		.name = "Panasonic MN88473",
576877ba50bSAntti Palosaari 		.symbol_rate_min = 1000000,
577877ba50bSAntti Palosaari 		.symbol_rate_max = 7200000,
578877ba50bSAntti Palosaari 		.caps =	FE_CAN_FEC_1_2                 |
579877ba50bSAntti Palosaari 			FE_CAN_FEC_2_3                 |
580877ba50bSAntti Palosaari 			FE_CAN_FEC_3_4                 |
581877ba50bSAntti Palosaari 			FE_CAN_FEC_5_6                 |
582877ba50bSAntti Palosaari 			FE_CAN_FEC_7_8                 |
583877ba50bSAntti Palosaari 			FE_CAN_FEC_AUTO                |
584877ba50bSAntti Palosaari 			FE_CAN_QPSK                    |
585877ba50bSAntti Palosaari 			FE_CAN_QAM_16                  |
586877ba50bSAntti Palosaari 			FE_CAN_QAM_32                  |
587877ba50bSAntti Palosaari 			FE_CAN_QAM_64                  |
588877ba50bSAntti Palosaari 			FE_CAN_QAM_128                 |
589877ba50bSAntti Palosaari 			FE_CAN_QAM_256                 |
590877ba50bSAntti Palosaari 			FE_CAN_QAM_AUTO                |
591877ba50bSAntti Palosaari 			FE_CAN_TRANSMISSION_MODE_AUTO  |
592877ba50bSAntti Palosaari 			FE_CAN_GUARD_INTERVAL_AUTO     |
593877ba50bSAntti Palosaari 			FE_CAN_HIERARCHY_AUTO          |
594877ba50bSAntti Palosaari 			FE_CAN_MUTE_TS                 |
59540eca140SAntti Palosaari 			FE_CAN_2G_MODULATION           |
59640eca140SAntti Palosaari 			FE_CAN_MULTISTREAM
597877ba50bSAntti Palosaari 	},
598877ba50bSAntti Palosaari 
599877ba50bSAntti Palosaari 	.get_tune_settings = mn88473_get_tune_settings,
600877ba50bSAntti Palosaari 
601877ba50bSAntti Palosaari 	.init = mn88473_init,
602877ba50bSAntti Palosaari 	.sleep = mn88473_sleep,
603877ba50bSAntti Palosaari 
604877ba50bSAntti Palosaari 	.set_frontend = mn88473_set_frontend,
605877ba50bSAntti Palosaari 
606877ba50bSAntti Palosaari 	.read_status = mn88473_read_status,
607877ba50bSAntti Palosaari };
608877ba50bSAntti Palosaari 
mn88473_probe(struct i2c_client * client)60932f2129cSUwe Kleine-König static int mn88473_probe(struct i2c_client *client)
610877ba50bSAntti Palosaari {
611877ba50bSAntti Palosaari 	struct mn88473_config *config = client->dev.platform_data;
612877ba50bSAntti Palosaari 	struct mn88473_dev *dev;
613877ba50bSAntti Palosaari 	int ret;
6147908fad9SAntti Palosaari 	unsigned int uitmp;
615877ba50bSAntti Palosaari 	static const struct regmap_config regmap_config = {
616877ba50bSAntti Palosaari 		.reg_bits = 8,
617877ba50bSAntti Palosaari 		.val_bits = 8,
618877ba50bSAntti Palosaari 	};
619877ba50bSAntti Palosaari 
620877ba50bSAntti Palosaari 	dev_dbg(&client->dev, "\n");
621877ba50bSAntti Palosaari 
6227908fad9SAntti Palosaari 	/* Caller really need to provide pointer for frontend we create */
623877ba50bSAntti Palosaari 	if (config->fe == NULL) {
624877ba50bSAntti Palosaari 		dev_err(&client->dev, "frontend pointer not defined\n");
625877ba50bSAntti Palosaari 		ret = -EINVAL;
626877ba50bSAntti Palosaari 		goto err;
627877ba50bSAntti Palosaari 	}
628877ba50bSAntti Palosaari 
629877ba50bSAntti Palosaari 	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
630877ba50bSAntti Palosaari 	if (dev == NULL) {
631877ba50bSAntti Palosaari 		ret = -ENOMEM;
632877ba50bSAntti Palosaari 		goto err;
633877ba50bSAntti Palosaari 	}
634877ba50bSAntti Palosaari 
6357908fad9SAntti Palosaari 	if (config->i2c_wr_max)
636877ba50bSAntti Palosaari 		dev->i2c_wr_max = config->i2c_wr_max;
637877ba50bSAntti Palosaari 	else
6387908fad9SAntti Palosaari 		dev->i2c_wr_max = ~0;
6397908fad9SAntti Palosaari 
6407908fad9SAntti Palosaari 	if (config->xtal)
6417908fad9SAntti Palosaari 		dev->clk = config->xtal;
6427908fad9SAntti Palosaari 	else
6437908fad9SAntti Palosaari 		dev->clk = 25000000;
644877ba50bSAntti Palosaari 	dev->client[0] = client;
645877ba50bSAntti Palosaari 	dev->regmap[0] = regmap_init_i2c(dev->client[0], &regmap_config);
646877ba50bSAntti Palosaari 	if (IS_ERR(dev->regmap[0])) {
647877ba50bSAntti Palosaari 		ret = PTR_ERR(dev->regmap[0]);
648877ba50bSAntti Palosaari 		goto err_kfree;
649877ba50bSAntti Palosaari 	}
650877ba50bSAntti Palosaari 
651877ba50bSAntti Palosaari 	/*
6527908fad9SAntti Palosaari 	 * Chip has three I2C addresses for different register banks. Used
653877ba50bSAntti Palosaari 	 * addresses are 0x18, 0x1a and 0x1c. We register two dummy clients,
6547908fad9SAntti Palosaari 	 * 0x1a and 0x1c, in order to get own I2C client for each register bank.
6557908fad9SAntti Palosaari 	 *
6567908fad9SAntti Palosaari 	 * Also, register bank 2 do not support sequential I/O. Only single
6577908fad9SAntti Palosaari 	 * register write or read is allowed to that bank.
658877ba50bSAntti Palosaari 	 */
6594cdd5156SWolfram Sang 	dev->client[1] = i2c_new_dummy_device(client->adapter, 0x1a);
6604cdd5156SWolfram Sang 	if (IS_ERR(dev->client[1])) {
6614cdd5156SWolfram Sang 		ret = PTR_ERR(dev->client[1]);
662877ba50bSAntti Palosaari 		dev_err(&client->dev, "I2C registration failed\n");
663877ba50bSAntti Palosaari 		goto err_regmap_0_regmap_exit;
664877ba50bSAntti Palosaari 	}
665877ba50bSAntti Palosaari 	dev->regmap[1] = regmap_init_i2c(dev->client[1], &regmap_config);
666877ba50bSAntti Palosaari 	if (IS_ERR(dev->regmap[1])) {
667877ba50bSAntti Palosaari 		ret = PTR_ERR(dev->regmap[1]);
668877ba50bSAntti Palosaari 		goto err_client_1_i2c_unregister_device;
669877ba50bSAntti Palosaari 	}
670877ba50bSAntti Palosaari 	i2c_set_clientdata(dev->client[1], dev);
671877ba50bSAntti Palosaari 
6724cdd5156SWolfram Sang 	dev->client[2] = i2c_new_dummy_device(client->adapter, 0x1c);
6734cdd5156SWolfram Sang 	if (IS_ERR(dev->client[2])) {
6744cdd5156SWolfram Sang 		ret = PTR_ERR(dev->client[2]);
675877ba50bSAntti Palosaari 		dev_err(&client->dev, "2nd I2C registration failed\n");
676877ba50bSAntti Palosaari 		goto err_regmap_1_regmap_exit;
677877ba50bSAntti Palosaari 	}
678877ba50bSAntti Palosaari 	dev->regmap[2] = regmap_init_i2c(dev->client[2], &regmap_config);
679877ba50bSAntti Palosaari 	if (IS_ERR(dev->regmap[2])) {
680877ba50bSAntti Palosaari 		ret = PTR_ERR(dev->regmap[2]);
681877ba50bSAntti Palosaari 		goto err_client_2_i2c_unregister_device;
682877ba50bSAntti Palosaari 	}
683877ba50bSAntti Palosaari 	i2c_set_clientdata(dev->client[2], dev);
684877ba50bSAntti Palosaari 
685d930b5b5SAntti Palosaari 	/* Check demod answers with correct chip id */
686d930b5b5SAntti Palosaari 	ret = regmap_read(dev->regmap[2], 0xff, &uitmp);
687d930b5b5SAntti Palosaari 	if (ret)
688d930b5b5SAntti Palosaari 		goto err_regmap_2_regmap_exit;
689d930b5b5SAntti Palosaari 
690d930b5b5SAntti Palosaari 	dev_dbg(&client->dev, "chip id=%02x\n", uitmp);
691d930b5b5SAntti Palosaari 
692d930b5b5SAntti Palosaari 	if (uitmp != 0x03) {
693d930b5b5SAntti Palosaari 		ret = -ENODEV;
694d930b5b5SAntti Palosaari 		goto err_regmap_2_regmap_exit;
695d930b5b5SAntti Palosaari 	}
696d930b5b5SAntti Palosaari 
6977908fad9SAntti Palosaari 	/* Sleep because chip is active by default */
6987908fad9SAntti Palosaari 	ret = regmap_write(dev->regmap[2], 0x05, 0x3e);
6997908fad9SAntti Palosaari 	if (ret)
70037cf9b2dSAntti Palosaari 		goto err_regmap_2_regmap_exit;
7017908fad9SAntti Palosaari 
7027908fad9SAntti Palosaari 	/* Create dvb frontend */
7037908fad9SAntti Palosaari 	memcpy(&dev->frontend.ops, &mn88473_ops, sizeof(dev->frontend.ops));
7047908fad9SAntti Palosaari 	dev->frontend.demodulator_priv = client;
7057908fad9SAntti Palosaari 	*config->fe = &dev->frontend;
706877ba50bSAntti Palosaari 	i2c_set_clientdata(client, dev);
707877ba50bSAntti Palosaari 
7087908fad9SAntti Palosaari 	dev_info(&client->dev, "Panasonic MN88473 successfully identified\n");
7097908fad9SAntti Palosaari 
710877ba50bSAntti Palosaari 	return 0;
71137cf9b2dSAntti Palosaari err_regmap_2_regmap_exit:
71237cf9b2dSAntti Palosaari 	regmap_exit(dev->regmap[2]);
713877ba50bSAntti Palosaari err_client_2_i2c_unregister_device:
714877ba50bSAntti Palosaari 	i2c_unregister_device(dev->client[2]);
715877ba50bSAntti Palosaari err_regmap_1_regmap_exit:
716877ba50bSAntti Palosaari 	regmap_exit(dev->regmap[1]);
717877ba50bSAntti Palosaari err_client_1_i2c_unregister_device:
718877ba50bSAntti Palosaari 	i2c_unregister_device(dev->client[1]);
719877ba50bSAntti Palosaari err_regmap_0_regmap_exit:
720877ba50bSAntti Palosaari 	regmap_exit(dev->regmap[0]);
721877ba50bSAntti Palosaari err_kfree:
722877ba50bSAntti Palosaari 	kfree(dev);
723877ba50bSAntti Palosaari err:
724877ba50bSAntti Palosaari 	dev_dbg(&client->dev, "failed=%d\n", ret);
725877ba50bSAntti Palosaari 	return ret;
726877ba50bSAntti Palosaari }
727877ba50bSAntti Palosaari 
mn88473_remove(struct i2c_client * client)728ed5c2f5fSUwe Kleine-König static void mn88473_remove(struct i2c_client *client)
729877ba50bSAntti Palosaari {
730877ba50bSAntti Palosaari 	struct mn88473_dev *dev = i2c_get_clientdata(client);
731877ba50bSAntti Palosaari 
732877ba50bSAntti Palosaari 	dev_dbg(&client->dev, "\n");
733877ba50bSAntti Palosaari 
734877ba50bSAntti Palosaari 	regmap_exit(dev->regmap[2]);
735877ba50bSAntti Palosaari 	i2c_unregister_device(dev->client[2]);
736877ba50bSAntti Palosaari 
737877ba50bSAntti Palosaari 	regmap_exit(dev->regmap[1]);
738877ba50bSAntti Palosaari 	i2c_unregister_device(dev->client[1]);
739877ba50bSAntti Palosaari 
740877ba50bSAntti Palosaari 	regmap_exit(dev->regmap[0]);
741877ba50bSAntti Palosaari 
742877ba50bSAntti Palosaari 	kfree(dev);
743877ba50bSAntti Palosaari }
744877ba50bSAntti Palosaari 
745877ba50bSAntti Palosaari static const struct i2c_device_id mn88473_id_table[] = {
746877ba50bSAntti Palosaari 	{"mn88473", 0},
747877ba50bSAntti Palosaari 	{}
748877ba50bSAntti Palosaari };
749877ba50bSAntti Palosaari MODULE_DEVICE_TABLE(i2c, mn88473_id_table);
750877ba50bSAntti Palosaari 
751877ba50bSAntti Palosaari static struct i2c_driver mn88473_driver = {
752877ba50bSAntti Palosaari 	.driver = {
753877ba50bSAntti Palosaari 		.name		     = "mn88473",
7547908fad9SAntti Palosaari 		.suppress_bind_attrs = true,
755877ba50bSAntti Palosaari 	},
756*aaeb31c0SUwe Kleine-König 	.probe		= mn88473_probe,
757877ba50bSAntti Palosaari 	.remove		= mn88473_remove,
758877ba50bSAntti Palosaari 	.id_table	= mn88473_id_table,
759877ba50bSAntti Palosaari };
760877ba50bSAntti Palosaari 
761877ba50bSAntti Palosaari module_i2c_driver(mn88473_driver);
762877ba50bSAntti Palosaari 
763877ba50bSAntti Palosaari MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
764877ba50bSAntti Palosaari MODULE_DESCRIPTION("Panasonic MN88473 DVB-T/T2/C demodulator driver");
765877ba50bSAntti Palosaari MODULE_LICENSE("GPL");
766877ba50bSAntti Palosaari MODULE_FIRMWARE(MN88473_FIRMWARE);
767