174ba9207SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
29a0bf528SMauro Carvalho Chehab   /*
39a0bf528SMauro Carvalho Chehab      Driver for ST STB6000 DVBS Silicon tuner
49a0bf528SMauro Carvalho Chehab 
59a0bf528SMauro Carvalho Chehab      Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by)
69a0bf528SMauro Carvalho Chehab 
79a0bf528SMauro Carvalho Chehab 
89a0bf528SMauro Carvalho Chehab   */
99a0bf528SMauro Carvalho Chehab 
109a0bf528SMauro Carvalho Chehab #include <linux/slab.h>
119a0bf528SMauro Carvalho Chehab #include <linux/module.h>
129a0bf528SMauro Carvalho Chehab #include <linux/dvb/frontend.h>
139a0bf528SMauro Carvalho Chehab #include <asm/types.h>
149a0bf528SMauro Carvalho Chehab 
159a0bf528SMauro Carvalho Chehab #include "stb6000.h"
169a0bf528SMauro Carvalho Chehab 
179a0bf528SMauro Carvalho Chehab static int debug;
189a0bf528SMauro Carvalho Chehab #define dprintk(args...) \
199a0bf528SMauro Carvalho Chehab 	do { \
209a0bf528SMauro Carvalho Chehab 		if (debug) \
219a0bf528SMauro Carvalho Chehab 			printk(KERN_DEBUG "stb6000: " args); \
229a0bf528SMauro Carvalho Chehab 	} while (0)
239a0bf528SMauro Carvalho Chehab 
249a0bf528SMauro Carvalho Chehab struct stb6000_priv {
259a0bf528SMauro Carvalho Chehab 	/* i2c details */
269a0bf528SMauro Carvalho Chehab 	int i2c_address;
279a0bf528SMauro Carvalho Chehab 	struct i2c_adapter *i2c;
289a0bf528SMauro Carvalho Chehab 	u32 frequency;
299a0bf528SMauro Carvalho Chehab };
309a0bf528SMauro Carvalho Chehab 
stb6000_release(struct dvb_frontend * fe)31f2709c20SMauro Carvalho Chehab static void stb6000_release(struct dvb_frontend *fe)
32f2709c20SMauro Carvalho Chehab {
33f2709c20SMauro Carvalho Chehab 	kfree(fe->tuner_priv);
34f2709c20SMauro Carvalho Chehab 	fe->tuner_priv = NULL;
35f2709c20SMauro Carvalho Chehab }
36f2709c20SMauro Carvalho Chehab 
stb6000_sleep(struct dvb_frontend * fe)379a0bf528SMauro Carvalho Chehab static int stb6000_sleep(struct dvb_frontend *fe)
389a0bf528SMauro Carvalho Chehab {
399a0bf528SMauro Carvalho Chehab 	struct stb6000_priv *priv = fe->tuner_priv;
409a0bf528SMauro Carvalho Chehab 	int ret;
419a0bf528SMauro Carvalho Chehab 	u8 buf[] = { 10, 0 };
429a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg = {
439a0bf528SMauro Carvalho Chehab 		.addr = priv->i2c_address,
449a0bf528SMauro Carvalho Chehab 		.flags = 0,
459a0bf528SMauro Carvalho Chehab 		.buf = buf,
469a0bf528SMauro Carvalho Chehab 		.len = 2
479a0bf528SMauro Carvalho Chehab 	};
489a0bf528SMauro Carvalho Chehab 
499a0bf528SMauro Carvalho Chehab 	dprintk("%s:\n", __func__);
509a0bf528SMauro Carvalho Chehab 
519a0bf528SMauro Carvalho Chehab 	if (fe->ops.i2c_gate_ctrl)
529a0bf528SMauro Carvalho Chehab 		fe->ops.i2c_gate_ctrl(fe, 1);
539a0bf528SMauro Carvalho Chehab 
549a0bf528SMauro Carvalho Chehab 	ret = i2c_transfer(priv->i2c, &msg, 1);
559a0bf528SMauro Carvalho Chehab 	if (ret != 1)
569a0bf528SMauro Carvalho Chehab 		dprintk("%s: i2c error\n", __func__);
579a0bf528SMauro Carvalho Chehab 
589a0bf528SMauro Carvalho Chehab 	if (fe->ops.i2c_gate_ctrl)
599a0bf528SMauro Carvalho Chehab 		fe->ops.i2c_gate_ctrl(fe, 0);
609a0bf528SMauro Carvalho Chehab 
619a0bf528SMauro Carvalho Chehab 	return (ret == 1) ? 0 : ret;
629a0bf528SMauro Carvalho Chehab }
639a0bf528SMauro Carvalho Chehab 
stb6000_set_params(struct dvb_frontend * fe)649a0bf528SMauro Carvalho Chehab static int stb6000_set_params(struct dvb_frontend *fe)
659a0bf528SMauro Carvalho Chehab {
669a0bf528SMauro Carvalho Chehab 	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
679a0bf528SMauro Carvalho Chehab 	struct stb6000_priv *priv = fe->tuner_priv;
689a0bf528SMauro Carvalho Chehab 	unsigned int n, m;
699a0bf528SMauro Carvalho Chehab 	int ret;
709a0bf528SMauro Carvalho Chehab 	u32 freq_mhz;
719a0bf528SMauro Carvalho Chehab 	int bandwidth;
729a0bf528SMauro Carvalho Chehab 	u8 buf[12];
739a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg = {
749a0bf528SMauro Carvalho Chehab 		.addr = priv->i2c_address,
759a0bf528SMauro Carvalho Chehab 		.flags = 0,
769a0bf528SMauro Carvalho Chehab 		.buf = buf,
779a0bf528SMauro Carvalho Chehab 		.len = 12
789a0bf528SMauro Carvalho Chehab 	};
799a0bf528SMauro Carvalho Chehab 
809a0bf528SMauro Carvalho Chehab 	dprintk("%s:\n", __func__);
819a0bf528SMauro Carvalho Chehab 
829a0bf528SMauro Carvalho Chehab 	freq_mhz = p->frequency / 1000;
839a0bf528SMauro Carvalho Chehab 	bandwidth = p->symbol_rate / 1000000;
849a0bf528SMauro Carvalho Chehab 
859a0bf528SMauro Carvalho Chehab 	if (bandwidth > 31)
869a0bf528SMauro Carvalho Chehab 		bandwidth = 31;
879a0bf528SMauro Carvalho Chehab 
889a0bf528SMauro Carvalho Chehab 	if ((freq_mhz > 949) && (freq_mhz < 2151)) {
899a0bf528SMauro Carvalho Chehab 		buf[0] = 0x01;
909a0bf528SMauro Carvalho Chehab 		buf[1] = 0xac;
919a0bf528SMauro Carvalho Chehab 		if (freq_mhz < 1950)
929a0bf528SMauro Carvalho Chehab 			buf[1] = 0xaa;
939a0bf528SMauro Carvalho Chehab 		if (freq_mhz < 1800)
949a0bf528SMauro Carvalho Chehab 			buf[1] = 0xa8;
959a0bf528SMauro Carvalho Chehab 		if (freq_mhz < 1650)
969a0bf528SMauro Carvalho Chehab 			buf[1] = 0xa6;
979a0bf528SMauro Carvalho Chehab 		if (freq_mhz < 1530)
989a0bf528SMauro Carvalho Chehab 			buf[1] = 0xa5;
999a0bf528SMauro Carvalho Chehab 		if (freq_mhz < 1470)
1009a0bf528SMauro Carvalho Chehab 			buf[1] = 0xa4;
1019a0bf528SMauro Carvalho Chehab 		if (freq_mhz < 1370)
1029a0bf528SMauro Carvalho Chehab 			buf[1] = 0xa2;
1039a0bf528SMauro Carvalho Chehab 		if (freq_mhz < 1300)
1049a0bf528SMauro Carvalho Chehab 			buf[1] = 0xa1;
1059a0bf528SMauro Carvalho Chehab 		if (freq_mhz < 1200)
1069a0bf528SMauro Carvalho Chehab 			buf[1] = 0xa0;
1079a0bf528SMauro Carvalho Chehab 		if (freq_mhz < 1075)
1089a0bf528SMauro Carvalho Chehab 			buf[1] = 0xbc;
1099a0bf528SMauro Carvalho Chehab 		if (freq_mhz < 1000)
1109a0bf528SMauro Carvalho Chehab 			buf[1] = 0xba;
1119a0bf528SMauro Carvalho Chehab 		if (freq_mhz < 1075) {
1129a0bf528SMauro Carvalho Chehab 			n = freq_mhz / 8; /* vco=lo*4 */
1139a0bf528SMauro Carvalho Chehab 			m = 2;
1149a0bf528SMauro Carvalho Chehab 		} else {
1159a0bf528SMauro Carvalho Chehab 			n = freq_mhz / 16; /* vco=lo*2 */
1169a0bf528SMauro Carvalho Chehab 			m = 1;
1179a0bf528SMauro Carvalho Chehab 		}
1189a0bf528SMauro Carvalho Chehab 		buf[2] = n >> 1;
1199a0bf528SMauro Carvalho Chehab 		buf[3] = (unsigned char)(((n & 1) << 7) |
1209a0bf528SMauro Carvalho Chehab 					(m * freq_mhz - n * 16) | 0x60);
1219a0bf528SMauro Carvalho Chehab 		buf[4] = 0x04;
1229a0bf528SMauro Carvalho Chehab 		buf[5] = 0x0e;
1239a0bf528SMauro Carvalho Chehab 
1249a0bf528SMauro Carvalho Chehab 		buf[6] = (unsigned char)(bandwidth);
1259a0bf528SMauro Carvalho Chehab 
1269a0bf528SMauro Carvalho Chehab 		buf[7] = 0xd8;
1279a0bf528SMauro Carvalho Chehab 		buf[8] = 0xd0;
1289a0bf528SMauro Carvalho Chehab 		buf[9] = 0x50;
1299a0bf528SMauro Carvalho Chehab 		buf[10] = 0xeb;
1309a0bf528SMauro Carvalho Chehab 		buf[11] = 0x4f;
1319a0bf528SMauro Carvalho Chehab 
1329a0bf528SMauro Carvalho Chehab 		if (fe->ops.i2c_gate_ctrl)
1339a0bf528SMauro Carvalho Chehab 			fe->ops.i2c_gate_ctrl(fe, 1);
1349a0bf528SMauro Carvalho Chehab 
1359a0bf528SMauro Carvalho Chehab 		ret = i2c_transfer(priv->i2c, &msg, 1);
1369a0bf528SMauro Carvalho Chehab 		if (ret != 1)
1379a0bf528SMauro Carvalho Chehab 			dprintk("%s: i2c error\n", __func__);
1389a0bf528SMauro Carvalho Chehab 
1399a0bf528SMauro Carvalho Chehab 		udelay(10);
1409a0bf528SMauro Carvalho Chehab 		if (fe->ops.i2c_gate_ctrl)
1419a0bf528SMauro Carvalho Chehab 			fe->ops.i2c_gate_ctrl(fe, 0);
1429a0bf528SMauro Carvalho Chehab 
1439a0bf528SMauro Carvalho Chehab 		buf[0] = 0x07;
1449a0bf528SMauro Carvalho Chehab 		buf[1] = 0xdf;
1459a0bf528SMauro Carvalho Chehab 		buf[2] = 0xd0;
1469a0bf528SMauro Carvalho Chehab 		buf[3] = 0x50;
1479a0bf528SMauro Carvalho Chehab 		buf[4] = 0xfb;
1489a0bf528SMauro Carvalho Chehab 		msg.len = 5;
1499a0bf528SMauro Carvalho Chehab 
1509a0bf528SMauro Carvalho Chehab 		if (fe->ops.i2c_gate_ctrl)
1519a0bf528SMauro Carvalho Chehab 			fe->ops.i2c_gate_ctrl(fe, 1);
1529a0bf528SMauro Carvalho Chehab 
1539a0bf528SMauro Carvalho Chehab 		ret = i2c_transfer(priv->i2c, &msg, 1);
1549a0bf528SMauro Carvalho Chehab 		if (ret != 1)
1559a0bf528SMauro Carvalho Chehab 			dprintk("%s: i2c error\n", __func__);
1569a0bf528SMauro Carvalho Chehab 
1579a0bf528SMauro Carvalho Chehab 		udelay(10);
1589a0bf528SMauro Carvalho Chehab 		if (fe->ops.i2c_gate_ctrl)
1599a0bf528SMauro Carvalho Chehab 			fe->ops.i2c_gate_ctrl(fe, 0);
1609a0bf528SMauro Carvalho Chehab 
1619a0bf528SMauro Carvalho Chehab 		priv->frequency = freq_mhz * 1000;
1629a0bf528SMauro Carvalho Chehab 
1639a0bf528SMauro Carvalho Chehab 		return (ret == 1) ? 0 : ret;
1649a0bf528SMauro Carvalho Chehab 	}
1659a0bf528SMauro Carvalho Chehab 	return -1;
1669a0bf528SMauro Carvalho Chehab }
1679a0bf528SMauro Carvalho Chehab 
stb6000_get_frequency(struct dvb_frontend * fe,u32 * frequency)1689a0bf528SMauro Carvalho Chehab static int stb6000_get_frequency(struct dvb_frontend *fe, u32 *frequency)
1699a0bf528SMauro Carvalho Chehab {
1709a0bf528SMauro Carvalho Chehab 	struct stb6000_priv *priv = fe->tuner_priv;
1719a0bf528SMauro Carvalho Chehab 	*frequency = priv->frequency;
1729a0bf528SMauro Carvalho Chehab 	return 0;
1739a0bf528SMauro Carvalho Chehab }
1749a0bf528SMauro Carvalho Chehab 
17514c4bf3cSJulia Lawall static const struct dvb_tuner_ops stb6000_tuner_ops = {
1769a0bf528SMauro Carvalho Chehab 	.info = {
1779a0bf528SMauro Carvalho Chehab 		.name = "ST STB6000",
178a3f90c75SMauro Carvalho Chehab 		.frequency_min_hz =  950 * MHz,
179a3f90c75SMauro Carvalho Chehab 		.frequency_max_hz = 2150 * MHz
1809a0bf528SMauro Carvalho Chehab 	},
181f2709c20SMauro Carvalho Chehab 	.release = stb6000_release,
1829a0bf528SMauro Carvalho Chehab 	.sleep = stb6000_sleep,
1839a0bf528SMauro Carvalho Chehab 	.set_params = stb6000_set_params,
1849a0bf528SMauro Carvalho Chehab 	.get_frequency = stb6000_get_frequency,
1859a0bf528SMauro Carvalho Chehab };
1869a0bf528SMauro Carvalho Chehab 
stb6000_attach(struct dvb_frontend * fe,int addr,struct i2c_adapter * i2c)1879a0bf528SMauro Carvalho Chehab struct dvb_frontend *stb6000_attach(struct dvb_frontend *fe, int addr,
1889a0bf528SMauro Carvalho Chehab 						struct i2c_adapter *i2c)
1899a0bf528SMauro Carvalho Chehab {
1909a0bf528SMauro Carvalho Chehab 	struct stb6000_priv *priv = NULL;
1919a0bf528SMauro Carvalho Chehab 	u8 b0[] = { 0 };
1929a0bf528SMauro Carvalho Chehab 	u8 b1[] = { 0, 0 };
1939a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg[2] = {
1949a0bf528SMauro Carvalho Chehab 		{
1959a0bf528SMauro Carvalho Chehab 			.addr = addr,
1969a0bf528SMauro Carvalho Chehab 			.flags = 0,
1979a0bf528SMauro Carvalho Chehab 			.buf = b0,
1989a0bf528SMauro Carvalho Chehab 			.len = 0
1999a0bf528SMauro Carvalho Chehab 		}, {
2009a0bf528SMauro Carvalho Chehab 			.addr = addr,
2019a0bf528SMauro Carvalho Chehab 			.flags = I2C_M_RD,
2029a0bf528SMauro Carvalho Chehab 			.buf = b1,
2039a0bf528SMauro Carvalho Chehab 			.len = 2
2049a0bf528SMauro Carvalho Chehab 		}
2059a0bf528SMauro Carvalho Chehab 	};
2069a0bf528SMauro Carvalho Chehab 	int ret;
2079a0bf528SMauro Carvalho Chehab 
2089a0bf528SMauro Carvalho Chehab 	dprintk("%s:\n", __func__);
2099a0bf528SMauro Carvalho Chehab 
2109a0bf528SMauro Carvalho Chehab 	if (fe->ops.i2c_gate_ctrl)
2119a0bf528SMauro Carvalho Chehab 		fe->ops.i2c_gate_ctrl(fe, 1);
2129a0bf528SMauro Carvalho Chehab 
2139a0bf528SMauro Carvalho Chehab 	/* is some i2c device here ? */
2149a0bf528SMauro Carvalho Chehab 	ret = i2c_transfer(i2c, msg, 2);
2159a0bf528SMauro Carvalho Chehab 	if (fe->ops.i2c_gate_ctrl)
2169a0bf528SMauro Carvalho Chehab 		fe->ops.i2c_gate_ctrl(fe, 0);
2179a0bf528SMauro Carvalho Chehab 
2189a0bf528SMauro Carvalho Chehab 	if (ret != 2)
2199a0bf528SMauro Carvalho Chehab 		return NULL;
2209a0bf528SMauro Carvalho Chehab 
2219a0bf528SMauro Carvalho Chehab 	priv = kzalloc(sizeof(struct stb6000_priv), GFP_KERNEL);
2229a0bf528SMauro Carvalho Chehab 	if (priv == NULL)
2239a0bf528SMauro Carvalho Chehab 		return NULL;
2249a0bf528SMauro Carvalho Chehab 
2259a0bf528SMauro Carvalho Chehab 	priv->i2c_address = addr;
2269a0bf528SMauro Carvalho Chehab 	priv->i2c = i2c;
2279a0bf528SMauro Carvalho Chehab 
2289a0bf528SMauro Carvalho Chehab 	memcpy(&fe->ops.tuner_ops, &stb6000_tuner_ops,
2299a0bf528SMauro Carvalho Chehab 				sizeof(struct dvb_tuner_ops));
2309a0bf528SMauro Carvalho Chehab 
2319a0bf528SMauro Carvalho Chehab 	fe->tuner_priv = priv;
2329a0bf528SMauro Carvalho Chehab 
2339a0bf528SMauro Carvalho Chehab 	return fe;
2349a0bf528SMauro Carvalho Chehab }
235*86495af1SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(stb6000_attach);
2369a0bf528SMauro Carvalho Chehab 
2379a0bf528SMauro Carvalho Chehab module_param(debug, int, 0644);
2389a0bf528SMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
2399a0bf528SMauro Carvalho Chehab 
2409a0bf528SMauro Carvalho Chehab MODULE_DESCRIPTION("DVB STB6000 driver");
2419a0bf528SMauro Carvalho Chehab MODULE_AUTHOR("Igor M. Liplianin <liplianin@me.by>");
2429a0bf528SMauro Carvalho Chehab MODULE_LICENSE("GPL");
243