1*c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
29a0bf528SMauro Carvalho Chehab /*
39a0bf528SMauro Carvalho Chehab  * Realtek RTL2830 DVB-T demodulator driver
49a0bf528SMauro Carvalho Chehab  *
59a0bf528SMauro Carvalho Chehab  * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
69a0bf528SMauro Carvalho Chehab  */
79a0bf528SMauro Carvalho Chehab 
89a0bf528SMauro Carvalho Chehab #include "rtl2830_priv.h"
99a0bf528SMauro Carvalho Chehab 
1015d37f38SAntti Palosaari /* Our regmap is bypassing I2C adapter lock, thus we do it! */
11d858b0e7SMauro Carvalho Chehab static int rtl2830_bulk_write(struct i2c_client *client, unsigned int reg,
1215d37f38SAntti Palosaari 			      const void *val, size_t val_count)
139a0bf528SMauro Carvalho Chehab {
141f153c4dSAntti Palosaari 	struct rtl2830_dev *dev = i2c_get_clientdata(client);
159a0bf528SMauro Carvalho Chehab 	int ret;
169a0bf528SMauro Carvalho Chehab 
17dfecde40SPeter Rosin 	i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT);
1815d37f38SAntti Palosaari 	ret = regmap_bulk_write(dev->regmap, reg, val, val_count);
19dfecde40SPeter Rosin 	i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT);
20fd4cfa8bSAntti Palosaari 	return ret;
219a0bf528SMauro Carvalho Chehab }
229a0bf528SMauro Carvalho Chehab 
23d858b0e7SMauro Carvalho Chehab static int rtl2830_update_bits(struct i2c_client *client, unsigned int reg,
2415d37f38SAntti Palosaari 			       unsigned int mask, unsigned int val)
259a0bf528SMauro Carvalho Chehab {
261f153c4dSAntti Palosaari 	struct rtl2830_dev *dev = i2c_get_clientdata(client);
279a0bf528SMauro Carvalho Chehab 	int ret;
289a0bf528SMauro Carvalho Chehab 
29dfecde40SPeter Rosin 	i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT);
3015d37f38SAntti Palosaari 	ret = regmap_update_bits(dev->regmap, reg, mask, val);
31dfecde40SPeter Rosin 	i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT);
32fd4cfa8bSAntti Palosaari 	return ret;
339a0bf528SMauro Carvalho Chehab }
349a0bf528SMauro Carvalho Chehab 
35d858b0e7SMauro Carvalho Chehab static int rtl2830_bulk_read(struct i2c_client *client, unsigned int reg,
36d858b0e7SMauro Carvalho Chehab 			     void *val, size_t val_count)
379a0bf528SMauro Carvalho Chehab {
3815d37f38SAntti Palosaari 	struct rtl2830_dev *dev = i2c_get_clientdata(client);
399a0bf528SMauro Carvalho Chehab 	int ret;
409a0bf528SMauro Carvalho Chehab 
41dfecde40SPeter Rosin 	i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT);
4215d37f38SAntti Palosaari 	ret = regmap_bulk_read(dev->regmap, reg, val, val_count);
43dfecde40SPeter Rosin 	i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT);
449a0bf528SMauro Carvalho Chehab 	return ret;
459a0bf528SMauro Carvalho Chehab }
469a0bf528SMauro Carvalho Chehab 
479a0bf528SMauro Carvalho Chehab static int rtl2830_init(struct dvb_frontend *fe)
489a0bf528SMauro Carvalho Chehab {
491f153c4dSAntti Palosaari 	struct i2c_client *client = fe->demodulator_priv;
501f153c4dSAntti Palosaari 	struct rtl2830_dev *dev = i2c_get_clientdata(client);
5147b4dbffSAntti Palosaari 	struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache;
529a0bf528SMauro Carvalho Chehab 	int ret, i;
539a0bf528SMauro Carvalho Chehab 	struct rtl2830_reg_val_mask tab[] = {
549a0bf528SMauro Carvalho Chehab 		{0x00d, 0x01, 0x03},
559a0bf528SMauro Carvalho Chehab 		{0x00d, 0x10, 0x10},
569a0bf528SMauro Carvalho Chehab 		{0x104, 0x00, 0x1e},
579a0bf528SMauro Carvalho Chehab 		{0x105, 0x80, 0x80},
589a0bf528SMauro Carvalho Chehab 		{0x110, 0x02, 0x03},
599a0bf528SMauro Carvalho Chehab 		{0x110, 0x08, 0x0c},
609a0bf528SMauro Carvalho Chehab 		{0x17b, 0x00, 0x40},
619a0bf528SMauro Carvalho Chehab 		{0x17d, 0x05, 0x0f},
629a0bf528SMauro Carvalho Chehab 		{0x17d, 0x50, 0xf0},
639a0bf528SMauro Carvalho Chehab 		{0x18c, 0x08, 0x0f},
649a0bf528SMauro Carvalho Chehab 		{0x18d, 0x00, 0xc0},
659a0bf528SMauro Carvalho Chehab 		{0x188, 0x05, 0x0f},
669a0bf528SMauro Carvalho Chehab 		{0x189, 0x00, 0xfc},
679a0bf528SMauro Carvalho Chehab 		{0x2d5, 0x02, 0x02},
689a0bf528SMauro Carvalho Chehab 		{0x2f1, 0x02, 0x06},
699a0bf528SMauro Carvalho Chehab 		{0x2f1, 0x20, 0xf8},
709a0bf528SMauro Carvalho Chehab 		{0x16d, 0x00, 0x01},
719a0bf528SMauro Carvalho Chehab 		{0x1a6, 0x00, 0x80},
72b8cb50d2SAntti Palosaari 		{0x106, dev->pdata->vtop, 0x3f},
73b8cb50d2SAntti Palosaari 		{0x107, dev->pdata->krf, 0x3f},
749a0bf528SMauro Carvalho Chehab 		{0x112, 0x28, 0xff},
75b8cb50d2SAntti Palosaari 		{0x103, dev->pdata->agc_targ_val, 0xff},
769a0bf528SMauro Carvalho Chehab 		{0x00a, 0x02, 0x07},
779a0bf528SMauro Carvalho Chehab 		{0x140, 0x0c, 0x3c},
789a0bf528SMauro Carvalho Chehab 		{0x140, 0x40, 0xc0},
799a0bf528SMauro Carvalho Chehab 		{0x15b, 0x05, 0x07},
809a0bf528SMauro Carvalho Chehab 		{0x15b, 0x28, 0x38},
819a0bf528SMauro Carvalho Chehab 		{0x15c, 0x05, 0x07},
829a0bf528SMauro Carvalho Chehab 		{0x15c, 0x28, 0x38},
83b8cb50d2SAntti Palosaari 		{0x115, dev->pdata->spec_inv, 0x01},
849a0bf528SMauro Carvalho Chehab 		{0x16f, 0x01, 0x07},
859a0bf528SMauro Carvalho Chehab 		{0x170, 0x18, 0x38},
869a0bf528SMauro Carvalho Chehab 		{0x172, 0x0f, 0x0f},
879a0bf528SMauro Carvalho Chehab 		{0x173, 0x08, 0x38},
889a0bf528SMauro Carvalho Chehab 		{0x175, 0x01, 0x07},
899a0bf528SMauro Carvalho Chehab 		{0x176, 0x00, 0xc0},
909a0bf528SMauro Carvalho Chehab 	};
919a0bf528SMauro Carvalho Chehab 
929a0bf528SMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(tab); i++) {
9315d37f38SAntti Palosaari 		ret = rtl2830_update_bits(client, tab[i].reg, tab[i].mask,
9415d37f38SAntti Palosaari 					  tab[i].val);
959a0bf528SMauro Carvalho Chehab 		if (ret)
969a0bf528SMauro Carvalho Chehab 			goto err;
979a0bf528SMauro Carvalho Chehab 	}
989a0bf528SMauro Carvalho Chehab 
9915d37f38SAntti Palosaari 	ret = rtl2830_bulk_write(client, 0x18f, "\x28\x00", 2);
1009a0bf528SMauro Carvalho Chehab 	if (ret)
1019a0bf528SMauro Carvalho Chehab 		goto err;
1029a0bf528SMauro Carvalho Chehab 
10315d37f38SAntti Palosaari 	ret = rtl2830_bulk_write(client, 0x195,
1049a0bf528SMauro Carvalho Chehab 				 "\x04\x06\x0a\x12\x0a\x12\x1e\x28", 8);
1059a0bf528SMauro Carvalho Chehab 	if (ret)
1069a0bf528SMauro Carvalho Chehab 		goto err;
1079a0bf528SMauro Carvalho Chehab 
1089a0bf528SMauro Carvalho Chehab 	/* TODO: spec init */
1099a0bf528SMauro Carvalho Chehab 
1109a0bf528SMauro Carvalho Chehab 	/* soft reset */
11115d37f38SAntti Palosaari 	ret = rtl2830_update_bits(client, 0x101, 0x04, 0x04);
1129a0bf528SMauro Carvalho Chehab 	if (ret)
1139a0bf528SMauro Carvalho Chehab 		goto err;
1149a0bf528SMauro Carvalho Chehab 
11515d37f38SAntti Palosaari 	ret = rtl2830_update_bits(client, 0x101, 0x04, 0x00);
1169a0bf528SMauro Carvalho Chehab 	if (ret)
1179a0bf528SMauro Carvalho Chehab 		goto err;
1189a0bf528SMauro Carvalho Chehab 
11947b4dbffSAntti Palosaari 	/* init stats here in order signal app which stats are supported */
120871f7025SAntti Palosaari 	c->strength.len = 1;
121871f7025SAntti Palosaari 	c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
12247b4dbffSAntti Palosaari 	c->cnr.len = 1;
12347b4dbffSAntti Palosaari 	c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
1245bb11ca5SAntti Palosaari 	c->post_bit_error.len = 1;
1255bb11ca5SAntti Palosaari 	c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
1265bb11ca5SAntti Palosaari 	c->post_bit_count.len = 1;
1275bb11ca5SAntti Palosaari 	c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
12847b4dbffSAntti Palosaari 
129f544f100SAntti Palosaari 	dev->sleeping = false;
1309a0bf528SMauro Carvalho Chehab 
1319a0bf528SMauro Carvalho Chehab 	return ret;
1329a0bf528SMauro Carvalho Chehab err:
1337cc39328SAntti Palosaari 	dev_dbg(&client->dev, "failed=%d\n", ret);
1349a0bf528SMauro Carvalho Chehab 	return ret;
1359a0bf528SMauro Carvalho Chehab }
1369a0bf528SMauro Carvalho Chehab 
1379a0bf528SMauro Carvalho Chehab static int rtl2830_sleep(struct dvb_frontend *fe)
1389a0bf528SMauro Carvalho Chehab {
1391f153c4dSAntti Palosaari 	struct i2c_client *client = fe->demodulator_priv;
1401f153c4dSAntti Palosaari 	struct rtl2830_dev *dev = i2c_get_clientdata(client);
141947debb4SAntti Palosaari 
142f544f100SAntti Palosaari 	dev->sleeping = true;
14347b4dbffSAntti Palosaari 	dev->fe_status = 0;
144947debb4SAntti Palosaari 
1459a0bf528SMauro Carvalho Chehab 	return 0;
1469a0bf528SMauro Carvalho Chehab }
1479a0bf528SMauro Carvalho Chehab 
148a17ff2eeSMauro Carvalho Chehab static int rtl2830_get_tune_settings(struct dvb_frontend *fe,
1499a0bf528SMauro Carvalho Chehab 				     struct dvb_frontend_tune_settings *s)
1509a0bf528SMauro Carvalho Chehab {
1519a0bf528SMauro Carvalho Chehab 	s->min_delay_ms = 500;
152f1b1eabfSMauro Carvalho Chehab 	s->step_size = fe->ops.info.frequency_stepsize_hz * 2;
153f1b1eabfSMauro Carvalho Chehab 	s->max_drift = (fe->ops.info.frequency_stepsize_hz * 2) + 1;
1549a0bf528SMauro Carvalho Chehab 
1559a0bf528SMauro Carvalho Chehab 	return 0;
1569a0bf528SMauro Carvalho Chehab }
1579a0bf528SMauro Carvalho Chehab 
1589a0bf528SMauro Carvalho Chehab static int rtl2830_set_frontend(struct dvb_frontend *fe)
1599a0bf528SMauro Carvalho Chehab {
1601f153c4dSAntti Palosaari 	struct i2c_client *client = fe->demodulator_priv;
1611f153c4dSAntti Palosaari 	struct rtl2830_dev *dev = i2c_get_clientdata(client);
1629a0bf528SMauro Carvalho Chehab 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
1639a0bf528SMauro Carvalho Chehab 	int ret, i;
16466b3c4deSAntti Palosaari 	u64 num;
16515d37f38SAntti Palosaari 	u8 buf[3], u8tmp;
16666b3c4deSAntti Palosaari 	u32 if_ctl, if_frequency;
1673a2fca26SAntti Palosaari 	static const u8 bw_params1[3][34] = {
1689a0bf528SMauro Carvalho Chehab 		{
1699a0bf528SMauro Carvalho Chehab 		0x1f, 0xf0, 0x1f, 0xf0, 0x1f, 0xfa, 0x00, 0x17, 0x00, 0x41,
1709a0bf528SMauro Carvalho Chehab 		0x00, 0x64, 0x00, 0x67, 0x00, 0x38, 0x1f, 0xde, 0x1f, 0x7a,
1719a0bf528SMauro Carvalho Chehab 		0x1f, 0x47, 0x1f, 0x7c, 0x00, 0x30, 0x01, 0x4b, 0x02, 0x82,
1729a0bf528SMauro Carvalho Chehab 		0x03, 0x73, 0x03, 0xcf, /* 6 MHz */
1739a0bf528SMauro Carvalho Chehab 		}, {
1749a0bf528SMauro Carvalho Chehab 		0x1f, 0xfa, 0x1f, 0xda, 0x1f, 0xc1, 0x1f, 0xb3, 0x1f, 0xca,
1759a0bf528SMauro Carvalho Chehab 		0x00, 0x07, 0x00, 0x4d, 0x00, 0x6d, 0x00, 0x40, 0x1f, 0xca,
1769a0bf528SMauro Carvalho Chehab 		0x1f, 0x4d, 0x1f, 0x2a, 0x1f, 0xb2, 0x00, 0xec, 0x02, 0x7e,
1779a0bf528SMauro Carvalho Chehab 		0x03, 0xd0, 0x04, 0x53, /* 7 MHz */
1789a0bf528SMauro Carvalho Chehab 		}, {
1799a0bf528SMauro Carvalho Chehab 		0x00, 0x10, 0x00, 0x0e, 0x1f, 0xf7, 0x1f, 0xc9, 0x1f, 0xa0,
1809a0bf528SMauro Carvalho Chehab 		0x1f, 0xa6, 0x1f, 0xec, 0x00, 0x4e, 0x00, 0x7d, 0x00, 0x3a,
1819a0bf528SMauro Carvalho Chehab 		0x1f, 0x98, 0x1f, 0x10, 0x1f, 0x40, 0x00, 0x75, 0x02, 0x5f,
1829a0bf528SMauro Carvalho Chehab 		0x04, 0x24, 0x04, 0xdb, /* 8 MHz */
1839a0bf528SMauro Carvalho Chehab 		},
1849a0bf528SMauro Carvalho Chehab 	};
1853a2fca26SAntti Palosaari 	static const u8 bw_params2[3][6] = {
1863a2fca26SAntti Palosaari 		{0xc3, 0x0c, 0x44, 0x33, 0x33, 0x30}, /* 6 MHz */
1873a2fca26SAntti Palosaari 		{0xb8, 0xe3, 0x93, 0x99, 0x99, 0x98}, /* 7 MHz */
1883a2fca26SAntti Palosaari 		{0xae, 0xba, 0xf3, 0x26, 0x66, 0x64}, /* 8 MHz */
1899a0bf528SMauro Carvalho Chehab 	};
1909a0bf528SMauro Carvalho Chehab 
1917cc39328SAntti Palosaari 	dev_dbg(&client->dev, "frequency=%u bandwidth_hz=%u inversion=%u\n",
1927cc39328SAntti Palosaari 		c->frequency, c->bandwidth_hz, c->inversion);
1939a0bf528SMauro Carvalho Chehab 
1949a0bf528SMauro Carvalho Chehab 	/* program tuner */
1959a0bf528SMauro Carvalho Chehab 	if (fe->ops.tuner_ops.set_params)
1969a0bf528SMauro Carvalho Chehab 		fe->ops.tuner_ops.set_params(fe);
1979a0bf528SMauro Carvalho Chehab 
1989a0bf528SMauro Carvalho Chehab 	switch (c->bandwidth_hz) {
1999a0bf528SMauro Carvalho Chehab 	case 6000000:
2009a0bf528SMauro Carvalho Chehab 		i = 0;
2019a0bf528SMauro Carvalho Chehab 		break;
2029a0bf528SMauro Carvalho Chehab 	case 7000000:
2039a0bf528SMauro Carvalho Chehab 		i = 1;
2049a0bf528SMauro Carvalho Chehab 		break;
2059a0bf528SMauro Carvalho Chehab 	case 8000000:
2069a0bf528SMauro Carvalho Chehab 		i = 2;
2079a0bf528SMauro Carvalho Chehab 		break;
2089a0bf528SMauro Carvalho Chehab 	default:
2097cc39328SAntti Palosaari 		dev_err(&client->dev, "invalid bandwidth_hz %u\n",
2107cc39328SAntti Palosaari 			c->bandwidth_hz);
2119a0bf528SMauro Carvalho Chehab 		return -EINVAL;
2129a0bf528SMauro Carvalho Chehab 	}
2139a0bf528SMauro Carvalho Chehab 
21415d37f38SAntti Palosaari 	ret = rtl2830_update_bits(client, 0x008, 0x06, i << 1);
2159a0bf528SMauro Carvalho Chehab 	if (ret)
2169a0bf528SMauro Carvalho Chehab 		goto err;
2179a0bf528SMauro Carvalho Chehab 
21866b3c4deSAntti Palosaari 	/* program if frequency */
21966b3c4deSAntti Palosaari 	if (fe->ops.tuner_ops.get_if_frequency)
22066b3c4deSAntti Palosaari 		ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency);
22166b3c4deSAntti Palosaari 	else
22266b3c4deSAntti Palosaari 		ret = -EINVAL;
223947debb4SAntti Palosaari 	if (ret)
22466b3c4deSAntti Palosaari 		goto err;
22566b3c4deSAntti Palosaari 
226b8cb50d2SAntti Palosaari 	num = if_frequency % dev->pdata->clk;
22766b3c4deSAntti Palosaari 	num *= 0x400000;
228b8cb50d2SAntti Palosaari 	num = div_u64(num, dev->pdata->clk);
22966b3c4deSAntti Palosaari 	num = -num;
23066b3c4deSAntti Palosaari 	if_ctl = num & 0x3fffff;
2317cc39328SAntti Palosaari 	dev_dbg(&client->dev, "if_frequency=%d if_ctl=%08x\n",
2327cc39328SAntti Palosaari 		if_frequency, if_ctl);
23366b3c4deSAntti Palosaari 
23415d37f38SAntti Palosaari 	buf[0] = (if_ctl >> 16) & 0x3f;
23566b3c4deSAntti Palosaari 	buf[1] = (if_ctl >>  8) & 0xff;
23666b3c4deSAntti Palosaari 	buf[2] = (if_ctl >>  0) & 0xff;
23766b3c4deSAntti Palosaari 
23815d37f38SAntti Palosaari 	ret = rtl2830_bulk_read(client, 0x119, &u8tmp, 1);
23915d37f38SAntti Palosaari 	if (ret)
24015d37f38SAntti Palosaari 		goto err;
24115d37f38SAntti Palosaari 
24215d37f38SAntti Palosaari 	buf[0] |= u8tmp & 0xc0;  /* [7:6] */
24315d37f38SAntti Palosaari 
24415d37f38SAntti Palosaari 	ret = rtl2830_bulk_write(client, 0x119, buf, 3);
24566b3c4deSAntti Palosaari 	if (ret)
24666b3c4deSAntti Palosaari 		goto err;
24766b3c4deSAntti Palosaari 
2489a0bf528SMauro Carvalho Chehab 	/* 1/2 split I2C write */
24915d37f38SAntti Palosaari 	ret = rtl2830_bulk_write(client, 0x11c, &bw_params1[i][0], 17);
2509a0bf528SMauro Carvalho Chehab 	if (ret)
2519a0bf528SMauro Carvalho Chehab 		goto err;
2529a0bf528SMauro Carvalho Chehab 
2539a0bf528SMauro Carvalho Chehab 	/* 2/2 split I2C write */
25415d37f38SAntti Palosaari 	ret = rtl2830_bulk_write(client, 0x12d, &bw_params1[i][17], 17);
2559a0bf528SMauro Carvalho Chehab 	if (ret)
2569a0bf528SMauro Carvalho Chehab 		goto err;
2579a0bf528SMauro Carvalho Chehab 
25815d37f38SAntti Palosaari 	ret = rtl2830_bulk_write(client, 0x19d, bw_params2[i], 6);
2599a0bf528SMauro Carvalho Chehab 	if (ret)
2609a0bf528SMauro Carvalho Chehab 		goto err;
2619a0bf528SMauro Carvalho Chehab 
2629a0bf528SMauro Carvalho Chehab 	return ret;
2639a0bf528SMauro Carvalho Chehab err:
2647cc39328SAntti Palosaari 	dev_dbg(&client->dev, "failed=%d\n", ret);
2659a0bf528SMauro Carvalho Chehab 	return ret;
2669a0bf528SMauro Carvalho Chehab }
2679a0bf528SMauro Carvalho Chehab 
2687e3e68bcSMauro Carvalho Chehab static int rtl2830_get_frontend(struct dvb_frontend *fe,
2697e3e68bcSMauro Carvalho Chehab 				struct dtv_frontend_properties *c)
2709a0bf528SMauro Carvalho Chehab {
2711f153c4dSAntti Palosaari 	struct i2c_client *client = fe->demodulator_priv;
2721f153c4dSAntti Palosaari 	struct rtl2830_dev *dev = i2c_get_clientdata(client);
2739a0bf528SMauro Carvalho Chehab 	int ret;
2749a0bf528SMauro Carvalho Chehab 	u8 buf[3];
2759a0bf528SMauro Carvalho Chehab 
276f544f100SAntti Palosaari 	if (dev->sleeping)
2779a0bf528SMauro Carvalho Chehab 		return 0;
2789a0bf528SMauro Carvalho Chehab 
27915d37f38SAntti Palosaari 	ret = rtl2830_bulk_read(client, 0x33c, buf, 2);
2809a0bf528SMauro Carvalho Chehab 	if (ret)
2819a0bf528SMauro Carvalho Chehab 		goto err;
2829a0bf528SMauro Carvalho Chehab 
28315d37f38SAntti Palosaari 	ret = rtl2830_bulk_read(client, 0x351, &buf[2], 1);
2849a0bf528SMauro Carvalho Chehab 	if (ret)
2859a0bf528SMauro Carvalho Chehab 		goto err;
2869a0bf528SMauro Carvalho Chehab 
2877cc39328SAntti Palosaari 	dev_dbg(&client->dev, "TPS=%*ph\n", 3, buf);
2889a0bf528SMauro Carvalho Chehab 
2899a0bf528SMauro Carvalho Chehab 	switch ((buf[0] >> 2) & 3) {
2909a0bf528SMauro Carvalho Chehab 	case 0:
2919a0bf528SMauro Carvalho Chehab 		c->modulation = QPSK;
2929a0bf528SMauro Carvalho Chehab 		break;
2939a0bf528SMauro Carvalho Chehab 	case 1:
2949a0bf528SMauro Carvalho Chehab 		c->modulation = QAM_16;
2959a0bf528SMauro Carvalho Chehab 		break;
2969a0bf528SMauro Carvalho Chehab 	case 2:
2979a0bf528SMauro Carvalho Chehab 		c->modulation = QAM_64;
2989a0bf528SMauro Carvalho Chehab 		break;
2999a0bf528SMauro Carvalho Chehab 	}
3009a0bf528SMauro Carvalho Chehab 
3019a0bf528SMauro Carvalho Chehab 	switch ((buf[2] >> 2) & 1) {
3029a0bf528SMauro Carvalho Chehab 	case 0:
3039a0bf528SMauro Carvalho Chehab 		c->transmission_mode = TRANSMISSION_MODE_2K;
3049a0bf528SMauro Carvalho Chehab 		break;
3059a0bf528SMauro Carvalho Chehab 	case 1:
3069a0bf528SMauro Carvalho Chehab 		c->transmission_mode = TRANSMISSION_MODE_8K;
3079a0bf528SMauro Carvalho Chehab 	}
3089a0bf528SMauro Carvalho Chehab 
3099a0bf528SMauro Carvalho Chehab 	switch ((buf[2] >> 0) & 3) {
3109a0bf528SMauro Carvalho Chehab 	case 0:
3119a0bf528SMauro Carvalho Chehab 		c->guard_interval = GUARD_INTERVAL_1_32;
3129a0bf528SMauro Carvalho Chehab 		break;
3139a0bf528SMauro Carvalho Chehab 	case 1:
3149a0bf528SMauro Carvalho Chehab 		c->guard_interval = GUARD_INTERVAL_1_16;
3159a0bf528SMauro Carvalho Chehab 		break;
3169a0bf528SMauro Carvalho Chehab 	case 2:
3179a0bf528SMauro Carvalho Chehab 		c->guard_interval = GUARD_INTERVAL_1_8;
3189a0bf528SMauro Carvalho Chehab 		break;
3199a0bf528SMauro Carvalho Chehab 	case 3:
3209a0bf528SMauro Carvalho Chehab 		c->guard_interval = GUARD_INTERVAL_1_4;
3219a0bf528SMauro Carvalho Chehab 		break;
3229a0bf528SMauro Carvalho Chehab 	}
3239a0bf528SMauro Carvalho Chehab 
3249a0bf528SMauro Carvalho Chehab 	switch ((buf[0] >> 4) & 7) {
3259a0bf528SMauro Carvalho Chehab 	case 0:
3269a0bf528SMauro Carvalho Chehab 		c->hierarchy = HIERARCHY_NONE;
3279a0bf528SMauro Carvalho Chehab 		break;
3289a0bf528SMauro Carvalho Chehab 	case 1:
3299a0bf528SMauro Carvalho Chehab 		c->hierarchy = HIERARCHY_1;
3309a0bf528SMauro Carvalho Chehab 		break;
3319a0bf528SMauro Carvalho Chehab 	case 2:
3329a0bf528SMauro Carvalho Chehab 		c->hierarchy = HIERARCHY_2;
3339a0bf528SMauro Carvalho Chehab 		break;
3349a0bf528SMauro Carvalho Chehab 	case 3:
3359a0bf528SMauro Carvalho Chehab 		c->hierarchy = HIERARCHY_4;
3369a0bf528SMauro Carvalho Chehab 		break;
3379a0bf528SMauro Carvalho Chehab 	}
3389a0bf528SMauro Carvalho Chehab 
3399a0bf528SMauro Carvalho Chehab 	switch ((buf[1] >> 3) & 7) {
3409a0bf528SMauro Carvalho Chehab 	case 0:
3419a0bf528SMauro Carvalho Chehab 		c->code_rate_HP = FEC_1_2;
3429a0bf528SMauro Carvalho Chehab 		break;
3439a0bf528SMauro Carvalho Chehab 	case 1:
3449a0bf528SMauro Carvalho Chehab 		c->code_rate_HP = FEC_2_3;
3459a0bf528SMauro Carvalho Chehab 		break;
3469a0bf528SMauro Carvalho Chehab 	case 2:
3479a0bf528SMauro Carvalho Chehab 		c->code_rate_HP = FEC_3_4;
3489a0bf528SMauro Carvalho Chehab 		break;
3499a0bf528SMauro Carvalho Chehab 	case 3:
3509a0bf528SMauro Carvalho Chehab 		c->code_rate_HP = FEC_5_6;
3519a0bf528SMauro Carvalho Chehab 		break;
3529a0bf528SMauro Carvalho Chehab 	case 4:
3539a0bf528SMauro Carvalho Chehab 		c->code_rate_HP = FEC_7_8;
3549a0bf528SMauro Carvalho Chehab 		break;
3559a0bf528SMauro Carvalho Chehab 	}
3569a0bf528SMauro Carvalho Chehab 
3579a0bf528SMauro Carvalho Chehab 	switch ((buf[1] >> 0) & 7) {
3589a0bf528SMauro Carvalho Chehab 	case 0:
3599a0bf528SMauro Carvalho Chehab 		c->code_rate_LP = FEC_1_2;
3609a0bf528SMauro Carvalho Chehab 		break;
3619a0bf528SMauro Carvalho Chehab 	case 1:
3629a0bf528SMauro Carvalho Chehab 		c->code_rate_LP = FEC_2_3;
3639a0bf528SMauro Carvalho Chehab 		break;
3649a0bf528SMauro Carvalho Chehab 	case 2:
3659a0bf528SMauro Carvalho Chehab 		c->code_rate_LP = FEC_3_4;
3669a0bf528SMauro Carvalho Chehab 		break;
3679a0bf528SMauro Carvalho Chehab 	case 3:
3689a0bf528SMauro Carvalho Chehab 		c->code_rate_LP = FEC_5_6;
3699a0bf528SMauro Carvalho Chehab 		break;
3709a0bf528SMauro Carvalho Chehab 	case 4:
3719a0bf528SMauro Carvalho Chehab 		c->code_rate_LP = FEC_7_8;
3729a0bf528SMauro Carvalho Chehab 		break;
3739a0bf528SMauro Carvalho Chehab 	}
3749a0bf528SMauro Carvalho Chehab 
3759a0bf528SMauro Carvalho Chehab 	return 0;
3769a0bf528SMauro Carvalho Chehab err:
3777cc39328SAntti Palosaari 	dev_dbg(&client->dev, "failed=%d\n", ret);
3789a0bf528SMauro Carvalho Chehab 	return ret;
3799a0bf528SMauro Carvalho Chehab }
3809a0bf528SMauro Carvalho Chehab 
3810df289a2SMauro Carvalho Chehab static int rtl2830_read_status(struct dvb_frontend *fe, enum fe_status *status)
3829a0bf528SMauro Carvalho Chehab {
3831f153c4dSAntti Palosaari 	struct i2c_client *client = fe->demodulator_priv;
384b8cb50d2SAntti Palosaari 	struct rtl2830_dev *dev = i2c_get_clientdata(client);
3854a7e445bSAntti Palosaari 	struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache;
3864a7e445bSAntti Palosaari 	int ret, stmp;
3874a7e445bSAntti Palosaari 	unsigned int utmp;
3884a7e445bSAntti Palosaari 	u8 u8tmp, buf[2];
389947debb4SAntti Palosaari 
3909a0bf528SMauro Carvalho Chehab 	*status = 0;
3919a0bf528SMauro Carvalho Chehab 
392f544f100SAntti Palosaari 	if (dev->sleeping)
3939a0bf528SMauro Carvalho Chehab 		return 0;
3949a0bf528SMauro Carvalho Chehab 
39515d37f38SAntti Palosaari 	ret = rtl2830_bulk_read(client, 0x351, &u8tmp, 1);
3969a0bf528SMauro Carvalho Chehab 	if (ret)
3979a0bf528SMauro Carvalho Chehab 		goto err;
3989a0bf528SMauro Carvalho Chehab 
39915d37f38SAntti Palosaari 	u8tmp = (u8tmp >> 3) & 0x0f; /* [6:3] */
40015d37f38SAntti Palosaari 	if (u8tmp == 11) {
4019a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
4029a0bf528SMauro Carvalho Chehab 			FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
40315d37f38SAntti Palosaari 	} else if (u8tmp == 10) {
4049a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
4059a0bf528SMauro Carvalho Chehab 			FE_HAS_VITERBI;
4069a0bf528SMauro Carvalho Chehab 	}
4079a0bf528SMauro Carvalho Chehab 
40847b4dbffSAntti Palosaari 	dev->fe_status = *status;
40947b4dbffSAntti Palosaari 
4104a7e445bSAntti Palosaari 	/* Signal strength */
4114a7e445bSAntti Palosaari 	if (dev->fe_status & FE_HAS_SIGNAL) {
4124a7e445bSAntti Palosaari 		/* Read IF AGC */
4134a7e445bSAntti Palosaari 		ret = rtl2830_bulk_read(client, 0x359, buf, 2);
4144a7e445bSAntti Palosaari 		if (ret)
4154a7e445bSAntti Palosaari 			goto err;
4164a7e445bSAntti Palosaari 
4174a7e445bSAntti Palosaari 		stmp = buf[0] << 8 | buf[1] << 0;
4184a7e445bSAntti Palosaari 		stmp = sign_extend32(stmp, 13);
4194a7e445bSAntti Palosaari 		utmp = clamp_val(-4 * stmp + 32767, 0x0000, 0xffff);
4204a7e445bSAntti Palosaari 
4214a7e445bSAntti Palosaari 		dev_dbg(&client->dev, "IF AGC=%d\n", stmp);
4224a7e445bSAntti Palosaari 
4234a7e445bSAntti Palosaari 		c->strength.stat[0].scale = FE_SCALE_RELATIVE;
4244a7e445bSAntti Palosaari 		c->strength.stat[0].uvalue = utmp;
4254a7e445bSAntti Palosaari 	} else {
4264a7e445bSAntti Palosaari 		c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
4274a7e445bSAntti Palosaari 	}
4284a7e445bSAntti Palosaari 
4294a7e445bSAntti Palosaari 	/* CNR */
4304a7e445bSAntti Palosaari 	if (dev->fe_status & FE_HAS_VITERBI) {
4314a7e445bSAntti Palosaari 		unsigned int hierarchy, constellation;
4324a7e445bSAntti Palosaari 		#define CONSTELLATION_NUM 3
4334a7e445bSAntti Palosaari 		#define HIERARCHY_NUM 4
4344a7e445bSAntti Palosaari 		static const u32 constant[CONSTELLATION_NUM][HIERARCHY_NUM] = {
4354a7e445bSAntti Palosaari 			{70705899, 70705899, 70705899, 70705899},
4364a7e445bSAntti Palosaari 			{82433173, 82433173, 87483115, 94445660},
4374a7e445bSAntti Palosaari 			{92888734, 92888734, 95487525, 99770748},
4384a7e445bSAntti Palosaari 		};
4394a7e445bSAntti Palosaari 
4404a7e445bSAntti Palosaari 		ret = rtl2830_bulk_read(client, 0x33c, &u8tmp, 1);
4414a7e445bSAntti Palosaari 		if (ret)
4424a7e445bSAntti Palosaari 			goto err;
4434a7e445bSAntti Palosaari 
4444a7e445bSAntti Palosaari 		constellation = (u8tmp >> 2) & 0x03; /* [3:2] */
4454a7e445bSAntti Palosaari 		if (constellation > CONSTELLATION_NUM - 1)
4464a7e445bSAntti Palosaari 			goto err;
4474a7e445bSAntti Palosaari 
4484a7e445bSAntti Palosaari 		hierarchy = (u8tmp >> 4) & 0x07; /* [6:4] */
4494a7e445bSAntti Palosaari 		if (hierarchy > HIERARCHY_NUM - 1)
4504a7e445bSAntti Palosaari 			goto err;
4514a7e445bSAntti Palosaari 
4524a7e445bSAntti Palosaari 		ret = rtl2830_bulk_read(client, 0x40c, buf, 2);
4534a7e445bSAntti Palosaari 		if (ret)
4544a7e445bSAntti Palosaari 			goto err;
4554a7e445bSAntti Palosaari 
4564a7e445bSAntti Palosaari 		utmp = buf[0] << 8 | buf[1] << 0;
4574a7e445bSAntti Palosaari 		if (utmp)
4584a7e445bSAntti Palosaari 			stmp = (constant[constellation][hierarchy] -
4594a7e445bSAntti Palosaari 			       intlog10(utmp)) / ((1 << 24) / 10000);
4604a7e445bSAntti Palosaari 		else
4614a7e445bSAntti Palosaari 			stmp = 0;
4624a7e445bSAntti Palosaari 
4634a7e445bSAntti Palosaari 		dev_dbg(&client->dev, "CNR raw=%u\n", utmp);
4644a7e445bSAntti Palosaari 
4654a7e445bSAntti Palosaari 		c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
4664a7e445bSAntti Palosaari 		c->cnr.stat[0].svalue = stmp;
4674a7e445bSAntti Palosaari 	} else {
4684a7e445bSAntti Palosaari 		c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
4694a7e445bSAntti Palosaari 	}
4704a7e445bSAntti Palosaari 
4714a7e445bSAntti Palosaari 	/* BER */
4724a7e445bSAntti Palosaari 	if (dev->fe_status & FE_HAS_LOCK) {
4734a7e445bSAntti Palosaari 		ret = rtl2830_bulk_read(client, 0x34e, buf, 2);
4744a7e445bSAntti Palosaari 		if (ret)
4754a7e445bSAntti Palosaari 			goto err;
4764a7e445bSAntti Palosaari 
4774a7e445bSAntti Palosaari 		utmp = buf[0] << 8 | buf[1] << 0;
4784a7e445bSAntti Palosaari 		dev->post_bit_error += utmp;
4794a7e445bSAntti Palosaari 		dev->post_bit_count += 1000000;
4804a7e445bSAntti Palosaari 
4814a7e445bSAntti Palosaari 		dev_dbg(&client->dev, "BER errors=%u total=1000000\n", utmp);
4824a7e445bSAntti Palosaari 
4834a7e445bSAntti Palosaari 		c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
4844a7e445bSAntti Palosaari 		c->post_bit_error.stat[0].uvalue = dev->post_bit_error;
4854a7e445bSAntti Palosaari 		c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
4864a7e445bSAntti Palosaari 		c->post_bit_count.stat[0].uvalue = dev->post_bit_count;
4874a7e445bSAntti Palosaari 	} else {
4884a7e445bSAntti Palosaari 		c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
4894a7e445bSAntti Palosaari 		c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
4904a7e445bSAntti Palosaari 	}
4914a7e445bSAntti Palosaari 
4924a7e445bSAntti Palosaari 
4939a0bf528SMauro Carvalho Chehab 	return ret;
4949a0bf528SMauro Carvalho Chehab err:
4957cc39328SAntti Palosaari 	dev_dbg(&client->dev, "failed=%d\n", ret);
4969a0bf528SMauro Carvalho Chehab 	return ret;
4979a0bf528SMauro Carvalho Chehab }
4989a0bf528SMauro Carvalho Chehab 
4999a0bf528SMauro Carvalho Chehab static int rtl2830_read_snr(struct dvb_frontend *fe, u16 *snr)
5009a0bf528SMauro Carvalho Chehab {
5016dcfe3ccSAntti Palosaari 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
5029a0bf528SMauro Carvalho Chehab 
5036dcfe3ccSAntti Palosaari 	if (c->cnr.stat[0].scale == FE_SCALE_DECIBEL)
5046dcfe3ccSAntti Palosaari 		*snr = div_s64(c->cnr.stat[0].svalue, 100);
5059a0bf528SMauro Carvalho Chehab 	else
5069a0bf528SMauro Carvalho Chehab 		*snr = 0;
5079a0bf528SMauro Carvalho Chehab 
5089a0bf528SMauro Carvalho Chehab 	return 0;
5099a0bf528SMauro Carvalho Chehab }
5109a0bf528SMauro Carvalho Chehab 
5119a0bf528SMauro Carvalho Chehab static int rtl2830_read_ber(struct dvb_frontend *fe, u32 *ber)
5129a0bf528SMauro Carvalho Chehab {
5131f153c4dSAntti Palosaari 	struct i2c_client *client = fe->demodulator_priv;
5141f153c4dSAntti Palosaari 	struct rtl2830_dev *dev = i2c_get_clientdata(client);
5159a0bf528SMauro Carvalho Chehab 
516f491391cSAntti Palosaari 	*ber = (dev->post_bit_error - dev->post_bit_error_prev);
517f491391cSAntti Palosaari 	dev->post_bit_error_prev = dev->post_bit_error;
5189a0bf528SMauro Carvalho Chehab 
5199a0bf528SMauro Carvalho Chehab 	return 0;
5209a0bf528SMauro Carvalho Chehab }
5219a0bf528SMauro Carvalho Chehab 
5229a0bf528SMauro Carvalho Chehab static int rtl2830_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
5239a0bf528SMauro Carvalho Chehab {
5249a0bf528SMauro Carvalho Chehab 	*ucblocks = 0;
525947debb4SAntti Palosaari 
5269a0bf528SMauro Carvalho Chehab 	return 0;
5279a0bf528SMauro Carvalho Chehab }
5289a0bf528SMauro Carvalho Chehab 
5299a0bf528SMauro Carvalho Chehab static int rtl2830_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
5309a0bf528SMauro Carvalho Chehab {
531d512e286SAntti Palosaari 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
5329a0bf528SMauro Carvalho Chehab 
533d512e286SAntti Palosaari 	if (c->strength.stat[0].scale == FE_SCALE_RELATIVE)
534d512e286SAntti Palosaari 		*strength = c->strength.stat[0].uvalue;
5359a0bf528SMauro Carvalho Chehab 	else
536d512e286SAntti Palosaari 		*strength = 0;
5379a0bf528SMauro Carvalho Chehab 
5389a0bf528SMauro Carvalho Chehab 	return 0;
5399a0bf528SMauro Carvalho Chehab }
5409a0bf528SMauro Carvalho Chehab 
541bd336e63SMax Kellermann static const struct dvb_frontend_ops rtl2830_ops = {
5429a0bf528SMauro Carvalho Chehab 	.delsys = {SYS_DVBT},
5439a0bf528SMauro Carvalho Chehab 	.info = {
5449a0bf528SMauro Carvalho Chehab 		.name = "Realtek RTL2830 (DVB-T)",
5459a0bf528SMauro Carvalho Chehab 		.caps = FE_CAN_FEC_1_2 |
5469a0bf528SMauro Carvalho Chehab 			FE_CAN_FEC_2_3 |
5479a0bf528SMauro Carvalho Chehab 			FE_CAN_FEC_3_4 |
5489a0bf528SMauro Carvalho Chehab 			FE_CAN_FEC_5_6 |
5499a0bf528SMauro Carvalho Chehab 			FE_CAN_FEC_7_8 |
5509a0bf528SMauro Carvalho Chehab 			FE_CAN_FEC_AUTO |
5519a0bf528SMauro Carvalho Chehab 			FE_CAN_QPSK |
5529a0bf528SMauro Carvalho Chehab 			FE_CAN_QAM_16 |
5539a0bf528SMauro Carvalho Chehab 			FE_CAN_QAM_64 |
5549a0bf528SMauro Carvalho Chehab 			FE_CAN_QAM_AUTO |
5559a0bf528SMauro Carvalho Chehab 			FE_CAN_TRANSMISSION_MODE_AUTO |
5569a0bf528SMauro Carvalho Chehab 			FE_CAN_GUARD_INTERVAL_AUTO |
5579a0bf528SMauro Carvalho Chehab 			FE_CAN_HIERARCHY_AUTO |
5589a0bf528SMauro Carvalho Chehab 			FE_CAN_RECOVER |
5599a0bf528SMauro Carvalho Chehab 			FE_CAN_MUTE_TS
5609a0bf528SMauro Carvalho Chehab 	},
5619a0bf528SMauro Carvalho Chehab 
5629a0bf528SMauro Carvalho Chehab 	.init = rtl2830_init,
5639a0bf528SMauro Carvalho Chehab 	.sleep = rtl2830_sleep,
5649a0bf528SMauro Carvalho Chehab 
5659a0bf528SMauro Carvalho Chehab 	.get_tune_settings = rtl2830_get_tune_settings,
5669a0bf528SMauro Carvalho Chehab 
5679a0bf528SMauro Carvalho Chehab 	.set_frontend = rtl2830_set_frontend,
5689a0bf528SMauro Carvalho Chehab 	.get_frontend = rtl2830_get_frontend,
5699a0bf528SMauro Carvalho Chehab 
5709a0bf528SMauro Carvalho Chehab 	.read_status = rtl2830_read_status,
5719a0bf528SMauro Carvalho Chehab 	.read_snr = rtl2830_read_snr,
5729a0bf528SMauro Carvalho Chehab 	.read_ber = rtl2830_read_ber,
5739a0bf528SMauro Carvalho Chehab 	.read_ucblocks = rtl2830_read_ucblocks,
5749a0bf528SMauro Carvalho Chehab 	.read_signal_strength = rtl2830_read_signal_strength,
5759a0bf528SMauro Carvalho Chehab };
5769a0bf528SMauro Carvalho Chehab 
577df70ddadSAntti Palosaari static int rtl2830_pid_filter_ctrl(struct dvb_frontend *fe, int onoff)
578df70ddadSAntti Palosaari {
579df70ddadSAntti Palosaari 	struct i2c_client *client = fe->demodulator_priv;
580df70ddadSAntti Palosaari 	int ret;
581df70ddadSAntti Palosaari 	u8 u8tmp;
582df70ddadSAntti Palosaari 
583df70ddadSAntti Palosaari 	dev_dbg(&client->dev, "onoff=%d\n", onoff);
584df70ddadSAntti Palosaari 
585df70ddadSAntti Palosaari 	/* enable / disable PID filter */
586df70ddadSAntti Palosaari 	if (onoff)
587df70ddadSAntti Palosaari 		u8tmp = 0x80;
588df70ddadSAntti Palosaari 	else
589df70ddadSAntti Palosaari 		u8tmp = 0x00;
590df70ddadSAntti Palosaari 
59115d37f38SAntti Palosaari 	ret = rtl2830_update_bits(client, 0x061, 0x80, u8tmp);
592df70ddadSAntti Palosaari 	if (ret)
593df70ddadSAntti Palosaari 		goto err;
594df70ddadSAntti Palosaari 
595df70ddadSAntti Palosaari 	return 0;
596df70ddadSAntti Palosaari err:
597df70ddadSAntti Palosaari 	dev_dbg(&client->dev, "failed=%d\n", ret);
598df70ddadSAntti Palosaari 	return ret;
599df70ddadSAntti Palosaari }
600df70ddadSAntti Palosaari 
601df70ddadSAntti Palosaari static int rtl2830_pid_filter(struct dvb_frontend *fe, u8 index, u16 pid, int onoff)
602df70ddadSAntti Palosaari {
603df70ddadSAntti Palosaari 	struct i2c_client *client = fe->demodulator_priv;
604df70ddadSAntti Palosaari 	struct rtl2830_dev *dev = i2c_get_clientdata(client);
605df70ddadSAntti Palosaari 	int ret;
606df70ddadSAntti Palosaari 	u8 buf[4];
607df70ddadSAntti Palosaari 
608df70ddadSAntti Palosaari 	dev_dbg(&client->dev, "index=%d pid=%04x onoff=%d\n",
609df70ddadSAntti Palosaari 		index, pid, onoff);
610df70ddadSAntti Palosaari 
611df70ddadSAntti Palosaari 	/* skip invalid PIDs (0x2000) */
612df70ddadSAntti Palosaari 	if (pid > 0x1fff || index > 32)
613df70ddadSAntti Palosaari 		return 0;
614df70ddadSAntti Palosaari 
615df70ddadSAntti Palosaari 	if (onoff)
616df70ddadSAntti Palosaari 		set_bit(index, &dev->filters);
617df70ddadSAntti Palosaari 	else
618df70ddadSAntti Palosaari 		clear_bit(index, &dev->filters);
619df70ddadSAntti Palosaari 
620df70ddadSAntti Palosaari 	/* enable / disable PIDs */
621df70ddadSAntti Palosaari 	buf[0] = (dev->filters >>  0) & 0xff;
622df70ddadSAntti Palosaari 	buf[1] = (dev->filters >>  8) & 0xff;
623df70ddadSAntti Palosaari 	buf[2] = (dev->filters >> 16) & 0xff;
624df70ddadSAntti Palosaari 	buf[3] = (dev->filters >> 24) & 0xff;
62515d37f38SAntti Palosaari 	ret = rtl2830_bulk_write(client, 0x062, buf, 4);
626df70ddadSAntti Palosaari 	if (ret)
627df70ddadSAntti Palosaari 		goto err;
628df70ddadSAntti Palosaari 
629df70ddadSAntti Palosaari 	/* add PID */
630df70ddadSAntti Palosaari 	buf[0] = (pid >> 8) & 0xff;
631df70ddadSAntti Palosaari 	buf[1] = (pid >> 0) & 0xff;
63215d37f38SAntti Palosaari 	ret = rtl2830_bulk_write(client, 0x066 + 2 * index, buf, 2);
633df70ddadSAntti Palosaari 	if (ret)
634df70ddadSAntti Palosaari 		goto err;
635df70ddadSAntti Palosaari 
636df70ddadSAntti Palosaari 	return 0;
637df70ddadSAntti Palosaari err:
638df70ddadSAntti Palosaari 	dev_dbg(&client->dev, "failed=%d\n", ret);
639df70ddadSAntti Palosaari 	return ret;
640df70ddadSAntti Palosaari }
641df70ddadSAntti Palosaari 
64228c08799SAntti Palosaari /*
64315d37f38SAntti Palosaari  * I2C gate/mux/repeater logic
64415d37f38SAntti Palosaari  * We must use unlocked __i2c_transfer() here (through regmap) because of I2C
64515d37f38SAntti Palosaari  * adapter lock is already taken by tuner driver.
64615d37f38SAntti Palosaari  * Gate is closed automatically after single I2C transfer.
64728c08799SAntti Palosaari  */
648a0119159SPeter Rosin static int rtl2830_select(struct i2c_mux_core *muxc, u32 chan_id)
64928c08799SAntti Palosaari {
650a0119159SPeter Rosin 	struct i2c_client *client = i2c_mux_priv(muxc);
651f544f100SAntti Palosaari 	struct rtl2830_dev *dev = i2c_get_clientdata(client);
65228c08799SAntti Palosaari 	int ret;
65328c08799SAntti Palosaari 
654fd4cfa8bSAntti Palosaari 	dev_dbg(&client->dev, "\n");
655fd4cfa8bSAntti Palosaari 
65615d37f38SAntti Palosaari 	/* open I2C repeater for 1 transfer, closes automatically */
65715d37f38SAntti Palosaari 	/* XXX: regmap_update_bits() does not lock I2C adapter */
65815d37f38SAntti Palosaari 	ret = regmap_update_bits(dev->regmap, 0x101, 0x08, 0x08);
65915d37f38SAntti Palosaari 	if (ret)
66028c08799SAntti Palosaari 		goto err;
66128c08799SAntti Palosaari 
66228c08799SAntti Palosaari 	return 0;
66328c08799SAntti Palosaari err:
6647cc39328SAntti Palosaari 	dev_dbg(&client->dev, "failed=%d\n", ret);
66528c08799SAntti Palosaari 	return ret;
66628c08799SAntti Palosaari }
66728c08799SAntti Palosaari 
66828c08799SAntti Palosaari static struct dvb_frontend *rtl2830_get_dvb_frontend(struct i2c_client *client)
66928c08799SAntti Palosaari {
670f544f100SAntti Palosaari 	struct rtl2830_dev *dev = i2c_get_clientdata(client);
67128c08799SAntti Palosaari 
67228c08799SAntti Palosaari 	dev_dbg(&client->dev, "\n");
67328c08799SAntti Palosaari 
674f544f100SAntti Palosaari 	return &dev->fe;
67528c08799SAntti Palosaari }
67628c08799SAntti Palosaari 
67728c08799SAntti Palosaari static struct i2c_adapter *rtl2830_get_i2c_adapter(struct i2c_client *client)
67828c08799SAntti Palosaari {
679f544f100SAntti Palosaari 	struct rtl2830_dev *dev = i2c_get_clientdata(client);
68028c08799SAntti Palosaari 
68128c08799SAntti Palosaari 	dev_dbg(&client->dev, "\n");
68228c08799SAntti Palosaari 
683a0119159SPeter Rosin 	return dev->muxc->adapter[0];
68428c08799SAntti Palosaari }
68528c08799SAntti Palosaari 
68615d37f38SAntti Palosaari /*
68715d37f38SAntti Palosaari  * We implement own I2C access routines for regmap in order to get manual access
68815d37f38SAntti Palosaari  * to I2C adapter lock, which is needed for I2C mux adapter.
68915d37f38SAntti Palosaari  */
69015d37f38SAntti Palosaari static int rtl2830_regmap_read(void *context, const void *reg_buf,
69115d37f38SAntti Palosaari 			       size_t reg_size, void *val_buf, size_t val_size)
69215d37f38SAntti Palosaari {
69315d37f38SAntti Palosaari 	struct i2c_client *client = context;
69415d37f38SAntti Palosaari 	int ret;
69515d37f38SAntti Palosaari 	struct i2c_msg msg[2] = {
69615d37f38SAntti Palosaari 		{
69715d37f38SAntti Palosaari 			.addr = client->addr,
69815d37f38SAntti Palosaari 			.flags = 0,
69915d37f38SAntti Palosaari 			.len = reg_size,
70015d37f38SAntti Palosaari 			.buf = (u8 *)reg_buf,
70115d37f38SAntti Palosaari 		}, {
70215d37f38SAntti Palosaari 			.addr = client->addr,
70315d37f38SAntti Palosaari 			.flags = I2C_M_RD,
70415d37f38SAntti Palosaari 			.len = val_size,
70515d37f38SAntti Palosaari 			.buf = val_buf,
70615d37f38SAntti Palosaari 		}
70715d37f38SAntti Palosaari 	};
70815d37f38SAntti Palosaari 
70915d37f38SAntti Palosaari 	ret = __i2c_transfer(client->adapter, msg, 2);
71015d37f38SAntti Palosaari 	if (ret != 2) {
71115d37f38SAntti Palosaari 		dev_warn(&client->dev, "i2c reg read failed %d\n", ret);
71215d37f38SAntti Palosaari 		if (ret >= 0)
71315d37f38SAntti Palosaari 			ret = -EREMOTEIO;
71415d37f38SAntti Palosaari 		return ret;
71515d37f38SAntti Palosaari 	}
71615d37f38SAntti Palosaari 	return 0;
71715d37f38SAntti Palosaari }
71815d37f38SAntti Palosaari 
71915d37f38SAntti Palosaari static int rtl2830_regmap_write(void *context, const void *data, size_t count)
72015d37f38SAntti Palosaari {
72115d37f38SAntti Palosaari 	struct i2c_client *client = context;
72215d37f38SAntti Palosaari 	int ret;
72315d37f38SAntti Palosaari 	struct i2c_msg msg[1] = {
72415d37f38SAntti Palosaari 		{
72515d37f38SAntti Palosaari 			.addr = client->addr,
72615d37f38SAntti Palosaari 			.flags = 0,
72715d37f38SAntti Palosaari 			.len = count,
72815d37f38SAntti Palosaari 			.buf = (u8 *)data,
72915d37f38SAntti Palosaari 		}
73015d37f38SAntti Palosaari 	};
73115d37f38SAntti Palosaari 
73215d37f38SAntti Palosaari 	ret = __i2c_transfer(client->adapter, msg, 1);
73315d37f38SAntti Palosaari 	if (ret != 1) {
73415d37f38SAntti Palosaari 		dev_warn(&client->dev, "i2c reg write failed %d\n", ret);
73515d37f38SAntti Palosaari 		if (ret >= 0)
73615d37f38SAntti Palosaari 			ret = -EREMOTEIO;
73715d37f38SAntti Palosaari 		return ret;
73815d37f38SAntti Palosaari 	}
73915d37f38SAntti Palosaari 	return 0;
74015d37f38SAntti Palosaari }
74115d37f38SAntti Palosaari 
74215d37f38SAntti Palosaari static int rtl2830_regmap_gather_write(void *context, const void *reg,
74315d37f38SAntti Palosaari 				       size_t reg_len, const void *val,
74415d37f38SAntti Palosaari 				       size_t val_len)
74515d37f38SAntti Palosaari {
74615d37f38SAntti Palosaari 	struct i2c_client *client = context;
74715d37f38SAntti Palosaari 	int ret;
74815d37f38SAntti Palosaari 	u8 buf[256];
74915d37f38SAntti Palosaari 	struct i2c_msg msg[1] = {
75015d37f38SAntti Palosaari 		{
75115d37f38SAntti Palosaari 			.addr = client->addr,
75215d37f38SAntti Palosaari 			.flags = 0,
75315d37f38SAntti Palosaari 			.len = 1 + val_len,
75415d37f38SAntti Palosaari 			.buf = buf,
75515d37f38SAntti Palosaari 		}
75615d37f38SAntti Palosaari 	};
75715d37f38SAntti Palosaari 
75815d37f38SAntti Palosaari 	buf[0] = *(u8 const *)reg;
75915d37f38SAntti Palosaari 	memcpy(&buf[1], val, val_len);
76015d37f38SAntti Palosaari 
76115d37f38SAntti Palosaari 	ret = __i2c_transfer(client->adapter, msg, 1);
76215d37f38SAntti Palosaari 	if (ret != 1) {
76315d37f38SAntti Palosaari 		dev_warn(&client->dev, "i2c reg write failed %d\n", ret);
76415d37f38SAntti Palosaari 		if (ret >= 0)
76515d37f38SAntti Palosaari 			ret = -EREMOTEIO;
76615d37f38SAntti Palosaari 		return ret;
76715d37f38SAntti Palosaari 	}
76815d37f38SAntti Palosaari 	return 0;
76915d37f38SAntti Palosaari }
77015d37f38SAntti Palosaari 
77128c08799SAntti Palosaari static int rtl2830_probe(struct i2c_client *client,
77228c08799SAntti Palosaari 			 const struct i2c_device_id *id)
77328c08799SAntti Palosaari {
77428c08799SAntti Palosaari 	struct rtl2830_platform_data *pdata = client->dev.platform_data;
775f544f100SAntti Palosaari 	struct rtl2830_dev *dev;
77628c08799SAntti Palosaari 	int ret;
77728c08799SAntti Palosaari 	u8 u8tmp;
77815d37f38SAntti Palosaari 	static const struct regmap_bus regmap_bus = {
77915d37f38SAntti Palosaari 		.read = rtl2830_regmap_read,
78015d37f38SAntti Palosaari 		.write = rtl2830_regmap_write,
78115d37f38SAntti Palosaari 		.gather_write = rtl2830_regmap_gather_write,
78215d37f38SAntti Palosaari 		.val_format_endian_default = REGMAP_ENDIAN_NATIVE,
78315d37f38SAntti Palosaari 	};
78415d37f38SAntti Palosaari 	static const struct regmap_range_cfg regmap_range_cfg[] = {
78515d37f38SAntti Palosaari 		{
78615d37f38SAntti Palosaari 			.selector_reg     = 0x00,
78715d37f38SAntti Palosaari 			.selector_mask    = 0xff,
78815d37f38SAntti Palosaari 			.selector_shift   = 0,
78915d37f38SAntti Palosaari 			.window_start     = 0,
79015d37f38SAntti Palosaari 			.window_len       = 0x100,
79115d37f38SAntti Palosaari 			.range_min        = 0 * 0x100,
79215d37f38SAntti Palosaari 			.range_max        = 5 * 0x100,
79315d37f38SAntti Palosaari 		},
79415d37f38SAntti Palosaari 	};
79515d37f38SAntti Palosaari 	static const struct regmap_config regmap_config = {
79615d37f38SAntti Palosaari 		.reg_bits    =  8,
79715d37f38SAntti Palosaari 		.val_bits    =  8,
79815d37f38SAntti Palosaari 		.max_register = 5 * 0x100,
79915d37f38SAntti Palosaari 		.ranges = regmap_range_cfg,
80015d37f38SAntti Palosaari 		.num_ranges = ARRAY_SIZE(regmap_range_cfg),
80115d37f38SAntti Palosaari 	};
80228c08799SAntti Palosaari 
80328c08799SAntti Palosaari 	dev_dbg(&client->dev, "\n");
80428c08799SAntti Palosaari 
80528c08799SAntti Palosaari 	if (pdata == NULL) {
80628c08799SAntti Palosaari 		ret = -EINVAL;
80728c08799SAntti Palosaari 		goto err;
80828c08799SAntti Palosaari 	}
80928c08799SAntti Palosaari 
81028c08799SAntti Palosaari 	/* allocate memory for the internal state */
811f544f100SAntti Palosaari 	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
812f544f100SAntti Palosaari 	if (dev == NULL) {
81328c08799SAntti Palosaari 		ret = -ENOMEM;
81428c08799SAntti Palosaari 		goto err;
81528c08799SAntti Palosaari 	}
81628c08799SAntti Palosaari 
81728c08799SAntti Palosaari 	/* setup the state */
818f544f100SAntti Palosaari 	i2c_set_clientdata(client, dev);
81947b4dbffSAntti Palosaari 	dev->client = client;
820b8cb50d2SAntti Palosaari 	dev->pdata = client->dev.platform_data;
821f544f100SAntti Palosaari 	dev->sleeping = true;
82215d37f38SAntti Palosaari 	dev->regmap = regmap_init(&client->dev, &regmap_bus, client,
82315d37f38SAntti Palosaari 				  &regmap_config);
82415d37f38SAntti Palosaari 	if (IS_ERR(dev->regmap)) {
82515d37f38SAntti Palosaari 		ret = PTR_ERR(dev->regmap);
82615d37f38SAntti Palosaari 		goto err_kfree;
82715d37f38SAntti Palosaari 	}
82828c08799SAntti Palosaari 
82928c08799SAntti Palosaari 	/* check if the demod is there */
83015d37f38SAntti Palosaari 	ret = rtl2830_bulk_read(client, 0x000, &u8tmp, 1);
83128c08799SAntti Palosaari 	if (ret)
83215d37f38SAntti Palosaari 		goto err_regmap_exit;
83328c08799SAntti Palosaari 
83428c08799SAntti Palosaari 	/* create muxed i2c adapter for tuner */
835a0119159SPeter Rosin 	dev->muxc = i2c_mux_alloc(client->adapter, &client->dev, 1, 0, 0,
836a0119159SPeter Rosin 				  rtl2830_select, NULL);
837a0119159SPeter Rosin 	if (!dev->muxc) {
838a0119159SPeter Rosin 		ret = -ENOMEM;
83915d37f38SAntti Palosaari 		goto err_regmap_exit;
84028c08799SAntti Palosaari 	}
841a0119159SPeter Rosin 	dev->muxc->priv = client;
842a0119159SPeter Rosin 	ret = i2c_mux_add_adapter(dev->muxc, 0, 0, 0);
843a0119159SPeter Rosin 	if (ret)
844a0119159SPeter Rosin 		goto err_regmap_exit;
84528c08799SAntti Palosaari 
84628c08799SAntti Palosaari 	/* create dvb frontend */
847f544f100SAntti Palosaari 	memcpy(&dev->fe.ops, &rtl2830_ops, sizeof(dev->fe.ops));
8481f153c4dSAntti Palosaari 	dev->fe.demodulator_priv = client;
84928c08799SAntti Palosaari 
85028c08799SAntti Palosaari 	/* setup callbacks */
85128c08799SAntti Palosaari 	pdata->get_dvb_frontend = rtl2830_get_dvb_frontend;
85228c08799SAntti Palosaari 	pdata->get_i2c_adapter = rtl2830_get_i2c_adapter;
853df70ddadSAntti Palosaari 	pdata->pid_filter = rtl2830_pid_filter;
854df70ddadSAntti Palosaari 	pdata->pid_filter_ctrl = rtl2830_pid_filter_ctrl;
85528c08799SAntti Palosaari 
85628c08799SAntti Palosaari 	dev_info(&client->dev, "Realtek RTL2830 successfully attached\n");
85728c08799SAntti Palosaari 
858947debb4SAntti Palosaari 	return 0;
85915d37f38SAntti Palosaari err_regmap_exit:
86015d37f38SAntti Palosaari 	regmap_exit(dev->regmap);
86128c08799SAntti Palosaari err_kfree:
862f544f100SAntti Palosaari 	kfree(dev);
86328c08799SAntti Palosaari err:
86428c08799SAntti Palosaari 	dev_dbg(&client->dev, "failed=%d\n", ret);
86528c08799SAntti Palosaari 	return ret;
86628c08799SAntti Palosaari }
86728c08799SAntti Palosaari 
86828c08799SAntti Palosaari static int rtl2830_remove(struct i2c_client *client)
86928c08799SAntti Palosaari {
870f544f100SAntti Palosaari 	struct rtl2830_dev *dev = i2c_get_clientdata(client);
87128c08799SAntti Palosaari 
87228c08799SAntti Palosaari 	dev_dbg(&client->dev, "\n");
87328c08799SAntti Palosaari 
874a0119159SPeter Rosin 	i2c_mux_del_adapters(dev->muxc);
87515d37f38SAntti Palosaari 	regmap_exit(dev->regmap);
876f544f100SAntti Palosaari 	kfree(dev);
877947debb4SAntti Palosaari 
87828c08799SAntti Palosaari 	return 0;
87928c08799SAntti Palosaari }
88028c08799SAntti Palosaari 
88128c08799SAntti Palosaari static const struct i2c_device_id rtl2830_id_table[] = {
88228c08799SAntti Palosaari 	{"rtl2830", 0},
88328c08799SAntti Palosaari 	{}
88428c08799SAntti Palosaari };
88528c08799SAntti Palosaari MODULE_DEVICE_TABLE(i2c, rtl2830_id_table);
88628c08799SAntti Palosaari 
88728c08799SAntti Palosaari static struct i2c_driver rtl2830_driver = {
88828c08799SAntti Palosaari 	.driver = {
88928c08799SAntti Palosaari 		.name			= "rtl2830",
89095e7cdb7SAntti Palosaari 		.suppress_bind_attrs	= true,
89128c08799SAntti Palosaari 	},
89228c08799SAntti Palosaari 	.probe		= rtl2830_probe,
89328c08799SAntti Palosaari 	.remove		= rtl2830_remove,
89428c08799SAntti Palosaari 	.id_table	= rtl2830_id_table,
89528c08799SAntti Palosaari };
89628c08799SAntti Palosaari 
89728c08799SAntti Palosaari module_i2c_driver(rtl2830_driver);
89828c08799SAntti Palosaari 
8999a0bf528SMauro Carvalho Chehab MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
9009a0bf528SMauro Carvalho Chehab MODULE_DESCRIPTION("Realtek RTL2830 DVB-T demodulator driver");
9019a0bf528SMauro Carvalho Chehab MODULE_LICENSE("GPL");
902