174ba9207SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
29a0bf528SMauro Carvalho Chehab /*
39a0bf528SMauro Carvalho Chehab Driver for ST STV0288 demodulator
49a0bf528SMauro Carvalho Chehab Copyright (C) 2006 Georg Acher, BayCom GmbH, acher (at) baycom (dot) de
59a0bf528SMauro Carvalho Chehab for Reel Multimedia
69a0bf528SMauro Carvalho Chehab Copyright (C) 2008 TurboSight.com, Bob Liu <bob@turbosight.com>
79a0bf528SMauro Carvalho Chehab Copyright (C) 2008 Igor M. Liplianin <liplianin@me.by>
89a0bf528SMauro Carvalho Chehab Removed stb6000 specific tuner code and revised some
99a0bf528SMauro Carvalho Chehab procedures.
109a0bf528SMauro Carvalho Chehab 2010-09-01 Josef Pavlik <josef@pavlik.it>
119a0bf528SMauro Carvalho Chehab Fixed diseqc_msg, diseqc_burst and set_tone problems
129a0bf528SMauro Carvalho Chehab
139a0bf528SMauro Carvalho Chehab
149a0bf528SMauro Carvalho Chehab */
159a0bf528SMauro Carvalho Chehab
169a0bf528SMauro Carvalho Chehab #include <linux/init.h>
179a0bf528SMauro Carvalho Chehab #include <linux/kernel.h>
189a0bf528SMauro Carvalho Chehab #include <linux/module.h>
199a0bf528SMauro Carvalho Chehab #include <linux/string.h>
209a0bf528SMauro Carvalho Chehab #include <linux/slab.h>
219a0bf528SMauro Carvalho Chehab #include <linux/jiffies.h>
229a0bf528SMauro Carvalho Chehab #include <asm/div64.h>
239a0bf528SMauro Carvalho Chehab
24fada1935SMauro Carvalho Chehab #include <media/dvb_frontend.h>
259a0bf528SMauro Carvalho Chehab #include "stv0288.h"
269a0bf528SMauro Carvalho Chehab
279a0bf528SMauro Carvalho Chehab struct stv0288_state {
289a0bf528SMauro Carvalho Chehab struct i2c_adapter *i2c;
299a0bf528SMauro Carvalho Chehab const struct stv0288_config *config;
309a0bf528SMauro Carvalho Chehab struct dvb_frontend frontend;
319a0bf528SMauro Carvalho Chehab
329a0bf528SMauro Carvalho Chehab u8 initialised:1;
339a0bf528SMauro Carvalho Chehab u32 tuner_frequency;
349a0bf528SMauro Carvalho Chehab u32 symbol_rate;
350df289a2SMauro Carvalho Chehab enum fe_code_rate fec_inner;
369a0bf528SMauro Carvalho Chehab int errmode;
379a0bf528SMauro Carvalho Chehab };
389a0bf528SMauro Carvalho Chehab
399a0bf528SMauro Carvalho Chehab #define STATUS_BER 0
409a0bf528SMauro Carvalho Chehab #define STATUS_UCBLOCKS 1
419a0bf528SMauro Carvalho Chehab
429a0bf528SMauro Carvalho Chehab static int debug;
439a0bf528SMauro Carvalho Chehab static int debug_legacy_dish_switch;
449a0bf528SMauro Carvalho Chehab #define dprintk(args...) \
459a0bf528SMauro Carvalho Chehab do { \
469a0bf528SMauro Carvalho Chehab if (debug) \
479a0bf528SMauro Carvalho Chehab printk(KERN_DEBUG "stv0288: " args); \
489a0bf528SMauro Carvalho Chehab } while (0)
499a0bf528SMauro Carvalho Chehab
509a0bf528SMauro Carvalho Chehab
stv0288_writeregI(struct stv0288_state * state,u8 reg,u8 data)519a0bf528SMauro Carvalho Chehab static int stv0288_writeregI(struct stv0288_state *state, u8 reg, u8 data)
529a0bf528SMauro Carvalho Chehab {
539a0bf528SMauro Carvalho Chehab int ret;
549a0bf528SMauro Carvalho Chehab u8 buf[] = { reg, data };
559a0bf528SMauro Carvalho Chehab struct i2c_msg msg = {
569a0bf528SMauro Carvalho Chehab .addr = state->config->demod_address,
579a0bf528SMauro Carvalho Chehab .flags = 0,
589a0bf528SMauro Carvalho Chehab .buf = buf,
599a0bf528SMauro Carvalho Chehab .len = 2
609a0bf528SMauro Carvalho Chehab };
619a0bf528SMauro Carvalho Chehab
629a0bf528SMauro Carvalho Chehab ret = i2c_transfer(state->i2c, &msg, 1);
639a0bf528SMauro Carvalho Chehab
649a0bf528SMauro Carvalho Chehab if (ret != 1)
654bd69e7bSMauro Carvalho Chehab dprintk("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n",
664bd69e7bSMauro Carvalho Chehab __func__, reg, data, ret);
679a0bf528SMauro Carvalho Chehab
689a0bf528SMauro Carvalho Chehab return (ret != 1) ? -EREMOTEIO : 0;
699a0bf528SMauro Carvalho Chehab }
709a0bf528SMauro Carvalho Chehab
stv0288_write(struct dvb_frontend * fe,const u8 buf[],int len)719a0bf528SMauro Carvalho Chehab static int stv0288_write(struct dvb_frontend *fe, const u8 buf[], int len)
729a0bf528SMauro Carvalho Chehab {
739a0bf528SMauro Carvalho Chehab struct stv0288_state *state = fe->demodulator_priv;
749a0bf528SMauro Carvalho Chehab
759a0bf528SMauro Carvalho Chehab if (len != 2)
769a0bf528SMauro Carvalho Chehab return -EINVAL;
779a0bf528SMauro Carvalho Chehab
789a0bf528SMauro Carvalho Chehab return stv0288_writeregI(state, buf[0], buf[1]);
799a0bf528SMauro Carvalho Chehab }
809a0bf528SMauro Carvalho Chehab
stv0288_readreg(struct stv0288_state * state,u8 reg)819a0bf528SMauro Carvalho Chehab static u8 stv0288_readreg(struct stv0288_state *state, u8 reg)
829a0bf528SMauro Carvalho Chehab {
839a0bf528SMauro Carvalho Chehab int ret;
849a0bf528SMauro Carvalho Chehab u8 b0[] = { reg };
859a0bf528SMauro Carvalho Chehab u8 b1[] = { 0 };
869a0bf528SMauro Carvalho Chehab struct i2c_msg msg[] = {
879a0bf528SMauro Carvalho Chehab {
889a0bf528SMauro Carvalho Chehab .addr = state->config->demod_address,
899a0bf528SMauro Carvalho Chehab .flags = 0,
909a0bf528SMauro Carvalho Chehab .buf = b0,
919a0bf528SMauro Carvalho Chehab .len = 1
929a0bf528SMauro Carvalho Chehab }, {
939a0bf528SMauro Carvalho Chehab .addr = state->config->demod_address,
949a0bf528SMauro Carvalho Chehab .flags = I2C_M_RD,
959a0bf528SMauro Carvalho Chehab .buf = b1,
969a0bf528SMauro Carvalho Chehab .len = 1
979a0bf528SMauro Carvalho Chehab }
989a0bf528SMauro Carvalho Chehab };
999a0bf528SMauro Carvalho Chehab
1009a0bf528SMauro Carvalho Chehab ret = i2c_transfer(state->i2c, msg, 2);
1019a0bf528SMauro Carvalho Chehab
1029a0bf528SMauro Carvalho Chehab if (ret != 2)
1039a0bf528SMauro Carvalho Chehab dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n",
1049a0bf528SMauro Carvalho Chehab __func__, reg, ret);
1059a0bf528SMauro Carvalho Chehab
1069a0bf528SMauro Carvalho Chehab return b1[0];
1079a0bf528SMauro Carvalho Chehab }
1089a0bf528SMauro Carvalho Chehab
stv0288_set_symbolrate(struct dvb_frontend * fe,u32 srate)1099a0bf528SMauro Carvalho Chehab static int stv0288_set_symbolrate(struct dvb_frontend *fe, u32 srate)
1109a0bf528SMauro Carvalho Chehab {
1119a0bf528SMauro Carvalho Chehab struct stv0288_state *state = fe->demodulator_priv;
1129a0bf528SMauro Carvalho Chehab unsigned int temp;
1139a0bf528SMauro Carvalho Chehab unsigned char b[3];
1149a0bf528SMauro Carvalho Chehab
1159a0bf528SMauro Carvalho Chehab if ((srate < 1000000) || (srate > 45000000))
1169a0bf528SMauro Carvalho Chehab return -EINVAL;
1179a0bf528SMauro Carvalho Chehab
1189a0bf528SMauro Carvalho Chehab stv0288_writeregI(state, 0x22, 0);
1199a0bf528SMauro Carvalho Chehab stv0288_writeregI(state, 0x23, 0);
1209a0bf528SMauro Carvalho Chehab stv0288_writeregI(state, 0x2b, 0xff);
1219a0bf528SMauro Carvalho Chehab stv0288_writeregI(state, 0x2c, 0xf7);
1229a0bf528SMauro Carvalho Chehab
1239a0bf528SMauro Carvalho Chehab temp = (unsigned int)srate / 1000;
1249a0bf528SMauro Carvalho Chehab
1259a0bf528SMauro Carvalho Chehab temp = temp * 32768;
1269a0bf528SMauro Carvalho Chehab temp = temp / 25;
1279a0bf528SMauro Carvalho Chehab temp = temp / 125;
1289a0bf528SMauro Carvalho Chehab b[0] = (unsigned char)((temp >> 12) & 0xff);
1299a0bf528SMauro Carvalho Chehab b[1] = (unsigned char)((temp >> 4) & 0xff);
1309a0bf528SMauro Carvalho Chehab b[2] = (unsigned char)((temp << 4) & 0xf0);
1319a0bf528SMauro Carvalho Chehab stv0288_writeregI(state, 0x28, 0x80); /* SFRH */
1329a0bf528SMauro Carvalho Chehab stv0288_writeregI(state, 0x29, 0); /* SFRM */
1339a0bf528SMauro Carvalho Chehab stv0288_writeregI(state, 0x2a, 0); /* SFRL */
1349a0bf528SMauro Carvalho Chehab
1359a0bf528SMauro Carvalho Chehab stv0288_writeregI(state, 0x28, b[0]);
1369a0bf528SMauro Carvalho Chehab stv0288_writeregI(state, 0x29, b[1]);
1379a0bf528SMauro Carvalho Chehab stv0288_writeregI(state, 0x2a, b[2]);
1389a0bf528SMauro Carvalho Chehab dprintk("stv0288: stv0288_set_symbolrate\n");
1399a0bf528SMauro Carvalho Chehab
1409a0bf528SMauro Carvalho Chehab return 0;
1419a0bf528SMauro Carvalho Chehab }
1429a0bf528SMauro Carvalho Chehab
stv0288_send_diseqc_msg(struct dvb_frontend * fe,struct dvb_diseqc_master_cmd * m)1439a0bf528SMauro Carvalho Chehab static int stv0288_send_diseqc_msg(struct dvb_frontend *fe,
1449a0bf528SMauro Carvalho Chehab struct dvb_diseqc_master_cmd *m)
1459a0bf528SMauro Carvalho Chehab {
1469a0bf528SMauro Carvalho Chehab struct stv0288_state *state = fe->demodulator_priv;
1479a0bf528SMauro Carvalho Chehab
1489a0bf528SMauro Carvalho Chehab int i;
1499a0bf528SMauro Carvalho Chehab
1509a0bf528SMauro Carvalho Chehab dprintk("%s\n", __func__);
1519a0bf528SMauro Carvalho Chehab
1529a0bf528SMauro Carvalho Chehab stv0288_writeregI(state, 0x09, 0);
1539a0bf528SMauro Carvalho Chehab msleep(30);
1549a0bf528SMauro Carvalho Chehab stv0288_writeregI(state, 0x05, 0x12);/* modulated mode, single shot */
1559a0bf528SMauro Carvalho Chehab
1569a0bf528SMauro Carvalho Chehab for (i = 0; i < m->msg_len; i++) {
1579a0bf528SMauro Carvalho Chehab if (stv0288_writeregI(state, 0x06, m->msg[i]))
1589a0bf528SMauro Carvalho Chehab return -EREMOTEIO;
1599a0bf528SMauro Carvalho Chehab }
1609a0bf528SMauro Carvalho Chehab msleep(m->msg_len*12);
1619a0bf528SMauro Carvalho Chehab return 0;
1629a0bf528SMauro Carvalho Chehab }
1639a0bf528SMauro Carvalho Chehab
stv0288_send_diseqc_burst(struct dvb_frontend * fe,enum fe_sec_mini_cmd burst)1649a0bf528SMauro Carvalho Chehab static int stv0288_send_diseqc_burst(struct dvb_frontend *fe,
1650df289a2SMauro Carvalho Chehab enum fe_sec_mini_cmd burst)
1669a0bf528SMauro Carvalho Chehab {
1679a0bf528SMauro Carvalho Chehab struct stv0288_state *state = fe->demodulator_priv;
1689a0bf528SMauro Carvalho Chehab
1699a0bf528SMauro Carvalho Chehab dprintk("%s\n", __func__);
1709a0bf528SMauro Carvalho Chehab
1719a0bf528SMauro Carvalho Chehab if (stv0288_writeregI(state, 0x05, 0x03))/* burst mode, single shot */
1729a0bf528SMauro Carvalho Chehab return -EREMOTEIO;
1739a0bf528SMauro Carvalho Chehab
1749a0bf528SMauro Carvalho Chehab if (stv0288_writeregI(state, 0x06, burst == SEC_MINI_A ? 0x00 : 0xff))
1759a0bf528SMauro Carvalho Chehab return -EREMOTEIO;
1769a0bf528SMauro Carvalho Chehab
1779a0bf528SMauro Carvalho Chehab msleep(15);
1789a0bf528SMauro Carvalho Chehab if (stv0288_writeregI(state, 0x05, 0x12))
1799a0bf528SMauro Carvalho Chehab return -EREMOTEIO;
1809a0bf528SMauro Carvalho Chehab
1819a0bf528SMauro Carvalho Chehab return 0;
1829a0bf528SMauro Carvalho Chehab }
1839a0bf528SMauro Carvalho Chehab
stv0288_set_tone(struct dvb_frontend * fe,enum fe_sec_tone_mode tone)1840df289a2SMauro Carvalho Chehab static int stv0288_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone)
1859a0bf528SMauro Carvalho Chehab {
1869a0bf528SMauro Carvalho Chehab struct stv0288_state *state = fe->demodulator_priv;
1879a0bf528SMauro Carvalho Chehab
1889a0bf528SMauro Carvalho Chehab switch (tone) {
1899a0bf528SMauro Carvalho Chehab case SEC_TONE_ON:
1909a0bf528SMauro Carvalho Chehab if (stv0288_writeregI(state, 0x05, 0x10))/* cont carrier */
1919a0bf528SMauro Carvalho Chehab return -EREMOTEIO;
1929a0bf528SMauro Carvalho Chehab break;
1939a0bf528SMauro Carvalho Chehab
1949a0bf528SMauro Carvalho Chehab case SEC_TONE_OFF:
1959a0bf528SMauro Carvalho Chehab if (stv0288_writeregI(state, 0x05, 0x12))/* burst mode off*/
1969a0bf528SMauro Carvalho Chehab return -EREMOTEIO;
1979a0bf528SMauro Carvalho Chehab break;
1989a0bf528SMauro Carvalho Chehab
1999a0bf528SMauro Carvalho Chehab default:
2009a0bf528SMauro Carvalho Chehab return -EINVAL;
2019a0bf528SMauro Carvalho Chehab }
2029a0bf528SMauro Carvalho Chehab return 0;
2039a0bf528SMauro Carvalho Chehab }
2049a0bf528SMauro Carvalho Chehab
2059a0bf528SMauro Carvalho Chehab static u8 stv0288_inittab[] = {
2069a0bf528SMauro Carvalho Chehab 0x01, 0x15,
2079a0bf528SMauro Carvalho Chehab 0x02, 0x20,
2089a0bf528SMauro Carvalho Chehab 0x09, 0x0,
2099a0bf528SMauro Carvalho Chehab 0x0a, 0x4,
2109a0bf528SMauro Carvalho Chehab 0x0b, 0x0,
2119a0bf528SMauro Carvalho Chehab 0x0c, 0x0,
2129a0bf528SMauro Carvalho Chehab 0x0d, 0x0,
2139a0bf528SMauro Carvalho Chehab 0x0e, 0xd4,
2149a0bf528SMauro Carvalho Chehab 0x0f, 0x30,
2159a0bf528SMauro Carvalho Chehab 0x11, 0x80,
2169a0bf528SMauro Carvalho Chehab 0x12, 0x03,
2179a0bf528SMauro Carvalho Chehab 0x13, 0x48,
2189a0bf528SMauro Carvalho Chehab 0x14, 0x84,
2199a0bf528SMauro Carvalho Chehab 0x15, 0x45,
2209a0bf528SMauro Carvalho Chehab 0x16, 0xb7,
2219a0bf528SMauro Carvalho Chehab 0x17, 0x9c,
2229a0bf528SMauro Carvalho Chehab 0x18, 0x0,
2239a0bf528SMauro Carvalho Chehab 0x19, 0xa6,
2249a0bf528SMauro Carvalho Chehab 0x1a, 0x88,
2259a0bf528SMauro Carvalho Chehab 0x1b, 0x8f,
2269a0bf528SMauro Carvalho Chehab 0x1c, 0xf0,
2279a0bf528SMauro Carvalho Chehab 0x20, 0x0b,
2289a0bf528SMauro Carvalho Chehab 0x21, 0x54,
2299a0bf528SMauro Carvalho Chehab 0x22, 0x0,
2309a0bf528SMauro Carvalho Chehab 0x23, 0x0,
2319a0bf528SMauro Carvalho Chehab 0x2b, 0xff,
2329a0bf528SMauro Carvalho Chehab 0x2c, 0xf7,
2339a0bf528SMauro Carvalho Chehab 0x30, 0x0,
2349a0bf528SMauro Carvalho Chehab 0x31, 0x1e,
2359a0bf528SMauro Carvalho Chehab 0x32, 0x14,
2369a0bf528SMauro Carvalho Chehab 0x33, 0x0f,
2379a0bf528SMauro Carvalho Chehab 0x34, 0x09,
2389a0bf528SMauro Carvalho Chehab 0x35, 0x0c,
2399a0bf528SMauro Carvalho Chehab 0x36, 0x05,
2409a0bf528SMauro Carvalho Chehab 0x37, 0x2f,
2419a0bf528SMauro Carvalho Chehab 0x38, 0x16,
2429a0bf528SMauro Carvalho Chehab 0x39, 0xbe,
2439a0bf528SMauro Carvalho Chehab 0x3a, 0x0,
2449a0bf528SMauro Carvalho Chehab 0x3b, 0x13,
2459a0bf528SMauro Carvalho Chehab 0x3c, 0x11,
2469a0bf528SMauro Carvalho Chehab 0x3d, 0x30,
2479a0bf528SMauro Carvalho Chehab 0x40, 0x63,
2489a0bf528SMauro Carvalho Chehab 0x41, 0x04,
2499a0bf528SMauro Carvalho Chehab 0x42, 0x20,
2509a0bf528SMauro Carvalho Chehab 0x43, 0x00,
2519a0bf528SMauro Carvalho Chehab 0x44, 0x00,
2529a0bf528SMauro Carvalho Chehab 0x45, 0x00,
2539a0bf528SMauro Carvalho Chehab 0x46, 0x00,
2549a0bf528SMauro Carvalho Chehab 0x47, 0x00,
2559a0bf528SMauro Carvalho Chehab 0x4a, 0x00,
2569a0bf528SMauro Carvalho Chehab 0x50, 0x10,
2579a0bf528SMauro Carvalho Chehab 0x51, 0x38,
2589a0bf528SMauro Carvalho Chehab 0x52, 0x21,
2599a0bf528SMauro Carvalho Chehab 0x58, 0x54,
2609a0bf528SMauro Carvalho Chehab 0x59, 0x86,
2619a0bf528SMauro Carvalho Chehab 0x5a, 0x0,
2629a0bf528SMauro Carvalho Chehab 0x5b, 0x9b,
2639a0bf528SMauro Carvalho Chehab 0x5c, 0x08,
2649a0bf528SMauro Carvalho Chehab 0x5d, 0x7f,
2659a0bf528SMauro Carvalho Chehab 0x5e, 0x0,
2669a0bf528SMauro Carvalho Chehab 0x5f, 0xff,
2679a0bf528SMauro Carvalho Chehab 0x70, 0x0,
2689a0bf528SMauro Carvalho Chehab 0x71, 0x0,
2699a0bf528SMauro Carvalho Chehab 0x72, 0x0,
2709a0bf528SMauro Carvalho Chehab 0x74, 0x0,
2719a0bf528SMauro Carvalho Chehab 0x75, 0x0,
2729a0bf528SMauro Carvalho Chehab 0x76, 0x0,
2739a0bf528SMauro Carvalho Chehab 0x81, 0x0,
2749a0bf528SMauro Carvalho Chehab 0x82, 0x3f,
2759a0bf528SMauro Carvalho Chehab 0x83, 0x3f,
2769a0bf528SMauro Carvalho Chehab 0x84, 0x0,
2779a0bf528SMauro Carvalho Chehab 0x85, 0x0,
2789a0bf528SMauro Carvalho Chehab 0x88, 0x0,
2799a0bf528SMauro Carvalho Chehab 0x89, 0x0,
2809a0bf528SMauro Carvalho Chehab 0x8a, 0x0,
2819a0bf528SMauro Carvalho Chehab 0x8b, 0x0,
2829a0bf528SMauro Carvalho Chehab 0x8c, 0x0,
2839a0bf528SMauro Carvalho Chehab 0x90, 0x0,
2849a0bf528SMauro Carvalho Chehab 0x91, 0x0,
2859a0bf528SMauro Carvalho Chehab 0x92, 0x0,
2869a0bf528SMauro Carvalho Chehab 0x93, 0x0,
2879a0bf528SMauro Carvalho Chehab 0x94, 0x1c,
2889a0bf528SMauro Carvalho Chehab 0x97, 0x0,
2899a0bf528SMauro Carvalho Chehab 0xa0, 0x48,
2909a0bf528SMauro Carvalho Chehab 0xa1, 0x0,
2919a0bf528SMauro Carvalho Chehab 0xb0, 0xb8,
2929a0bf528SMauro Carvalho Chehab 0xb1, 0x3a,
2939a0bf528SMauro Carvalho Chehab 0xb2, 0x10,
2949a0bf528SMauro Carvalho Chehab 0xb3, 0x82,
2959a0bf528SMauro Carvalho Chehab 0xb4, 0x80,
2969a0bf528SMauro Carvalho Chehab 0xb5, 0x82,
2979a0bf528SMauro Carvalho Chehab 0xb6, 0x82,
2989a0bf528SMauro Carvalho Chehab 0xb7, 0x82,
2999a0bf528SMauro Carvalho Chehab 0xb8, 0x20,
3009a0bf528SMauro Carvalho Chehab 0xb9, 0x0,
3019a0bf528SMauro Carvalho Chehab 0xf0, 0x0,
3029a0bf528SMauro Carvalho Chehab 0xf1, 0x0,
3039a0bf528SMauro Carvalho Chehab 0xf2, 0xc0,
3049a0bf528SMauro Carvalho Chehab 0x51, 0x36,
3059a0bf528SMauro Carvalho Chehab 0x52, 0x09,
3069a0bf528SMauro Carvalho Chehab 0x53, 0x94,
3079a0bf528SMauro Carvalho Chehab 0x54, 0x62,
3089a0bf528SMauro Carvalho Chehab 0x55, 0x29,
3099a0bf528SMauro Carvalho Chehab 0x56, 0x64,
3109a0bf528SMauro Carvalho Chehab 0x57, 0x2b,
3119a0bf528SMauro Carvalho Chehab 0xff, 0xff,
3129a0bf528SMauro Carvalho Chehab };
3139a0bf528SMauro Carvalho Chehab
stv0288_set_voltage(struct dvb_frontend * fe,enum fe_sec_voltage volt)3140df289a2SMauro Carvalho Chehab static int stv0288_set_voltage(struct dvb_frontend *fe,
3150df289a2SMauro Carvalho Chehab enum fe_sec_voltage volt)
3169a0bf528SMauro Carvalho Chehab {
3179a0bf528SMauro Carvalho Chehab dprintk("%s: %s\n", __func__,
3189a0bf528SMauro Carvalho Chehab volt == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" :
3199a0bf528SMauro Carvalho Chehab volt == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??");
3209a0bf528SMauro Carvalho Chehab
3219a0bf528SMauro Carvalho Chehab return 0;
3229a0bf528SMauro Carvalho Chehab }
3239a0bf528SMauro Carvalho Chehab
stv0288_init(struct dvb_frontend * fe)3249a0bf528SMauro Carvalho Chehab static int stv0288_init(struct dvb_frontend *fe)
3259a0bf528SMauro Carvalho Chehab {
3269a0bf528SMauro Carvalho Chehab struct stv0288_state *state = fe->demodulator_priv;
3279a0bf528SMauro Carvalho Chehab int i;
3289a0bf528SMauro Carvalho Chehab u8 reg;
3299a0bf528SMauro Carvalho Chehab u8 val;
3309a0bf528SMauro Carvalho Chehab
3319a0bf528SMauro Carvalho Chehab dprintk("stv0288: init chip\n");
3329a0bf528SMauro Carvalho Chehab stv0288_writeregI(state, 0x41, 0x04);
3339a0bf528SMauro Carvalho Chehab msleep(50);
3349a0bf528SMauro Carvalho Chehab
3359a0bf528SMauro Carvalho Chehab /* we have default inittab */
3369a0bf528SMauro Carvalho Chehab if (state->config->inittab == NULL) {
3379a0bf528SMauro Carvalho Chehab for (i = 0; !(stv0288_inittab[i] == 0xff &&
3389a0bf528SMauro Carvalho Chehab stv0288_inittab[i + 1] == 0xff); i += 2)
3399a0bf528SMauro Carvalho Chehab stv0288_writeregI(state, stv0288_inittab[i],
3409a0bf528SMauro Carvalho Chehab stv0288_inittab[i + 1]);
3419a0bf528SMauro Carvalho Chehab } else {
3429a0bf528SMauro Carvalho Chehab for (i = 0; ; i += 2) {
3439a0bf528SMauro Carvalho Chehab reg = state->config->inittab[i];
3449a0bf528SMauro Carvalho Chehab val = state->config->inittab[i+1];
3459a0bf528SMauro Carvalho Chehab if (reg == 0xff && val == 0xff)
3469a0bf528SMauro Carvalho Chehab break;
3479a0bf528SMauro Carvalho Chehab stv0288_writeregI(state, reg, val);
3489a0bf528SMauro Carvalho Chehab }
3499a0bf528SMauro Carvalho Chehab }
3509a0bf528SMauro Carvalho Chehab return 0;
3519a0bf528SMauro Carvalho Chehab }
3529a0bf528SMauro Carvalho Chehab
stv0288_read_status(struct dvb_frontend * fe,enum fe_status * status)3530df289a2SMauro Carvalho Chehab static int stv0288_read_status(struct dvb_frontend *fe, enum fe_status *status)
3549a0bf528SMauro Carvalho Chehab {
3559a0bf528SMauro Carvalho Chehab struct stv0288_state *state = fe->demodulator_priv;
3569a0bf528SMauro Carvalho Chehab
3579a0bf528SMauro Carvalho Chehab u8 sync = stv0288_readreg(state, 0x24);
3589a0bf528SMauro Carvalho Chehab if (sync == 255)
3599a0bf528SMauro Carvalho Chehab sync = 0;
3609a0bf528SMauro Carvalho Chehab
3619a0bf528SMauro Carvalho Chehab dprintk("%s : FE_READ_STATUS : VSTATUS: 0x%02x\n", __func__, sync);
3629a0bf528SMauro Carvalho Chehab
3639a0bf528SMauro Carvalho Chehab *status = 0;
3649a0bf528SMauro Carvalho Chehab if (sync & 0x80)
3659a0bf528SMauro Carvalho Chehab *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL;
3669a0bf528SMauro Carvalho Chehab if (sync & 0x10)
3679a0bf528SMauro Carvalho Chehab *status |= FE_HAS_VITERBI;
3689a0bf528SMauro Carvalho Chehab if (sync & 0x08) {
3699a0bf528SMauro Carvalho Chehab *status |= FE_HAS_LOCK;
3709a0bf528SMauro Carvalho Chehab dprintk("stv0288 has locked\n");
3719a0bf528SMauro Carvalho Chehab }
3729a0bf528SMauro Carvalho Chehab
3739a0bf528SMauro Carvalho Chehab return 0;
3749a0bf528SMauro Carvalho Chehab }
3759a0bf528SMauro Carvalho Chehab
stv0288_read_ber(struct dvb_frontend * fe,u32 * ber)3769a0bf528SMauro Carvalho Chehab static int stv0288_read_ber(struct dvb_frontend *fe, u32 *ber)
3779a0bf528SMauro Carvalho Chehab {
3789a0bf528SMauro Carvalho Chehab struct stv0288_state *state = fe->demodulator_priv;
3799a0bf528SMauro Carvalho Chehab
3809a0bf528SMauro Carvalho Chehab if (state->errmode != STATUS_BER)
3819a0bf528SMauro Carvalho Chehab return 0;
3829a0bf528SMauro Carvalho Chehab *ber = (stv0288_readreg(state, 0x26) << 8) |
3839a0bf528SMauro Carvalho Chehab stv0288_readreg(state, 0x27);
3849a0bf528SMauro Carvalho Chehab dprintk("stv0288_read_ber %d\n", *ber);
3859a0bf528SMauro Carvalho Chehab
3869a0bf528SMauro Carvalho Chehab return 0;
3879a0bf528SMauro Carvalho Chehab }
3889a0bf528SMauro Carvalho Chehab
3899a0bf528SMauro Carvalho Chehab
stv0288_read_signal_strength(struct dvb_frontend * fe,u16 * strength)3909a0bf528SMauro Carvalho Chehab static int stv0288_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
3919a0bf528SMauro Carvalho Chehab {
3929a0bf528SMauro Carvalho Chehab struct stv0288_state *state = fe->demodulator_priv;
3939a0bf528SMauro Carvalho Chehab
3949a0bf528SMauro Carvalho Chehab s32 signal = 0xffff - ((stv0288_readreg(state, 0x10) << 8));
3959a0bf528SMauro Carvalho Chehab
3969a0bf528SMauro Carvalho Chehab
3979a0bf528SMauro Carvalho Chehab signal = signal * 5 / 4;
3989a0bf528SMauro Carvalho Chehab *strength = (signal > 0xffff) ? 0xffff : (signal < 0) ? 0 : signal;
3999a0bf528SMauro Carvalho Chehab dprintk("stv0288_read_signal_strength %d\n", *strength);
4009a0bf528SMauro Carvalho Chehab
4019a0bf528SMauro Carvalho Chehab return 0;
4029a0bf528SMauro Carvalho Chehab }
stv0288_sleep(struct dvb_frontend * fe)4039a0bf528SMauro Carvalho Chehab static int stv0288_sleep(struct dvb_frontend *fe)
4049a0bf528SMauro Carvalho Chehab {
4059a0bf528SMauro Carvalho Chehab struct stv0288_state *state = fe->demodulator_priv;
4069a0bf528SMauro Carvalho Chehab
4079a0bf528SMauro Carvalho Chehab stv0288_writeregI(state, 0x41, 0x84);
4089a0bf528SMauro Carvalho Chehab state->initialised = 0;
4099a0bf528SMauro Carvalho Chehab
4109a0bf528SMauro Carvalho Chehab return 0;
4119a0bf528SMauro Carvalho Chehab }
stv0288_read_snr(struct dvb_frontend * fe,u16 * snr)4129a0bf528SMauro Carvalho Chehab static int stv0288_read_snr(struct dvb_frontend *fe, u16 *snr)
4139a0bf528SMauro Carvalho Chehab {
4149a0bf528SMauro Carvalho Chehab struct stv0288_state *state = fe->demodulator_priv;
4159a0bf528SMauro Carvalho Chehab
4169a0bf528SMauro Carvalho Chehab s32 xsnr = 0xffff - ((stv0288_readreg(state, 0x2d) << 8)
4179a0bf528SMauro Carvalho Chehab | stv0288_readreg(state, 0x2e));
4189a0bf528SMauro Carvalho Chehab xsnr = 3 * (xsnr - 0xa100);
4199a0bf528SMauro Carvalho Chehab *snr = (xsnr > 0xffff) ? 0xffff : (xsnr < 0) ? 0 : xsnr;
4209a0bf528SMauro Carvalho Chehab dprintk("stv0288_read_snr %d\n", *snr);
4219a0bf528SMauro Carvalho Chehab
4229a0bf528SMauro Carvalho Chehab return 0;
4239a0bf528SMauro Carvalho Chehab }
4249a0bf528SMauro Carvalho Chehab
stv0288_read_ucblocks(struct dvb_frontend * fe,u32 * ucblocks)4259a0bf528SMauro Carvalho Chehab static int stv0288_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
4269a0bf528SMauro Carvalho Chehab {
4279a0bf528SMauro Carvalho Chehab struct stv0288_state *state = fe->demodulator_priv;
4289a0bf528SMauro Carvalho Chehab
4299a0bf528SMauro Carvalho Chehab if (state->errmode != STATUS_BER)
4309a0bf528SMauro Carvalho Chehab return 0;
4319a0bf528SMauro Carvalho Chehab *ucblocks = (stv0288_readreg(state, 0x26) << 8) |
4329a0bf528SMauro Carvalho Chehab stv0288_readreg(state, 0x27);
4339a0bf528SMauro Carvalho Chehab dprintk("stv0288_read_ber %d\n", *ucblocks);
4349a0bf528SMauro Carvalho Chehab
4359a0bf528SMauro Carvalho Chehab return 0;
4369a0bf528SMauro Carvalho Chehab }
4379a0bf528SMauro Carvalho Chehab
stv0288_set_frontend(struct dvb_frontend * fe)4389a0bf528SMauro Carvalho Chehab static int stv0288_set_frontend(struct dvb_frontend *fe)
4399a0bf528SMauro Carvalho Chehab {
4409a0bf528SMauro Carvalho Chehab struct stv0288_state *state = fe->demodulator_priv;
4419a0bf528SMauro Carvalho Chehab struct dtv_frontend_properties *c = &fe->dtv_property_cache;
4429a0bf528SMauro Carvalho Chehab
44373921344SJason A. Donenfeld u8 tda[3], reg, time_out = 0;
44473921344SJason A. Donenfeld s8 tm;
4459a0bf528SMauro Carvalho Chehab
4469a0bf528SMauro Carvalho Chehab dprintk("%s : FE_SET_FRONTEND\n", __func__);
4479a0bf528SMauro Carvalho Chehab
4489a0bf528SMauro Carvalho Chehab if (c->delivery_system != SYS_DVBS) {
4494bd69e7bSMauro Carvalho Chehab dprintk("%s: unsupported delivery system selected (%d)\n",
4509a0bf528SMauro Carvalho Chehab __func__, c->delivery_system);
4519a0bf528SMauro Carvalho Chehab return -EOPNOTSUPP;
4529a0bf528SMauro Carvalho Chehab }
4539a0bf528SMauro Carvalho Chehab
4549a0bf528SMauro Carvalho Chehab if (state->config->set_ts_params)
4559a0bf528SMauro Carvalho Chehab state->config->set_ts_params(fe, 0);
4569a0bf528SMauro Carvalho Chehab
4579a0bf528SMauro Carvalho Chehab /* only frequency & symbol_rate are used for tuner*/
4589a0bf528SMauro Carvalho Chehab if (fe->ops.tuner_ops.set_params) {
4599a0bf528SMauro Carvalho Chehab fe->ops.tuner_ops.set_params(fe);
4609a0bf528SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl)
4619a0bf528SMauro Carvalho Chehab fe->ops.i2c_gate_ctrl(fe, 0);
4629a0bf528SMauro Carvalho Chehab }
4639a0bf528SMauro Carvalho Chehab
4649a0bf528SMauro Carvalho Chehab udelay(10);
4659a0bf528SMauro Carvalho Chehab stv0288_set_symbolrate(fe, c->symbol_rate);
4669a0bf528SMauro Carvalho Chehab /* Carrier lock control register */
4679a0bf528SMauro Carvalho Chehab stv0288_writeregI(state, 0x15, 0xc5);
4689a0bf528SMauro Carvalho Chehab
4699a0bf528SMauro Carvalho Chehab tda[2] = 0x0; /* CFRL */
4709a0bf528SMauro Carvalho Chehab for (tm = -9; tm < 7;) {
4719a0bf528SMauro Carvalho Chehab /* Viterbi status */
4729a0bf528SMauro Carvalho Chehab reg = stv0288_readreg(state, 0x24);
4739a0bf528SMauro Carvalho Chehab if (reg & 0x8)
4749a0bf528SMauro Carvalho Chehab break;
4759a0bf528SMauro Carvalho Chehab if (reg & 0x80) {
4769a0bf528SMauro Carvalho Chehab time_out++;
4779a0bf528SMauro Carvalho Chehab if (time_out > 10)
4789a0bf528SMauro Carvalho Chehab break;
4799a0bf528SMauro Carvalho Chehab tda[2] += 40;
4809a0bf528SMauro Carvalho Chehab if (tda[2] < 40)
4819a0bf528SMauro Carvalho Chehab tm++;
4829a0bf528SMauro Carvalho Chehab } else {
4839a0bf528SMauro Carvalho Chehab tm++;
4849a0bf528SMauro Carvalho Chehab tda[2] = 0;
4859a0bf528SMauro Carvalho Chehab time_out = 0;
4869a0bf528SMauro Carvalho Chehab }
4879a0bf528SMauro Carvalho Chehab tda[1] = (unsigned char)tm;
4889a0bf528SMauro Carvalho Chehab stv0288_writeregI(state, 0x2b, tda[1]);
4899a0bf528SMauro Carvalho Chehab stv0288_writeregI(state, 0x2c, tda[2]);
4909a0bf528SMauro Carvalho Chehab msleep(30);
4919a0bf528SMauro Carvalho Chehab }
4929a0bf528SMauro Carvalho Chehab state->tuner_frequency = c->frequency;
4939a0bf528SMauro Carvalho Chehab state->fec_inner = FEC_AUTO;
4949a0bf528SMauro Carvalho Chehab state->symbol_rate = c->symbol_rate;
4959a0bf528SMauro Carvalho Chehab
4969a0bf528SMauro Carvalho Chehab return 0;
4979a0bf528SMauro Carvalho Chehab }
4989a0bf528SMauro Carvalho Chehab
stv0288_i2c_gate_ctrl(struct dvb_frontend * fe,int enable)4999a0bf528SMauro Carvalho Chehab static int stv0288_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
5009a0bf528SMauro Carvalho Chehab {
5019a0bf528SMauro Carvalho Chehab struct stv0288_state *state = fe->demodulator_priv;
5029a0bf528SMauro Carvalho Chehab
5039a0bf528SMauro Carvalho Chehab if (enable)
5049a0bf528SMauro Carvalho Chehab stv0288_writeregI(state, 0x01, 0xb5);
5059a0bf528SMauro Carvalho Chehab else
5069a0bf528SMauro Carvalho Chehab stv0288_writeregI(state, 0x01, 0x35);
5079a0bf528SMauro Carvalho Chehab
5089a0bf528SMauro Carvalho Chehab udelay(1);
5099a0bf528SMauro Carvalho Chehab
5109a0bf528SMauro Carvalho Chehab return 0;
5119a0bf528SMauro Carvalho Chehab }
5129a0bf528SMauro Carvalho Chehab
stv0288_release(struct dvb_frontend * fe)5139a0bf528SMauro Carvalho Chehab static void stv0288_release(struct dvb_frontend *fe)
5149a0bf528SMauro Carvalho Chehab {
5159a0bf528SMauro Carvalho Chehab struct stv0288_state *state = fe->demodulator_priv;
5169a0bf528SMauro Carvalho Chehab kfree(state);
5179a0bf528SMauro Carvalho Chehab }
5189a0bf528SMauro Carvalho Chehab
519bd336e63SMax Kellermann static const struct dvb_frontend_ops stv0288_ops = {
5209a0bf528SMauro Carvalho Chehab .delsys = { SYS_DVBS },
5219a0bf528SMauro Carvalho Chehab .info = {
5229a0bf528SMauro Carvalho Chehab .name = "ST STV0288 DVB-S",
523f1b1eabfSMauro Carvalho Chehab .frequency_min_hz = 950 * MHz,
524f1b1eabfSMauro Carvalho Chehab .frequency_max_hz = 2150 * MHz,
525f1b1eabfSMauro Carvalho Chehab .frequency_stepsize_hz = 1 * MHz,
5269a0bf528SMauro Carvalho Chehab .symbol_rate_min = 1000000,
5279a0bf528SMauro Carvalho Chehab .symbol_rate_max = 45000000,
5289a0bf528SMauro Carvalho Chehab .symbol_rate_tolerance = 500, /* ppm */
5299a0bf528SMauro Carvalho Chehab .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
5309a0bf528SMauro Carvalho Chehab FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 |
5319a0bf528SMauro Carvalho Chehab FE_CAN_QPSK |
5329a0bf528SMauro Carvalho Chehab FE_CAN_FEC_AUTO
5339a0bf528SMauro Carvalho Chehab },
5349a0bf528SMauro Carvalho Chehab
5359a0bf528SMauro Carvalho Chehab .release = stv0288_release,
5369a0bf528SMauro Carvalho Chehab .init = stv0288_init,
5379a0bf528SMauro Carvalho Chehab .sleep = stv0288_sleep,
5389a0bf528SMauro Carvalho Chehab .write = stv0288_write,
5399a0bf528SMauro Carvalho Chehab .i2c_gate_ctrl = stv0288_i2c_gate_ctrl,
5409a0bf528SMauro Carvalho Chehab .read_status = stv0288_read_status,
5419a0bf528SMauro Carvalho Chehab .read_ber = stv0288_read_ber,
5429a0bf528SMauro Carvalho Chehab .read_signal_strength = stv0288_read_signal_strength,
5439a0bf528SMauro Carvalho Chehab .read_snr = stv0288_read_snr,
5449a0bf528SMauro Carvalho Chehab .read_ucblocks = stv0288_read_ucblocks,
5459a0bf528SMauro Carvalho Chehab .diseqc_send_master_cmd = stv0288_send_diseqc_msg,
5469a0bf528SMauro Carvalho Chehab .diseqc_send_burst = stv0288_send_diseqc_burst,
5479a0bf528SMauro Carvalho Chehab .set_tone = stv0288_set_tone,
5489a0bf528SMauro Carvalho Chehab .set_voltage = stv0288_set_voltage,
5499a0bf528SMauro Carvalho Chehab
5509a0bf528SMauro Carvalho Chehab .set_frontend = stv0288_set_frontend,
5519a0bf528SMauro Carvalho Chehab };
5529a0bf528SMauro Carvalho Chehab
stv0288_attach(const struct stv0288_config * config,struct i2c_adapter * i2c)5539a0bf528SMauro Carvalho Chehab struct dvb_frontend *stv0288_attach(const struct stv0288_config *config,
5549a0bf528SMauro Carvalho Chehab struct i2c_adapter *i2c)
5559a0bf528SMauro Carvalho Chehab {
5569a0bf528SMauro Carvalho Chehab struct stv0288_state *state = NULL;
5579a0bf528SMauro Carvalho Chehab int id;
5589a0bf528SMauro Carvalho Chehab
5599a0bf528SMauro Carvalho Chehab /* allocate memory for the internal state */
5609a0bf528SMauro Carvalho Chehab state = kzalloc(sizeof(struct stv0288_state), GFP_KERNEL);
5619a0bf528SMauro Carvalho Chehab if (state == NULL)
5629a0bf528SMauro Carvalho Chehab goto error;
5639a0bf528SMauro Carvalho Chehab
5649a0bf528SMauro Carvalho Chehab /* setup the state */
5659a0bf528SMauro Carvalho Chehab state->config = config;
5669a0bf528SMauro Carvalho Chehab state->i2c = i2c;
5679a0bf528SMauro Carvalho Chehab state->initialised = 0;
5689a0bf528SMauro Carvalho Chehab state->tuner_frequency = 0;
5699a0bf528SMauro Carvalho Chehab state->symbol_rate = 0;
5709a0bf528SMauro Carvalho Chehab state->fec_inner = 0;
5719a0bf528SMauro Carvalho Chehab state->errmode = STATUS_BER;
5729a0bf528SMauro Carvalho Chehab
5739a0bf528SMauro Carvalho Chehab stv0288_writeregI(state, 0x41, 0x04);
5749a0bf528SMauro Carvalho Chehab msleep(200);
5759a0bf528SMauro Carvalho Chehab id = stv0288_readreg(state, 0x00);
5769a0bf528SMauro Carvalho Chehab dprintk("stv0288 id %x\n", id);
5779a0bf528SMauro Carvalho Chehab
5789a0bf528SMauro Carvalho Chehab /* register 0x00 contains 0x11 for STV0288 */
5799a0bf528SMauro Carvalho Chehab if (id != 0x11)
5809a0bf528SMauro Carvalho Chehab goto error;
5819a0bf528SMauro Carvalho Chehab
5829a0bf528SMauro Carvalho Chehab /* create dvb_frontend */
5839a0bf528SMauro Carvalho Chehab memcpy(&state->frontend.ops, &stv0288_ops,
5849a0bf528SMauro Carvalho Chehab sizeof(struct dvb_frontend_ops));
5859a0bf528SMauro Carvalho Chehab state->frontend.demodulator_priv = state;
5869a0bf528SMauro Carvalho Chehab return &state->frontend;
5879a0bf528SMauro Carvalho Chehab
5889a0bf528SMauro Carvalho Chehab error:
5899a0bf528SMauro Carvalho Chehab kfree(state);
5909a0bf528SMauro Carvalho Chehab
5919a0bf528SMauro Carvalho Chehab return NULL;
5929a0bf528SMauro Carvalho Chehab }
593*86495af1SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(stv0288_attach);
5949a0bf528SMauro Carvalho Chehab
5959a0bf528SMauro Carvalho Chehab module_param(debug_legacy_dish_switch, int, 0444);
5969a0bf528SMauro Carvalho Chehab MODULE_PARM_DESC(debug_legacy_dish_switch,
5979a0bf528SMauro Carvalho Chehab "Enable timing analysis for Dish Network legacy switches");
5989a0bf528SMauro Carvalho Chehab
5999a0bf528SMauro Carvalho Chehab module_param(debug, int, 0644);
6009a0bf528SMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
6019a0bf528SMauro Carvalho Chehab
6029a0bf528SMauro Carvalho Chehab MODULE_DESCRIPTION("ST STV0288 DVB Demodulator driver");
6039a0bf528SMauro Carvalho Chehab MODULE_AUTHOR("Georg Acher, Bob Liu, Igor liplianin");
6049a0bf528SMauro Carvalho Chehab MODULE_LICENSE("GPL");
6059a0bf528SMauro Carvalho Chehab
606