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