1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
25afc9a25SJemma Denson /*
35afc9a25SJemma Denson Conexant cx24120/cx24118 - DVBS/S2 Satellite demod/tuner driver
45afc9a25SJemma Denson
55afc9a25SJemma Denson Copyright (C) 2008 Patrick Boettcher <pb@linuxtv.org>
65afc9a25SJemma Denson Copyright (C) 2009 Sergey Tyurin <forum.free-x.de>
75afc9a25SJemma Denson Updated 2012 by Jannis Achstetter <jannis_achstetter@web.de>
85afc9a25SJemma Denson Copyright (C) 2015 Jemma Denson <jdenson@gmail.com>
95afc9a25SJemma Denson April 2015
105afc9a25SJemma Denson Refactored & simplified driver
115afc9a25SJemma Denson Updated to work with delivery system supplied by DVBv5
125afc9a25SJemma Denson Add frequency, fec & pilot to get_frontend
135afc9a25SJemma Denson
145afc9a25SJemma Denson Cards supported: Technisat Skystar S2
155afc9a25SJemma Denson
165afc9a25SJemma Denson */
175afc9a25SJemma Denson
185afc9a25SJemma Denson #include <linux/slab.h>
195afc9a25SJemma Denson #include <linux/kernel.h>
205afc9a25SJemma Denson #include <linux/module.h>
215afc9a25SJemma Denson #include <linux/moduleparam.h>
225afc9a25SJemma Denson #include <linux/init.h>
235afc9a25SJemma Denson #include <linux/firmware.h>
24fada1935SMauro Carvalho Chehab #include <media/dvb_frontend.h>
255afc9a25SJemma Denson #include "cx24120.h"
265afc9a25SJemma Denson
275afc9a25SJemma Denson #define CX24120_SEARCH_RANGE_KHZ 5000
285afc9a25SJemma Denson #define CX24120_FIRMWARE "dvb-fe-cx24120-1.20.58.2.fw"
295afc9a25SJemma Denson
305afc9a25SJemma Denson /* cx24120 i2c registers */
312e89a5e0SPatrick Boettcher #define CX24120_REG_CMD_START 0x00 /* write cmd_id */
322e89a5e0SPatrick Boettcher #define CX24120_REG_CMD_ARGS 0x01 /* write command arguments */
332e89a5e0SPatrick Boettcher #define CX24120_REG_CMD_END 0x1f /* write 0x01 for end */
345afc9a25SJemma Denson
352e89a5e0SPatrick Boettcher #define CX24120_REG_MAILBOX 0x33
362e89a5e0SPatrick Boettcher #define CX24120_REG_FREQ3 0x34 /* frequency */
372e89a5e0SPatrick Boettcher #define CX24120_REG_FREQ2 0x35
382e89a5e0SPatrick Boettcher #define CX24120_REG_FREQ1 0x36
395afc9a25SJemma Denson
402e89a5e0SPatrick Boettcher #define CX24120_REG_FECMODE 0x39 /* FEC status */
412e89a5e0SPatrick Boettcher #define CX24120_REG_STATUS 0x3a /* Tuner status */
422e89a5e0SPatrick Boettcher #define CX24120_REG_SIGSTR_H 0x3a /* Signal strength high */
432e89a5e0SPatrick Boettcher #define CX24120_REG_SIGSTR_L 0x3b /* Signal strength low byte */
442e89a5e0SPatrick Boettcher #define CX24120_REG_QUALITY_H 0x40 /* SNR high byte */
452e89a5e0SPatrick Boettcher #define CX24120_REG_QUALITY_L 0x41 /* SNR low byte */
465afc9a25SJemma Denson
472e89a5e0SPatrick Boettcher #define CX24120_REG_BER_HH 0x47 /* BER high byte of high word */
482e89a5e0SPatrick Boettcher #define CX24120_REG_BER_HL 0x48 /* BER low byte of high word */
492e89a5e0SPatrick Boettcher #define CX24120_REG_BER_LH 0x49 /* BER high byte of low word */
502e89a5e0SPatrick Boettcher #define CX24120_REG_BER_LL 0x4a /* BER low byte of low word */
515afc9a25SJemma Denson
522e89a5e0SPatrick Boettcher #define CX24120_REG_UCB_H 0x50 /* UCB high byte */
532e89a5e0SPatrick Boettcher #define CX24120_REG_UCB_L 0x51 /* UCB low byte */
545afc9a25SJemma Denson
552e89a5e0SPatrick Boettcher #define CX24120_REG_CLKDIV 0xe6
562e89a5e0SPatrick Boettcher #define CX24120_REG_RATEDIV 0xf0
575afc9a25SJemma Denson
582e89a5e0SPatrick Boettcher #define CX24120_REG_REVISION 0xff /* Chip revision (ro) */
595afc9a25SJemma Denson
605afc9a25SJemma Denson /* Command messages */
615afc9a25SJemma Denson enum command_message_id {
625afc9a25SJemma Denson CMD_VCO_SET = 0x10, /* cmd.len = 12; */
635afc9a25SJemma Denson CMD_TUNEREQUEST = 0x11, /* cmd.len = 15; */
645afc9a25SJemma Denson
655afc9a25SJemma Denson CMD_MPEG_ONOFF = 0x13, /* cmd.len = 4; */
665afc9a25SJemma Denson CMD_MPEG_INIT = 0x14, /* cmd.len = 7; */
675afc9a25SJemma Denson CMD_BANDWIDTH = 0x15, /* cmd.len = 12; */
685afc9a25SJemma Denson CMD_CLOCK_READ = 0x16, /* read clock */
695afc9a25SJemma Denson CMD_CLOCK_SET = 0x17, /* cmd.len = 10; */
705afc9a25SJemma Denson
715afc9a25SJemma Denson CMD_DISEQC_MSG1 = 0x20, /* cmd.len = 11; */
725afc9a25SJemma Denson CMD_DISEQC_MSG2 = 0x21, /* cmd.len = d->msg_len + 6; */
735afc9a25SJemma Denson CMD_SETVOLTAGE = 0x22, /* cmd.len = 2; */
745afc9a25SJemma Denson CMD_SETTONE = 0x23, /* cmd.len = 4; */
755afc9a25SJemma Denson CMD_DISEQC_BURST = 0x24, /* cmd.len not used !!! */
765afc9a25SJemma Denson
775afc9a25SJemma Denson CMD_READ_SNR = 0x1a, /* Read signal strength */
785afc9a25SJemma Denson CMD_START_TUNER = 0x1b, /* ??? */
795afc9a25SJemma Denson
805afc9a25SJemma Denson CMD_FWVERSION = 0x35,
815afc9a25SJemma Denson
82ddcb252eSJemma Denson CMD_BER_CTRL = 0x3c, /* cmd.len = 0x03; */
835afc9a25SJemma Denson };
845afc9a25SJemma Denson
855afc9a25SJemma Denson #define CX24120_MAX_CMD_LEN 30
865afc9a25SJemma Denson
875afc9a25SJemma Denson /* pilot mask */
882e89a5e0SPatrick Boettcher #define CX24120_PILOT_OFF 0x00
892e89a5e0SPatrick Boettcher #define CX24120_PILOT_ON 0x40
902e89a5e0SPatrick Boettcher #define CX24120_PILOT_AUTO 0x80
915afc9a25SJemma Denson
925afc9a25SJemma Denson /* signal status */
932e89a5e0SPatrick Boettcher #define CX24120_HAS_SIGNAL 0x01
942e89a5e0SPatrick Boettcher #define CX24120_HAS_CARRIER 0x02
952e89a5e0SPatrick Boettcher #define CX24120_HAS_VITERBI 0x04
962e89a5e0SPatrick Boettcher #define CX24120_HAS_LOCK 0x08
972e89a5e0SPatrick Boettcher #define CX24120_HAS_UNK1 0x10
982e89a5e0SPatrick Boettcher #define CX24120_HAS_UNK2 0x20
992e89a5e0SPatrick Boettcher #define CX24120_STATUS_MASK 0x0f
1002e89a5e0SPatrick Boettcher #define CX24120_SIGNAL_MASK 0xc0
1015afc9a25SJemma Denson
102ddcb252eSJemma Denson /* ber window */
103ddcb252eSJemma Denson #define CX24120_BER_WINDOW 16
104ddcb252eSJemma Denson #define CX24120_BER_WSIZE ((1 << CX24120_BER_WINDOW) * 208 * 8)
105ddcb252eSJemma Denson
106c5fb0f5fSPatrick Boettcher #define info(args...) pr_info("cx24120: " args)
107c5fb0f5fSPatrick Boettcher #define err(args...) pr_err("cx24120: ### ERROR: " args)
1085afc9a25SJemma Denson
1095afc9a25SJemma Denson /* The Demod/Tuner can't easily provide these, we cache them */
1105afc9a25SJemma Denson struct cx24120_tuning {
1115afc9a25SJemma Denson u32 frequency;
1125afc9a25SJemma Denson u32 symbol_rate;
1130df289a2SMauro Carvalho Chehab enum fe_spectral_inversion inversion;
1140df289a2SMauro Carvalho Chehab enum fe_code_rate fec;
1155afc9a25SJemma Denson
1160df289a2SMauro Carvalho Chehab enum fe_delivery_system delsys;
1170df289a2SMauro Carvalho Chehab enum fe_modulation modulation;
1180df289a2SMauro Carvalho Chehab enum fe_pilot pilot;
1195afc9a25SJemma Denson
1205afc9a25SJemma Denson /* Demod values */
1215afc9a25SJemma Denson u8 fec_val;
1225afc9a25SJemma Denson u8 fec_mask;
1235afc9a25SJemma Denson u8 clkdiv;
1245afc9a25SJemma Denson u8 ratediv;
1255afc9a25SJemma Denson u8 inversion_val;
1265afc9a25SJemma Denson u8 pilot_val;
1275afc9a25SJemma Denson };
1285afc9a25SJemma Denson
1295afc9a25SJemma Denson /* Private state */
1305afc9a25SJemma Denson struct cx24120_state {
1315afc9a25SJemma Denson struct i2c_adapter *i2c;
1325afc9a25SJemma Denson const struct cx24120_config *config;
1335afc9a25SJemma Denson struct dvb_frontend frontend;
1345afc9a25SJemma Denson
1355afc9a25SJemma Denson u8 cold_init;
1365afc9a25SJemma Denson u8 mpeg_enabled;
1376138dc2fSJemma Denson u8 need_clock_set;
1385afc9a25SJemma Denson
1395afc9a25SJemma Denson /* current and next tuning parameters */
1405afc9a25SJemma Denson struct cx24120_tuning dcur;
1415afc9a25SJemma Denson struct cx24120_tuning dnxt;
1421462612cSJemma Denson
1430df289a2SMauro Carvalho Chehab enum fe_status fe_status;
144ddcb252eSJemma Denson
145c9adafa3SJemma Denson /* dvbv5 stats calculations */
14680e9710bSJemma Denson u32 bitrate;
147ddcb252eSJemma Denson u32 berw_usecs;
148fc443284SJemma Denson u32 ber_prev;
149bf8de2d3SJemma Denson u32 ucb_offset;
150ddcb252eSJemma Denson unsigned long ber_jiffies_stats;
15180e9710bSJemma Denson unsigned long per_jiffies_stats;
1525afc9a25SJemma Denson };
1535afc9a25SJemma Denson
1545afc9a25SJemma Denson /* Command message to firmware */
1555afc9a25SJemma Denson struct cx24120_cmd {
1565afc9a25SJemma Denson u8 id;
1575afc9a25SJemma Denson u8 len;
1585afc9a25SJemma Denson u8 arg[CX24120_MAX_CMD_LEN];
1595afc9a25SJemma Denson };
1605afc9a25SJemma Denson
1615afc9a25SJemma Denson /* Read single register */
cx24120_readreg(struct cx24120_state * state,u8 reg)1625afc9a25SJemma Denson static int cx24120_readreg(struct cx24120_state *state, u8 reg)
1635afc9a25SJemma Denson {
1645afc9a25SJemma Denson int ret;
1655afc9a25SJemma Denson u8 buf = 0;
1665afc9a25SJemma Denson struct i2c_msg msg[] = {
167fbdbab72SJemma Denson {
168fbdbab72SJemma Denson .addr = state->config->i2c_addr,
1695afc9a25SJemma Denson .flags = 0,
1705afc9a25SJemma Denson .len = 1,
1712e89a5e0SPatrick Boettcher .buf = ®
1722e89a5e0SPatrick Boettcher }, {
1732e89a5e0SPatrick Boettcher .addr = state->config->i2c_addr,
1745afc9a25SJemma Denson .flags = I2C_M_RD,
1755afc9a25SJemma Denson .len = 1,
1762e89a5e0SPatrick Boettcher .buf = &buf
1772e89a5e0SPatrick Boettcher }
1785afc9a25SJemma Denson };
1792e89a5e0SPatrick Boettcher
1805afc9a25SJemma Denson ret = i2c_transfer(state->i2c, msg, 2);
1815afc9a25SJemma Denson if (ret != 2) {
1822e89a5e0SPatrick Boettcher err("Read error: reg=0x%02x, ret=%i)\n", reg, ret);
1835afc9a25SJemma Denson return ret;
1845afc9a25SJemma Denson }
1855afc9a25SJemma Denson
1862f3f07fbSJemma Denson dev_dbg(&state->i2c->dev, "reg=0x%02x; data=0x%02x\n", reg, buf);
1875afc9a25SJemma Denson
1885afc9a25SJemma Denson return buf;
1895afc9a25SJemma Denson }
1905afc9a25SJemma Denson
1915afc9a25SJemma Denson /* Write single register */
cx24120_writereg(struct cx24120_state * state,u8 reg,u8 data)1925afc9a25SJemma Denson static int cx24120_writereg(struct cx24120_state *state, u8 reg, u8 data)
1935afc9a25SJemma Denson {
1945afc9a25SJemma Denson u8 buf[] = { reg, data };
1955afc9a25SJemma Denson struct i2c_msg msg = {
1965afc9a25SJemma Denson .addr = state->config->i2c_addr,
1975afc9a25SJemma Denson .flags = 0,
1985afc9a25SJemma Denson .buf = buf,
1992e89a5e0SPatrick Boettcher .len = 2
2002e89a5e0SPatrick Boettcher };
2015afc9a25SJemma Denson int ret;
2025afc9a25SJemma Denson
2035afc9a25SJemma Denson ret = i2c_transfer(state->i2c, &msg, 1);
2045afc9a25SJemma Denson if (ret != 1) {
2055afc9a25SJemma Denson err("Write error: i2c_write error(err == %i, 0x%02x: 0x%02x)\n",
2065afc9a25SJemma Denson ret, reg, data);
2075afc9a25SJemma Denson return ret;
2085afc9a25SJemma Denson }
2095afc9a25SJemma Denson
2102f3f07fbSJemma Denson dev_dbg(&state->i2c->dev, "reg=0x%02x; data=0x%02x\n", reg, data);
2115afc9a25SJemma Denson
2125afc9a25SJemma Denson return 0;
2135afc9a25SJemma Denson }
2145afc9a25SJemma Denson
215f7a77ebfSPatrick Boettcher /* Write multiple registers in chunks of i2c_wr_max-sized buffers */
cx24120_writeregs(struct cx24120_state * state,u8 reg,const u8 * values,u16 len,u8 incr)2161ff2e8edSPatrick Boettcher static int cx24120_writeregs(struct cx24120_state *state,
2175afc9a25SJemma Denson u8 reg, const u8 *values, u16 len, u8 incr)
2185afc9a25SJemma Denson {
2195afc9a25SJemma Denson int ret;
220f7a77ebfSPatrick Boettcher u16 max = state->config->i2c_wr_max > 0 ?
221f7a77ebfSPatrick Boettcher state->config->i2c_wr_max :
222f7a77ebfSPatrick Boettcher len;
2235afc9a25SJemma Denson
2245afc9a25SJemma Denson struct i2c_msg msg = {
2255afc9a25SJemma Denson .addr = state->config->i2c_addr,
2265afc9a25SJemma Denson .flags = 0,
227f7a77ebfSPatrick Boettcher };
228f7a77ebfSPatrick Boettcher
229f7a77ebfSPatrick Boettcher msg.buf = kmalloc(max + 1, GFP_KERNEL);
2301ff2e8edSPatrick Boettcher if (!msg.buf)
231f7a77ebfSPatrick Boettcher return -ENOMEM;
2325afc9a25SJemma Denson
2335afc9a25SJemma Denson while (len) {
234f7a77ebfSPatrick Boettcher msg.buf[0] = reg;
235f7a77ebfSPatrick Boettcher msg.len = len > max ? max : len;
236f7a77ebfSPatrick Boettcher memcpy(&msg.buf[1], values, msg.len);
2375afc9a25SJemma Denson
2385afc9a25SJemma Denson len -= msg.len; /* data length revers counter */
2395afc9a25SJemma Denson values += msg.len; /* incr data pointer */
2405afc9a25SJemma Denson
2415afc9a25SJemma Denson if (incr)
2425afc9a25SJemma Denson reg += msg.len;
2435afc9a25SJemma Denson msg.len++; /* don't forget the addr byte */
2445afc9a25SJemma Denson
2455afc9a25SJemma Denson ret = i2c_transfer(state->i2c, &msg, 1);
2465afc9a25SJemma Denson if (ret != 1) {
2475afc9a25SJemma Denson err("i2c_write error(err == %i, 0x%02x)\n", ret, reg);
248f7a77ebfSPatrick Boettcher goto out;
2495afc9a25SJemma Denson }
2505afc9a25SJemma Denson
2512f3f07fbSJemma Denson dev_dbg(&state->i2c->dev, "reg=0x%02x; data=%*ph\n",
2522f3f07fbSJemma Denson reg, msg.len - 1, msg.buf + 1);
2535afc9a25SJemma Denson }
2545afc9a25SJemma Denson
255f7a77ebfSPatrick Boettcher ret = 0;
256f7a77ebfSPatrick Boettcher
257f7a77ebfSPatrick Boettcher out:
258f7a77ebfSPatrick Boettcher kfree(msg.buf);
259f7a77ebfSPatrick Boettcher return ret;
2605afc9a25SJemma Denson }
2615afc9a25SJemma Denson
262bd336e63SMax Kellermann static const struct dvb_frontend_ops cx24120_ops;
2635afc9a25SJemma Denson
cx24120_attach(const struct cx24120_config * config,struct i2c_adapter * i2c)2645afc9a25SJemma Denson struct dvb_frontend *cx24120_attach(const struct cx24120_config *config,
2655afc9a25SJemma Denson struct i2c_adapter *i2c)
2665afc9a25SJemma Denson {
2671ff2e8edSPatrick Boettcher struct cx24120_state *state;
2685afc9a25SJemma Denson int demod_rev;
2695afc9a25SJemma Denson
2705afc9a25SJemma Denson info("Conexant cx24120/cx24118 - DVBS/S2 Satellite demod/tuner\n");
2711ff2e8edSPatrick Boettcher state = kzalloc(sizeof(*state), GFP_KERNEL);
2721ff2e8edSPatrick Boettcher if (!state) {
2735afc9a25SJemma Denson err("Unable to allocate memory for cx24120_state\n");
2745afc9a25SJemma Denson goto error;
2755afc9a25SJemma Denson }
2765afc9a25SJemma Denson
2775afc9a25SJemma Denson /* setup the state */
2785afc9a25SJemma Denson state->config = config;
2795afc9a25SJemma Denson state->i2c = i2c;
2805afc9a25SJemma Denson
2815afc9a25SJemma Denson /* check if the demod is present and has proper type */
2825afc9a25SJemma Denson demod_rev = cx24120_readreg(state, CX24120_REG_REVISION);
2835afc9a25SJemma Denson switch (demod_rev) {
2845afc9a25SJemma Denson case 0x07:
2855afc9a25SJemma Denson info("Demod cx24120 rev. 0x07 detected.\n");
2865afc9a25SJemma Denson break;
2875afc9a25SJemma Denson case 0x05:
2885afc9a25SJemma Denson info("Demod cx24120 rev. 0x05 detected.\n");
2895afc9a25SJemma Denson break;
2905afc9a25SJemma Denson default:
2911ff2e8edSPatrick Boettcher err("Unsupported demod revision: 0x%x detected.\n", demod_rev);
2925afc9a25SJemma Denson goto error;
2935afc9a25SJemma Denson }
2945afc9a25SJemma Denson
2955afc9a25SJemma Denson /* create dvb_frontend */
2965afc9a25SJemma Denson state->cold_init = 0;
2975afc9a25SJemma Denson memcpy(&state->frontend.ops, &cx24120_ops,
2985afc9a25SJemma Denson sizeof(struct dvb_frontend_ops));
2995afc9a25SJemma Denson state->frontend.demodulator_priv = state;
3005afc9a25SJemma Denson
3015afc9a25SJemma Denson info("Conexant cx24120/cx24118 attached.\n");
3025afc9a25SJemma Denson return &state->frontend;
3035afc9a25SJemma Denson
3045afc9a25SJemma Denson error:
3055afc9a25SJemma Denson kfree(state);
3065afc9a25SJemma Denson return NULL;
3075afc9a25SJemma Denson }
308*86495af1SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(cx24120_attach);
3095afc9a25SJemma Denson
cx24120_test_rom(struct cx24120_state * state)3105afc9a25SJemma Denson static int cx24120_test_rom(struct cx24120_state *state)
3115afc9a25SJemma Denson {
3125afc9a25SJemma Denson int err, ret;
3135afc9a25SJemma Denson
3145afc9a25SJemma Denson err = cx24120_readreg(state, 0xfd);
3155afc9a25SJemma Denson if (err & 4) {
3165afc9a25SJemma Denson ret = cx24120_readreg(state, 0xdf) & 0xfe;
3175afc9a25SJemma Denson err = cx24120_writereg(state, 0xdf, ret);
3185afc9a25SJemma Denson }
3195afc9a25SJemma Denson return err;
3205afc9a25SJemma Denson }
3215afc9a25SJemma Denson
cx24120_read_snr(struct dvb_frontend * fe,u16 * snr)3225afc9a25SJemma Denson static int cx24120_read_snr(struct dvb_frontend *fe, u16 *snr)
3235afc9a25SJemma Denson {
3243b5eb504SJemma Denson struct dtv_frontend_properties *c = &fe->dtv_property_cache;
3255afc9a25SJemma Denson
3263b5eb504SJemma Denson if (c->cnr.stat[0].scale != FE_SCALE_DECIBEL)
3273b5eb504SJemma Denson *snr = 0;
3283b5eb504SJemma Denson else
3293b5eb504SJemma Denson *snr = div_s64(c->cnr.stat[0].svalue, 100);
3305afc9a25SJemma Denson
3315afc9a25SJemma Denson return 0;
3325afc9a25SJemma Denson }
3335afc9a25SJemma Denson
cx24120_read_ber(struct dvb_frontend * fe,u32 * ber)3345afc9a25SJemma Denson static int cx24120_read_ber(struct dvb_frontend *fe, u32 *ber)
3355afc9a25SJemma Denson {
3365afc9a25SJemma Denson struct cx24120_state *state = fe->demodulator_priv;
337fc443284SJemma Denson struct dtv_frontend_properties *c = &fe->dtv_property_cache;
3385afc9a25SJemma Denson
339fc443284SJemma Denson if (c->post_bit_error.stat[0].scale != FE_SCALE_COUNTER) {
340fc443284SJemma Denson *ber = 0;
341fc443284SJemma Denson return 0;
342fc443284SJemma Denson }
343fc443284SJemma Denson
344fc443284SJemma Denson *ber = c->post_bit_error.stat[0].uvalue - state->ber_prev;
345fc443284SJemma Denson state->ber_prev = c->post_bit_error.stat[0].uvalue;
3465afc9a25SJemma Denson
3475afc9a25SJemma Denson return 0;
3485afc9a25SJemma Denson }
3495afc9a25SJemma Denson
3505afc9a25SJemma Denson static int cx24120_msg_mpeg_output_global_config(struct cx24120_state *state,
3515afc9a25SJemma Denson u8 flag);
3525afc9a25SJemma Denson
3535afc9a25SJemma Denson /* Check if we're running a command that needs to disable mpeg out */
cx24120_check_cmd(struct cx24120_state * state,u8 id)3545afc9a25SJemma Denson static void cx24120_check_cmd(struct cx24120_state *state, u8 id)
3555afc9a25SJemma Denson {
3565afc9a25SJemma Denson switch (id) {
3575afc9a25SJemma Denson case CMD_TUNEREQUEST:
3585afc9a25SJemma Denson case CMD_CLOCK_READ:
3595afc9a25SJemma Denson case CMD_DISEQC_MSG1:
3605afc9a25SJemma Denson case CMD_DISEQC_MSG2:
3615afc9a25SJemma Denson case CMD_SETVOLTAGE:
3625afc9a25SJemma Denson case CMD_SETTONE:
363270e7071SJemma Denson case CMD_DISEQC_BURST:
3645afc9a25SJemma Denson cx24120_msg_mpeg_output_global_config(state, 0);
3655afc9a25SJemma Denson /* Old driver would do a msleep(100) here */
366af7ab662SGustavo A. R. Silva return;
3675afc9a25SJemma Denson default:
3685afc9a25SJemma Denson return;
3695afc9a25SJemma Denson }
3705afc9a25SJemma Denson }
3715afc9a25SJemma Denson
3725afc9a25SJemma Denson /* Send a message to the firmware */
cx24120_message_send(struct cx24120_state * state,struct cx24120_cmd * cmd)3735afc9a25SJemma Denson static int cx24120_message_send(struct cx24120_state *state,
3745afc9a25SJemma Denson struct cx24120_cmd *cmd)
3755afc9a25SJemma Denson {
37665b01665SMauro Carvalho Chehab int ficus;
3775afc9a25SJemma Denson
3785afc9a25SJemma Denson if (state->mpeg_enabled) {
3795afc9a25SJemma Denson /* Disable mpeg out on certain commands */
3805afc9a25SJemma Denson cx24120_check_cmd(state, cmd->id);
3815afc9a25SJemma Denson }
3825afc9a25SJemma Denson
38365b01665SMauro Carvalho Chehab cx24120_writereg(state, CX24120_REG_CMD_START, cmd->id);
38465b01665SMauro Carvalho Chehab cx24120_writeregs(state, CX24120_REG_CMD_ARGS, &cmd->arg[0],
3855afc9a25SJemma Denson cmd->len, 1);
38665b01665SMauro Carvalho Chehab cx24120_writereg(state, CX24120_REG_CMD_END, 0x01);
3875afc9a25SJemma Denson
3885afc9a25SJemma Denson ficus = 1000;
3895afc9a25SJemma Denson while (cx24120_readreg(state, CX24120_REG_CMD_END)) {
3905afc9a25SJemma Denson msleep(20);
3915afc9a25SJemma Denson ficus -= 20;
3925afc9a25SJemma Denson if (ficus == 0) {
3935afc9a25SJemma Denson err("Error sending message to firmware\n");
3945afc9a25SJemma Denson return -EREMOTEIO;
3955afc9a25SJemma Denson }
3965afc9a25SJemma Denson }
3972f3f07fbSJemma Denson dev_dbg(&state->i2c->dev, "sent message 0x%02x\n", cmd->id);
3985afc9a25SJemma Denson
3995afc9a25SJemma Denson return 0;
4005afc9a25SJemma Denson }
4015afc9a25SJemma Denson
4025afc9a25SJemma Denson /* Send a message and fill arg[] with the results */
cx24120_message_sendrcv(struct cx24120_state * state,struct cx24120_cmd * cmd,u8 numreg)4035afc9a25SJemma Denson static int cx24120_message_sendrcv(struct cx24120_state *state,
4045afc9a25SJemma Denson struct cx24120_cmd *cmd, u8 numreg)
4055afc9a25SJemma Denson {
4065afc9a25SJemma Denson int ret, i;
4075afc9a25SJemma Denson
4085afc9a25SJemma Denson if (numreg > CX24120_MAX_CMD_LEN) {
4095afc9a25SJemma Denson err("Too many registers to read. cmd->reg = %d", numreg);
4105afc9a25SJemma Denson return -EREMOTEIO;
4115afc9a25SJemma Denson }
4125afc9a25SJemma Denson
4135afc9a25SJemma Denson ret = cx24120_message_send(state, cmd);
4145afc9a25SJemma Denson if (ret != 0)
4155afc9a25SJemma Denson return ret;
4165afc9a25SJemma Denson
4175afc9a25SJemma Denson if (!numreg)
4185afc9a25SJemma Denson return 0;
4195afc9a25SJemma Denson
4205afc9a25SJemma Denson /* Read numreg registers starting from register cmd->len */
4215afc9a25SJemma Denson for (i = 0; i < numreg; i++)
4225afc9a25SJemma Denson cmd->arg[i] = cx24120_readreg(state, (cmd->len + i + 1));
4235afc9a25SJemma Denson
4245afc9a25SJemma Denson return 0;
4255afc9a25SJemma Denson }
4265afc9a25SJemma Denson
cx24120_read_signal_strength(struct dvb_frontend * fe,u16 * signal_strength)4275afc9a25SJemma Denson static int cx24120_read_signal_strength(struct dvb_frontend *fe,
4285afc9a25SJemma Denson u16 *signal_strength)
4295afc9a25SJemma Denson {
43034ce475dSJemma Denson struct dtv_frontend_properties *c = &fe->dtv_property_cache;
4315afc9a25SJemma Denson
43234ce475dSJemma Denson if (c->strength.stat[0].scale != FE_SCALE_RELATIVE)
43334ce475dSJemma Denson *signal_strength = 0;
43434ce475dSJemma Denson else
43534ce475dSJemma Denson *signal_strength = c->strength.stat[0].uvalue;
4365afc9a25SJemma Denson
4375afc9a25SJemma Denson return 0;
4385afc9a25SJemma Denson }
4395afc9a25SJemma Denson
cx24120_msg_mpeg_output_global_config(struct cx24120_state * state,u8 enable)4405afc9a25SJemma Denson static int cx24120_msg_mpeg_output_global_config(struct cx24120_state *state,
4415afc9a25SJemma Denson u8 enable)
4425afc9a25SJemma Denson {
4435afc9a25SJemma Denson struct cx24120_cmd cmd;
4445afc9a25SJemma Denson int ret;
4455afc9a25SJemma Denson
4465afc9a25SJemma Denson cmd.id = CMD_MPEG_ONOFF;
4475afc9a25SJemma Denson cmd.len = 4;
4485afc9a25SJemma Denson cmd.arg[0] = 0x01;
4495afc9a25SJemma Denson cmd.arg[1] = 0x00;
4505afc9a25SJemma Denson cmd.arg[2] = enable ? 0 : (u8)(-1);
4515afc9a25SJemma Denson cmd.arg[3] = 0x01;
4525afc9a25SJemma Denson
4535afc9a25SJemma Denson ret = cx24120_message_send(state, &cmd);
4545afc9a25SJemma Denson if (ret != 0) {
4552f3f07fbSJemma Denson dev_dbg(&state->i2c->dev, "failed to %s MPEG output\n",
4562f3f07fbSJemma Denson enable ? "enable" : "disable");
4575afc9a25SJemma Denson return ret;
4585afc9a25SJemma Denson }
4595afc9a25SJemma Denson
4605afc9a25SJemma Denson state->mpeg_enabled = enable;
4612f3f07fbSJemma Denson dev_dbg(&state->i2c->dev, "MPEG output %s\n",
4622f3f07fbSJemma Denson enable ? "enabled" : "disabled");
4635afc9a25SJemma Denson
4645afc9a25SJemma Denson return 0;
4655afc9a25SJemma Denson }
4665afc9a25SJemma Denson
cx24120_msg_mpeg_output_config(struct cx24120_state * state,u8 seq)4675afc9a25SJemma Denson static int cx24120_msg_mpeg_output_config(struct cx24120_state *state, u8 seq)
4685afc9a25SJemma Denson {
4695afc9a25SJemma Denson struct cx24120_cmd cmd;
4705afc9a25SJemma Denson struct cx24120_initial_mpeg_config i =
4715afc9a25SJemma Denson state->config->initial_mpeg_config;
4725afc9a25SJemma Denson
4735afc9a25SJemma Denson cmd.id = CMD_MPEG_INIT;
4745afc9a25SJemma Denson cmd.len = 7;
4755afc9a25SJemma Denson cmd.arg[0] = seq; /* sequental number - can be 0,1,2 */
4765afc9a25SJemma Denson cmd.arg[1] = ((i.x1 & 0x01) << 1) | ((i.x1 >> 1) & 0x01);
4775afc9a25SJemma Denson cmd.arg[2] = 0x05;
4785afc9a25SJemma Denson cmd.arg[3] = 0x02;
4795afc9a25SJemma Denson cmd.arg[4] = ((i.x2 >> 1) & 0x01);
4805afc9a25SJemma Denson cmd.arg[5] = (i.x2 & 0xf0) | (i.x3 & 0x0f);
4815afc9a25SJemma Denson cmd.arg[6] = 0x10;
4825afc9a25SJemma Denson
4835afc9a25SJemma Denson return cx24120_message_send(state, &cmd);
4845afc9a25SJemma Denson }
4855afc9a25SJemma Denson
cx24120_diseqc_send_burst(struct dvb_frontend * fe,enum fe_sec_mini_cmd burst)4865afc9a25SJemma Denson static int cx24120_diseqc_send_burst(struct dvb_frontend *fe,
4870df289a2SMauro Carvalho Chehab enum fe_sec_mini_cmd burst)
4885afc9a25SJemma Denson {
4895afc9a25SJemma Denson struct cx24120_state *state = fe->demodulator_priv;
4905afc9a25SJemma Denson struct cx24120_cmd cmd;
4915afc9a25SJemma Denson
4922f3f07fbSJemma Denson dev_dbg(&state->i2c->dev, "\n");
4932f3f07fbSJemma Denson
494fbdbab72SJemma Denson /*
495fbdbab72SJemma Denson * Yes, cmd.len is set to zero. The old driver
4965afc9a25SJemma Denson * didn't specify any len, but also had a
4975afc9a25SJemma Denson * memset 0 before every use of the cmd struct
4985afc9a25SJemma Denson * which would have set it to zero.
4995afc9a25SJemma Denson * This quite probably needs looking into.
5005afc9a25SJemma Denson */
5015afc9a25SJemma Denson cmd.id = CMD_DISEQC_BURST;
5025afc9a25SJemma Denson cmd.len = 0;
5035afc9a25SJemma Denson cmd.arg[0] = 0x00;
5047c95e25eSJemma Denson cmd.arg[1] = (burst == SEC_MINI_B) ? 0x01 : 0x00;
5051ff2e8edSPatrick Boettcher
5065afc9a25SJemma Denson return cx24120_message_send(state, &cmd);
5075afc9a25SJemma Denson }
5085afc9a25SJemma Denson
cx24120_set_tone(struct dvb_frontend * fe,enum fe_sec_tone_mode tone)5090df289a2SMauro Carvalho Chehab static int cx24120_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone)
5105afc9a25SJemma Denson {
5115afc9a25SJemma Denson struct cx24120_state *state = fe->demodulator_priv;
5125afc9a25SJemma Denson struct cx24120_cmd cmd;
5135afc9a25SJemma Denson
5142f3f07fbSJemma Denson dev_dbg(&state->i2c->dev, "(%d)\n", tone);
5155afc9a25SJemma Denson
5165afc9a25SJemma Denson if ((tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF)) {
5175afc9a25SJemma Denson err("Invalid tone=%d\n", tone);
5185afc9a25SJemma Denson return -EINVAL;
5195afc9a25SJemma Denson }
5205afc9a25SJemma Denson
5215afc9a25SJemma Denson cmd.id = CMD_SETTONE;
5225afc9a25SJemma Denson cmd.len = 4;
5235afc9a25SJemma Denson cmd.arg[0] = 0x00;
5245afc9a25SJemma Denson cmd.arg[1] = 0x00;
5255afc9a25SJemma Denson cmd.arg[2] = 0x00;
5265afc9a25SJemma Denson cmd.arg[3] = (tone == SEC_TONE_ON) ? 0x01 : 0x00;
5275afc9a25SJemma Denson
5285afc9a25SJemma Denson return cx24120_message_send(state, &cmd);
5295afc9a25SJemma Denson }
5305afc9a25SJemma Denson
cx24120_set_voltage(struct dvb_frontend * fe,enum fe_sec_voltage voltage)5315afc9a25SJemma Denson static int cx24120_set_voltage(struct dvb_frontend *fe,
5320df289a2SMauro Carvalho Chehab enum fe_sec_voltage voltage)
5335afc9a25SJemma Denson {
5345afc9a25SJemma Denson struct cx24120_state *state = fe->demodulator_priv;
5355afc9a25SJemma Denson struct cx24120_cmd cmd;
5365afc9a25SJemma Denson
5372f3f07fbSJemma Denson dev_dbg(&state->i2c->dev, "(%d)\n", voltage);
5385afc9a25SJemma Denson
5395afc9a25SJemma Denson cmd.id = CMD_SETVOLTAGE;
5405afc9a25SJemma Denson cmd.len = 2;
5415afc9a25SJemma Denson cmd.arg[0] = 0x00;
5425afc9a25SJemma Denson cmd.arg[1] = (voltage == SEC_VOLTAGE_18) ? 0x01 : 0x00;
5435afc9a25SJemma Denson
5445afc9a25SJemma Denson return cx24120_message_send(state, &cmd);
5455afc9a25SJemma Denson }
5465afc9a25SJemma Denson
cx24120_send_diseqc_msg(struct dvb_frontend * fe,struct dvb_diseqc_master_cmd * d)5475afc9a25SJemma Denson static int cx24120_send_diseqc_msg(struct dvb_frontend *fe,
5485afc9a25SJemma Denson struct dvb_diseqc_master_cmd *d)
5495afc9a25SJemma Denson {
5505afc9a25SJemma Denson struct cx24120_state *state = fe->demodulator_priv;
5515afc9a25SJemma Denson struct cx24120_cmd cmd;
5525afc9a25SJemma Denson int back_count;
5535afc9a25SJemma Denson
5542f3f07fbSJemma Denson dev_dbg(&state->i2c->dev, "\n");
5555afc9a25SJemma Denson
5565afc9a25SJemma Denson cmd.id = CMD_DISEQC_MSG1;
5575afc9a25SJemma Denson cmd.len = 11;
5585afc9a25SJemma Denson cmd.arg[0] = 0x00;
5595afc9a25SJemma Denson cmd.arg[1] = 0x00;
5605afc9a25SJemma Denson cmd.arg[2] = 0x03;
5615afc9a25SJemma Denson cmd.arg[3] = 0x16;
5625afc9a25SJemma Denson cmd.arg[4] = 0x28;
5635afc9a25SJemma Denson cmd.arg[5] = 0x01;
5645afc9a25SJemma Denson cmd.arg[6] = 0x01;
5655afc9a25SJemma Denson cmd.arg[7] = 0x14;
5665afc9a25SJemma Denson cmd.arg[8] = 0x19;
5675afc9a25SJemma Denson cmd.arg[9] = 0x14;
5685afc9a25SJemma Denson cmd.arg[10] = 0x1e;
5695afc9a25SJemma Denson
5705afc9a25SJemma Denson if (cx24120_message_send(state, &cmd)) {
5715afc9a25SJemma Denson err("send 1st message(0x%x) failed\n", cmd.id);
5725afc9a25SJemma Denson return -EREMOTEIO;
5735afc9a25SJemma Denson }
5745afc9a25SJemma Denson
5755afc9a25SJemma Denson cmd.id = CMD_DISEQC_MSG2;
5765afc9a25SJemma Denson cmd.len = d->msg_len + 6;
5775afc9a25SJemma Denson cmd.arg[0] = 0x00;
5785afc9a25SJemma Denson cmd.arg[1] = 0x01;
5795afc9a25SJemma Denson cmd.arg[2] = 0x02;
5805afc9a25SJemma Denson cmd.arg[3] = 0x00;
5815afc9a25SJemma Denson cmd.arg[4] = 0x00;
5825afc9a25SJemma Denson cmd.arg[5] = d->msg_len;
5835afc9a25SJemma Denson
5845afc9a25SJemma Denson memcpy(&cmd.arg[6], &d->msg, d->msg_len);
5855afc9a25SJemma Denson
5865afc9a25SJemma Denson if (cx24120_message_send(state, &cmd)) {
5875afc9a25SJemma Denson err("send 2nd message(0x%x) failed\n", cmd.id);
5885afc9a25SJemma Denson return -EREMOTEIO;
5895afc9a25SJemma Denson }
5905afc9a25SJemma Denson
5915afc9a25SJemma Denson back_count = 500;
5925afc9a25SJemma Denson do {
5935afc9a25SJemma Denson if (!(cx24120_readreg(state, 0x93) & 0x01)) {
5942f3f07fbSJemma Denson dev_dbg(&state->i2c->dev, "diseqc sequence sent\n");
5955afc9a25SJemma Denson return 0;
5965afc9a25SJemma Denson }
5975afc9a25SJemma Denson msleep(20);
5985afc9a25SJemma Denson back_count -= 20;
5995afc9a25SJemma Denson } while (back_count);
6005afc9a25SJemma Denson
6015afc9a25SJemma Denson err("Too long waiting for diseqc.\n");
6025afc9a25SJemma Denson return -ETIMEDOUT;
6035afc9a25SJemma Denson }
6045afc9a25SJemma Denson
cx24120_get_stats(struct cx24120_state * state)6051462612cSJemma Denson static void cx24120_get_stats(struct cx24120_state *state)
6069fc18f18SJemma Denson {
6079fc18f18SJemma Denson struct dvb_frontend *fe = &state->frontend;
6089fc18f18SJemma Denson struct dtv_frontend_properties *c = &fe->dtv_property_cache;
60934ce475dSJemma Denson struct cx24120_cmd cmd;
610ddcb252eSJemma Denson int ret, cnr, msecs;
61180e9710bSJemma Denson u16 sig, ucb;
612ddcb252eSJemma Denson u32 ber;
6139fc18f18SJemma Denson
6142f3f07fbSJemma Denson dev_dbg(&state->i2c->dev, "\n");
6159fc18f18SJemma Denson
6169fc18f18SJemma Denson /* signal strength */
6171462612cSJemma Denson if (state->fe_status & FE_HAS_SIGNAL) {
61834ce475dSJemma Denson cmd.id = CMD_READ_SNR;
61934ce475dSJemma Denson cmd.len = 1;
62034ce475dSJemma Denson cmd.arg[0] = 0x00;
62134ce475dSJemma Denson
62234ce475dSJemma Denson ret = cx24120_message_send(state, &cmd);
62334ce475dSJemma Denson if (ret != 0) {
62434ce475dSJemma Denson err("error reading signal strength\n");
6259fc18f18SJemma Denson return;
62634ce475dSJemma Denson }
62734ce475dSJemma Denson
62834ce475dSJemma Denson /* raw */
629b0cdf1a1SJemma Denson sig = cx24120_readreg(state, CX24120_REG_SIGSTR_H) >> 6;
630b0cdf1a1SJemma Denson sig = sig << 8;
631b0cdf1a1SJemma Denson sig |= cx24120_readreg(state, CX24120_REG_SIGSTR_L);
632b0cdf1a1SJemma Denson dev_dbg(&state->i2c->dev,
6332f3f07fbSJemma Denson "signal strength from firmware = 0x%x\n", sig);
63434ce475dSJemma Denson
63534ce475dSJemma Denson /* cooked */
636b0cdf1a1SJemma Denson sig = -100 * sig + 94324;
6379fc18f18SJemma Denson
6389fc18f18SJemma Denson c->strength.stat[0].scale = FE_SCALE_RELATIVE;
639b0cdf1a1SJemma Denson c->strength.stat[0].uvalue = sig;
6409fc18f18SJemma Denson } else {
6419fc18f18SJemma Denson c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
6429fc18f18SJemma Denson }
6439fc18f18SJemma Denson
6443b5eb504SJemma Denson /* CNR */
6453b5eb504SJemma Denson if (state->fe_status & FE_HAS_VITERBI) {
6463b5eb504SJemma Denson cnr = cx24120_readreg(state, CX24120_REG_QUALITY_H) << 8;
6473b5eb504SJemma Denson cnr |= cx24120_readreg(state, CX24120_REG_QUALITY_L);
6482f3f07fbSJemma Denson dev_dbg(&state->i2c->dev, "read SNR index = %d\n", cnr);
6493b5eb504SJemma Denson
6503b5eb504SJemma Denson /* guessed - seems about right */
6513b5eb504SJemma Denson cnr = cnr * 100;
6523b5eb504SJemma Denson
6533b5eb504SJemma Denson c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
6543b5eb504SJemma Denson c->cnr.stat[0].svalue = cnr;
6553b5eb504SJemma Denson } else {
6563b5eb504SJemma Denson c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
6573b5eb504SJemma Denson }
6589fc18f18SJemma Denson
659e3f2f63eSJemma Denson /* BER & UCB require lock */
660e3f2f63eSJemma Denson if (!(state->fe_status & FE_HAS_LOCK)) {
661e3f2f63eSJemma Denson c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
662e3f2f63eSJemma Denson c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
663e3f2f63eSJemma Denson c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
664e3f2f63eSJemma Denson c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
665e3f2f63eSJemma Denson return;
666e3f2f63eSJemma Denson }
667e3f2f63eSJemma Denson
668ddcb252eSJemma Denson /* BER */
669ddcb252eSJemma Denson if (time_after(jiffies, state->ber_jiffies_stats)) {
670ddcb252eSJemma Denson msecs = (state->berw_usecs + 500) / 1000;
671ddcb252eSJemma Denson state->ber_jiffies_stats = jiffies + msecs_to_jiffies(msecs);
672ddcb252eSJemma Denson
673fc443284SJemma Denson ber = cx24120_readreg(state, CX24120_REG_BER_HH) << 24;
674fc443284SJemma Denson ber |= cx24120_readreg(state, CX24120_REG_BER_HL) << 16;
675fc443284SJemma Denson ber |= cx24120_readreg(state, CX24120_REG_BER_LH) << 8;
676fc443284SJemma Denson ber |= cx24120_readreg(state, CX24120_REG_BER_LL);
677fc443284SJemma Denson dev_dbg(&state->i2c->dev, "read BER index = %d\n", ber);
678ddcb252eSJemma Denson
679ddcb252eSJemma Denson c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
680ddcb252eSJemma Denson c->post_bit_error.stat[0].uvalue += ber;
681ddcb252eSJemma Denson
682ddcb252eSJemma Denson c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
683ddcb252eSJemma Denson c->post_bit_count.stat[0].uvalue += CX24120_BER_WSIZE;
684ddcb252eSJemma Denson }
685ddcb252eSJemma Denson
68680e9710bSJemma Denson /* UCB */
68780e9710bSJemma Denson if (time_after(jiffies, state->per_jiffies_stats)) {
68880e9710bSJemma Denson state->per_jiffies_stats = jiffies + msecs_to_jiffies(1000);
68980e9710bSJemma Denson
69080e9710bSJemma Denson ucb = cx24120_readreg(state, CX24120_REG_UCB_H) << 8;
69180e9710bSJemma Denson ucb |= cx24120_readreg(state, CX24120_REG_UCB_L);
69280e9710bSJemma Denson dev_dbg(&state->i2c->dev, "ucblocks = %d\n", ucb);
69380e9710bSJemma Denson
694bf8de2d3SJemma Denson /* handle reset */
695bf8de2d3SJemma Denson if (ucb < state->ucb_offset)
696bf8de2d3SJemma Denson state->ucb_offset = c->block_error.stat[0].uvalue;
697bf8de2d3SJemma Denson
69880e9710bSJemma Denson c->block_error.stat[0].scale = FE_SCALE_COUNTER;
699bf8de2d3SJemma Denson c->block_error.stat[0].uvalue = ucb + state->ucb_offset;
70080e9710bSJemma Denson
70180e9710bSJemma Denson c->block_count.stat[0].scale = FE_SCALE_COUNTER;
70280e9710bSJemma Denson c->block_count.stat[0].uvalue += state->bitrate / 8 / 208;
70380e9710bSJemma Denson }
7049fc18f18SJemma Denson }
7059fc18f18SJemma Denson
7066138dc2fSJemma Denson static void cx24120_set_clock_ratios(struct dvb_frontend *fe);
7076138dc2fSJemma Denson
7085afc9a25SJemma Denson /* Read current tuning status */
cx24120_read_status(struct dvb_frontend * fe,enum fe_status * status)7090df289a2SMauro Carvalho Chehab static int cx24120_read_status(struct dvb_frontend *fe, enum fe_status *status)
7105afc9a25SJemma Denson {
7115afc9a25SJemma Denson struct cx24120_state *state = fe->demodulator_priv;
7125afc9a25SJemma Denson int lock;
7135afc9a25SJemma Denson
7145afc9a25SJemma Denson lock = cx24120_readreg(state, CX24120_REG_STATUS);
7155afc9a25SJemma Denson
7162f3f07fbSJemma Denson dev_dbg(&state->i2c->dev, "status = 0x%02x\n", lock);
7175afc9a25SJemma Denson
7185afc9a25SJemma Denson *status = 0;
7195afc9a25SJemma Denson
7205afc9a25SJemma Denson if (lock & CX24120_HAS_SIGNAL)
7215afc9a25SJemma Denson *status = FE_HAS_SIGNAL;
7225afc9a25SJemma Denson if (lock & CX24120_HAS_CARRIER)
7235afc9a25SJemma Denson *status |= FE_HAS_CARRIER;
7245afc9a25SJemma Denson if (lock & CX24120_HAS_VITERBI)
7255afc9a25SJemma Denson *status |= FE_HAS_VITERBI | FE_HAS_SYNC;
7265afc9a25SJemma Denson if (lock & CX24120_HAS_LOCK)
7275afc9a25SJemma Denson *status |= FE_HAS_LOCK;
7285afc9a25SJemma Denson
729fbdbab72SJemma Denson /*
730fbdbab72SJemma Denson * TODO: is FE_HAS_SYNC in the right place?
7315afc9a25SJemma Denson * Other cx241xx drivers have this slightly
732fbdbab72SJemma Denson * different
733fbdbab72SJemma Denson */
7345afc9a25SJemma Denson
7351462612cSJemma Denson state->fe_status = *status;
7361462612cSJemma Denson cx24120_get_stats(state);
7379fc18f18SJemma Denson
7386138dc2fSJemma Denson /* Set the clock once tuned in */
7396138dc2fSJemma Denson if (state->need_clock_set && *status & FE_HAS_LOCK) {
7406138dc2fSJemma Denson /* Set clock ratios */
7416138dc2fSJemma Denson cx24120_set_clock_ratios(fe);
7426138dc2fSJemma Denson
7436138dc2fSJemma Denson /* Old driver would do a msleep(200) here */
7446138dc2fSJemma Denson
7456138dc2fSJemma Denson /* Renable mpeg output */
7466138dc2fSJemma Denson if (!state->mpeg_enabled)
7476138dc2fSJemma Denson cx24120_msg_mpeg_output_global_config(state, 1);
7486138dc2fSJemma Denson
7496138dc2fSJemma Denson state->need_clock_set = 0;
7506138dc2fSJemma Denson }
7516138dc2fSJemma Denson
7525afc9a25SJemma Denson return 0;
7535afc9a25SJemma Denson }
7545afc9a25SJemma Denson
755fbdbab72SJemma Denson /*
756fbdbab72SJemma Denson * FEC & modulation lookup table
7575afc9a25SJemma Denson * Used for decoding the REG_FECMODE register
7585afc9a25SJemma Denson * once tuned in.
7595afc9a25SJemma Denson */
760ec8fe6c9SMauro Carvalho Chehab struct cx24120_modfec {
7610df289a2SMauro Carvalho Chehab enum fe_delivery_system delsys;
7620df289a2SMauro Carvalho Chehab enum fe_modulation mod;
7630df289a2SMauro Carvalho Chehab enum fe_code_rate fec;
7645afc9a25SJemma Denson u8 val;
765ec8fe6c9SMauro Carvalho Chehab };
766ec8fe6c9SMauro Carvalho Chehab
767ec8fe6c9SMauro Carvalho Chehab static const struct cx24120_modfec modfec_lookup_table[] = {
7685afc9a25SJemma Denson /*delsys mod fec val */
7695afc9a25SJemma Denson { SYS_DVBS, QPSK, FEC_1_2, 0x01 },
7705afc9a25SJemma Denson { SYS_DVBS, QPSK, FEC_2_3, 0x02 },
7715afc9a25SJemma Denson { SYS_DVBS, QPSK, FEC_3_4, 0x03 },
7725afc9a25SJemma Denson { SYS_DVBS, QPSK, FEC_4_5, 0x04 },
7735afc9a25SJemma Denson { SYS_DVBS, QPSK, FEC_5_6, 0x05 },
7745afc9a25SJemma Denson { SYS_DVBS, QPSK, FEC_6_7, 0x06 },
7755afc9a25SJemma Denson { SYS_DVBS, QPSK, FEC_7_8, 0x07 },
7765afc9a25SJemma Denson
7775afc9a25SJemma Denson { SYS_DVBS2, QPSK, FEC_1_2, 0x04 },
7785afc9a25SJemma Denson { SYS_DVBS2, QPSK, FEC_3_5, 0x05 },
7795afc9a25SJemma Denson { SYS_DVBS2, QPSK, FEC_2_3, 0x06 },
7805afc9a25SJemma Denson { SYS_DVBS2, QPSK, FEC_3_4, 0x07 },
7815afc9a25SJemma Denson { SYS_DVBS2, QPSK, FEC_4_5, 0x08 },
7825afc9a25SJemma Denson { SYS_DVBS2, QPSK, FEC_5_6, 0x09 },
7835afc9a25SJemma Denson { SYS_DVBS2, QPSK, FEC_8_9, 0x0a },
7845afc9a25SJemma Denson { SYS_DVBS2, QPSK, FEC_9_10, 0x0b },
7855afc9a25SJemma Denson
7865afc9a25SJemma Denson { SYS_DVBS2, PSK_8, FEC_3_5, 0x0c },
7875afc9a25SJemma Denson { SYS_DVBS2, PSK_8, FEC_2_3, 0x0d },
7885afc9a25SJemma Denson { SYS_DVBS2, PSK_8, FEC_3_4, 0x0e },
7895afc9a25SJemma Denson { SYS_DVBS2, PSK_8, FEC_5_6, 0x0f },
7905afc9a25SJemma Denson { SYS_DVBS2, PSK_8, FEC_8_9, 0x10 },
7915afc9a25SJemma Denson { SYS_DVBS2, PSK_8, FEC_9_10, 0x11 },
7925afc9a25SJemma Denson };
7935afc9a25SJemma Denson
7945afc9a25SJemma Denson /* Retrieve current fec, modulation & pilot values */
cx24120_get_fec(struct dvb_frontend * fe)7955afc9a25SJemma Denson static int cx24120_get_fec(struct dvb_frontend *fe)
7965afc9a25SJemma Denson {
7975afc9a25SJemma Denson struct dtv_frontend_properties *c = &fe->dtv_property_cache;
7985afc9a25SJemma Denson struct cx24120_state *state = fe->demodulator_priv;
7995afc9a25SJemma Denson int idx;
8005afc9a25SJemma Denson int ret;
8011ff2e8edSPatrick Boettcher int fec;
8025afc9a25SJemma Denson
8035afc9a25SJemma Denson ret = cx24120_readreg(state, CX24120_REG_FECMODE);
8041ff2e8edSPatrick Boettcher fec = ret & 0x3f; /* Lower 6 bits */
8055afc9a25SJemma Denson
8062f3f07fbSJemma Denson dev_dbg(&state->i2c->dev, "raw fec = %d\n", fec);
8075afc9a25SJemma Denson
8085afc9a25SJemma Denson for (idx = 0; idx < ARRAY_SIZE(modfec_lookup_table); idx++) {
8095afc9a25SJemma Denson if (modfec_lookup_table[idx].delsys != state->dcur.delsys)
8105afc9a25SJemma Denson continue;
8111ff2e8edSPatrick Boettcher if (modfec_lookup_table[idx].val != fec)
8125afc9a25SJemma Denson continue;
8135afc9a25SJemma Denson
8145afc9a25SJemma Denson break; /* found */
8155afc9a25SJemma Denson }
8165afc9a25SJemma Denson
8175afc9a25SJemma Denson if (idx >= ARRAY_SIZE(modfec_lookup_table)) {
8182f3f07fbSJemma Denson dev_dbg(&state->i2c->dev, "couldn't find fec!\n");
8195afc9a25SJemma Denson return -EINVAL;
8205afc9a25SJemma Denson }
8215afc9a25SJemma Denson
8225afc9a25SJemma Denson /* save values back to cache */
8235afc9a25SJemma Denson c->modulation = modfec_lookup_table[idx].mod;
8245afc9a25SJemma Denson c->fec_inner = modfec_lookup_table[idx].fec;
8255afc9a25SJemma Denson c->pilot = (ret & 0x80) ? PILOT_ON : PILOT_OFF;
8265afc9a25SJemma Denson
8272f3f07fbSJemma Denson dev_dbg(&state->i2c->dev, "mod(%d), fec(%d), pilot(%d)\n",
8285afc9a25SJemma Denson c->modulation, c->fec_inner, c->pilot);
8295afc9a25SJemma Denson
8305afc9a25SJemma Denson return 0;
8315afc9a25SJemma Denson }
8325afc9a25SJemma Denson
833ddcb252eSJemma Denson /* Calculate ber window time */
cx24120_calculate_ber_window(struct cx24120_state * state,u32 rate)834edff2bacSHans Verkuil static void cx24120_calculate_ber_window(struct cx24120_state *state, u32 rate)
835ddcb252eSJemma Denson {
836ddcb252eSJemma Denson struct dvb_frontend *fe = &state->frontend;
837ddcb252eSJemma Denson struct dtv_frontend_properties *c = &fe->dtv_property_cache;
83880e9710bSJemma Denson u64 tmp;
839ddcb252eSJemma Denson
840ddcb252eSJemma Denson /*
841ddcb252eSJemma Denson * Calculate bitrate from rate in the clock ratios table.
842ddcb252eSJemma Denson * This isn't *exactly* right but close enough.
843ddcb252eSJemma Denson */
84480e9710bSJemma Denson tmp = (u64)c->symbol_rate * rate;
84580e9710bSJemma Denson do_div(tmp, 256);
84680e9710bSJemma Denson state->bitrate = tmp;
847ddcb252eSJemma Denson
848ddcb252eSJemma Denson /* usecs per ber window */
849ddcb252eSJemma Denson tmp = 1000000ULL * CX24120_BER_WSIZE;
85080e9710bSJemma Denson do_div(tmp, state->bitrate);
851ddcb252eSJemma Denson state->berw_usecs = tmp;
852ddcb252eSJemma Denson
85380e9710bSJemma Denson dev_dbg(&state->i2c->dev, "bitrate: %u, berw_usecs: %u\n",
85480e9710bSJemma Denson state->bitrate, state->berw_usecs);
855ddcb252eSJemma Denson }
856ddcb252eSJemma Denson
857fbdbab72SJemma Denson /*
858fbdbab72SJemma Denson * Clock ratios lookup table
8595afc9a25SJemma Denson *
8605afc9a25SJemma Denson * Values obtained from much larger table in old driver
8615afc9a25SJemma Denson * which had numerous entries which would never match.
8625afc9a25SJemma Denson *
8635afc9a25SJemma Denson * There's probably some way of calculating these but I
8645afc9a25SJemma Denson * can't determine the pattern
8655afc9a25SJemma Denson */
866ec8fe6c9SMauro Carvalho Chehab struct cx24120_clock_ratios_table {
8670df289a2SMauro Carvalho Chehab enum fe_delivery_system delsys;
8680df289a2SMauro Carvalho Chehab enum fe_pilot pilot;
8690df289a2SMauro Carvalho Chehab enum fe_modulation mod;
8700df289a2SMauro Carvalho Chehab enum fe_code_rate fec;
8715afc9a25SJemma Denson u32 m_rat;
8725afc9a25SJemma Denson u32 n_rat;
8735afc9a25SJemma Denson u32 rate;
874ec8fe6c9SMauro Carvalho Chehab };
875ec8fe6c9SMauro Carvalho Chehab
876ec8fe6c9SMauro Carvalho Chehab static const struct cx24120_clock_ratios_table clock_ratios_table[] = {
8775afc9a25SJemma Denson /*delsys pilot mod fec m_rat n_rat rate */
8785afc9a25SJemma Denson { SYS_DVBS2, PILOT_OFF, QPSK, FEC_1_2, 273088, 254505, 274 },
8795afc9a25SJemma Denson { SYS_DVBS2, PILOT_OFF, QPSK, FEC_3_5, 17272, 13395, 330 },
8805afc9a25SJemma Denson { SYS_DVBS2, PILOT_OFF, QPSK, FEC_2_3, 24344, 16967, 367 },
8815afc9a25SJemma Denson { SYS_DVBS2, PILOT_OFF, QPSK, FEC_3_4, 410788, 254505, 413 },
8825afc9a25SJemma Denson { SYS_DVBS2, PILOT_OFF, QPSK, FEC_4_5, 438328, 254505, 440 },
8835afc9a25SJemma Denson { SYS_DVBS2, PILOT_OFF, QPSK, FEC_5_6, 30464, 16967, 459 },
8845afc9a25SJemma Denson { SYS_DVBS2, PILOT_OFF, QPSK, FEC_8_9, 487832, 254505, 490 },
8855afc9a25SJemma Denson { SYS_DVBS2, PILOT_OFF, QPSK, FEC_9_10, 493952, 254505, 496 },
8865afc9a25SJemma Denson { SYS_DVBS2, PILOT_OFF, PSK_8, FEC_3_5, 328168, 169905, 494 },
8875afc9a25SJemma Denson { SYS_DVBS2, PILOT_OFF, PSK_8, FEC_2_3, 24344, 11327, 550 },
8885afc9a25SJemma Denson { SYS_DVBS2, PILOT_OFF, PSK_8, FEC_3_4, 410788, 169905, 618 },
8895afc9a25SJemma Denson { SYS_DVBS2, PILOT_OFF, PSK_8, FEC_5_6, 30464, 11327, 688 },
8905afc9a25SJemma Denson { SYS_DVBS2, PILOT_OFF, PSK_8, FEC_8_9, 487832, 169905, 735 },
8915afc9a25SJemma Denson { SYS_DVBS2, PILOT_OFF, PSK_8, FEC_9_10, 493952, 169905, 744 },
8925afc9a25SJemma Denson { SYS_DVBS2, PILOT_ON, QPSK, FEC_1_2, 273088, 260709, 268 },
8935afc9a25SJemma Denson { SYS_DVBS2, PILOT_ON, QPSK, FEC_3_5, 328168, 260709, 322 },
8945afc9a25SJemma Denson { SYS_DVBS2, PILOT_ON, QPSK, FEC_2_3, 121720, 86903, 358 },
8955afc9a25SJemma Denson { SYS_DVBS2, PILOT_ON, QPSK, FEC_3_4, 410788, 260709, 403 },
8965afc9a25SJemma Denson { SYS_DVBS2, PILOT_ON, QPSK, FEC_4_5, 438328, 260709, 430 },
8975afc9a25SJemma Denson { SYS_DVBS2, PILOT_ON, QPSK, FEC_5_6, 152320, 86903, 448 },
8985afc9a25SJemma Denson { SYS_DVBS2, PILOT_ON, QPSK, FEC_8_9, 487832, 260709, 479 },
8995afc9a25SJemma Denson { SYS_DVBS2, PILOT_ON, QPSK, FEC_9_10, 493952, 260709, 485 },
9005afc9a25SJemma Denson { SYS_DVBS2, PILOT_ON, PSK_8, FEC_3_5, 328168, 173853, 483 },
9015afc9a25SJemma Denson { SYS_DVBS2, PILOT_ON, PSK_8, FEC_2_3, 121720, 57951, 537 },
9025afc9a25SJemma Denson { SYS_DVBS2, PILOT_ON, PSK_8, FEC_3_4, 410788, 173853, 604 },
9035afc9a25SJemma Denson { SYS_DVBS2, PILOT_ON, PSK_8, FEC_5_6, 152320, 57951, 672 },
9045afc9a25SJemma Denson { SYS_DVBS2, PILOT_ON, PSK_8, FEC_8_9, 487832, 173853, 718 },
9055afc9a25SJemma Denson { SYS_DVBS2, PILOT_ON, PSK_8, FEC_9_10, 493952, 173853, 727 },
9065afc9a25SJemma Denson { SYS_DVBS, PILOT_OFF, QPSK, FEC_1_2, 152592, 152592, 256 },
9075afc9a25SJemma Denson { SYS_DVBS, PILOT_OFF, QPSK, FEC_2_3, 305184, 228888, 341 },
9085afc9a25SJemma Denson { SYS_DVBS, PILOT_OFF, QPSK, FEC_3_4, 457776, 305184, 384 },
9095afc9a25SJemma Denson { SYS_DVBS, PILOT_OFF, QPSK, FEC_5_6, 762960, 457776, 427 },
9105afc9a25SJemma Denson { SYS_DVBS, PILOT_OFF, QPSK, FEC_7_8, 1068144, 610368, 448 },
9115afc9a25SJemma Denson };
9125afc9a25SJemma Denson
9135afc9a25SJemma Denson /* Set clock ratio from lookup table */
cx24120_set_clock_ratios(struct dvb_frontend * fe)9145afc9a25SJemma Denson static void cx24120_set_clock_ratios(struct dvb_frontend *fe)
9155afc9a25SJemma Denson {
9165afc9a25SJemma Denson struct dtv_frontend_properties *c = &fe->dtv_property_cache;
9175afc9a25SJemma Denson struct cx24120_state *state = fe->demodulator_priv;
9185afc9a25SJemma Denson struct cx24120_cmd cmd;
9195afc9a25SJemma Denson int ret, idx;
9205afc9a25SJemma Denson
9215afc9a25SJemma Denson /* Find fec, modulation, pilot */
9225afc9a25SJemma Denson ret = cx24120_get_fec(fe);
9235afc9a25SJemma Denson if (ret != 0)
9245afc9a25SJemma Denson return;
9255afc9a25SJemma Denson
9265afc9a25SJemma Denson /* Find the clock ratios in the lookup table */
9275afc9a25SJemma Denson for (idx = 0; idx < ARRAY_SIZE(clock_ratios_table); idx++) {
9285afc9a25SJemma Denson if (clock_ratios_table[idx].delsys != state->dcur.delsys)
9295afc9a25SJemma Denson continue;
9305afc9a25SJemma Denson if (clock_ratios_table[idx].mod != c->modulation)
9315afc9a25SJemma Denson continue;
9325afc9a25SJemma Denson if (clock_ratios_table[idx].fec != c->fec_inner)
9335afc9a25SJemma Denson continue;
9345afc9a25SJemma Denson if (clock_ratios_table[idx].pilot != c->pilot)
9355afc9a25SJemma Denson continue;
9365afc9a25SJemma Denson
9375afc9a25SJemma Denson break; /* found */
9385afc9a25SJemma Denson }
9395afc9a25SJemma Denson
9405afc9a25SJemma Denson if (idx >= ARRAY_SIZE(clock_ratios_table)) {
9415afc9a25SJemma Denson info("Clock ratio not found - data reception in danger\n");
9425afc9a25SJemma Denson return;
9435afc9a25SJemma Denson }
9445afc9a25SJemma Denson
9455afc9a25SJemma Denson /* Read current values? */
9465afc9a25SJemma Denson cmd.id = CMD_CLOCK_READ;
9475afc9a25SJemma Denson cmd.len = 1;
9485afc9a25SJemma Denson cmd.arg[0] = 0x00;
9495afc9a25SJemma Denson ret = cx24120_message_sendrcv(state, &cmd, 6);
9505afc9a25SJemma Denson if (ret != 0)
9515afc9a25SJemma Denson return;
9525afc9a25SJemma Denson /* in cmd[0]-[5] - result */
9535afc9a25SJemma Denson
9542f3f07fbSJemma Denson dev_dbg(&state->i2c->dev, "m=%d, n=%d; idx: %d m=%d, n=%d, rate=%d\n",
9555afc9a25SJemma Denson cmd.arg[2] | (cmd.arg[1] << 8) | (cmd.arg[0] << 16),
9565afc9a25SJemma Denson cmd.arg[5] | (cmd.arg[4] << 8) | (cmd.arg[3] << 16),
9575afc9a25SJemma Denson idx,
9585afc9a25SJemma Denson clock_ratios_table[idx].m_rat,
9595afc9a25SJemma Denson clock_ratios_table[idx].n_rat,
9605afc9a25SJemma Denson clock_ratios_table[idx].rate);
9615afc9a25SJemma Denson
9625afc9a25SJemma Denson /* Set the clock */
9635afc9a25SJemma Denson cmd.id = CMD_CLOCK_SET;
9645afc9a25SJemma Denson cmd.len = 10;
9655afc9a25SJemma Denson cmd.arg[0] = 0;
9665afc9a25SJemma Denson cmd.arg[1] = 0x10;
9675afc9a25SJemma Denson cmd.arg[2] = (clock_ratios_table[idx].m_rat >> 16) & 0xff;
9685afc9a25SJemma Denson cmd.arg[3] = (clock_ratios_table[idx].m_rat >> 8) & 0xff;
9695afc9a25SJemma Denson cmd.arg[4] = (clock_ratios_table[idx].m_rat >> 0) & 0xff;
9705afc9a25SJemma Denson cmd.arg[5] = (clock_ratios_table[idx].n_rat >> 16) & 0xff;
9715afc9a25SJemma Denson cmd.arg[6] = (clock_ratios_table[idx].n_rat >> 8) & 0xff;
9725afc9a25SJemma Denson cmd.arg[7] = (clock_ratios_table[idx].n_rat >> 0) & 0xff;
9735afc9a25SJemma Denson cmd.arg[8] = (clock_ratios_table[idx].rate >> 8) & 0xff;
9745afc9a25SJemma Denson cmd.arg[9] = (clock_ratios_table[idx].rate >> 0) & 0xff;
9755afc9a25SJemma Denson
97696002c0aSDaniil Dulov ret = cx24120_message_send(state, &cmd);
97796002c0aSDaniil Dulov if (ret != 0)
97896002c0aSDaniil Dulov return;
979ddcb252eSJemma Denson
980ddcb252eSJemma Denson /* Calculate ber window rates for stat work */
981ddcb252eSJemma Denson cx24120_calculate_ber_window(state, clock_ratios_table[idx].rate);
9825afc9a25SJemma Denson }
9835afc9a25SJemma Denson
9845afc9a25SJemma Denson /* Set inversion value */
cx24120_set_inversion(struct cx24120_state * state,enum fe_spectral_inversion inversion)9855afc9a25SJemma Denson static int cx24120_set_inversion(struct cx24120_state *state,
9860df289a2SMauro Carvalho Chehab enum fe_spectral_inversion inversion)
9875afc9a25SJemma Denson {
9882f3f07fbSJemma Denson dev_dbg(&state->i2c->dev, "(%d)\n", inversion);
9895afc9a25SJemma Denson
9905afc9a25SJemma Denson switch (inversion) {
9915afc9a25SJemma Denson case INVERSION_OFF:
9925afc9a25SJemma Denson state->dnxt.inversion_val = 0x00;
9935afc9a25SJemma Denson break;
9945afc9a25SJemma Denson case INVERSION_ON:
9955afc9a25SJemma Denson state->dnxt.inversion_val = 0x04;
9965afc9a25SJemma Denson break;
9975afc9a25SJemma Denson case INVERSION_AUTO:
9985afc9a25SJemma Denson state->dnxt.inversion_val = 0x0c;
9995afc9a25SJemma Denson break;
10005afc9a25SJemma Denson default:
10015afc9a25SJemma Denson return -EINVAL;
10025afc9a25SJemma Denson }
10035afc9a25SJemma Denson
10045afc9a25SJemma Denson state->dnxt.inversion = inversion;
10055afc9a25SJemma Denson
10065afc9a25SJemma Denson return 0;
10075afc9a25SJemma Denson }
10085afc9a25SJemma Denson
10095c0a1c28SJemma Denson /* FEC lookup table for tuning */
1010ec8fe6c9SMauro Carvalho Chehab struct cx24120_modfec_table {
10110df289a2SMauro Carvalho Chehab enum fe_delivery_system delsys;
10120df289a2SMauro Carvalho Chehab enum fe_modulation mod;
10130df289a2SMauro Carvalho Chehab enum fe_code_rate fec;
10145afc9a25SJemma Denson u8 val;
1015ec8fe6c9SMauro Carvalho Chehab };
1016ec8fe6c9SMauro Carvalho Chehab
1017ec8fe6c9SMauro Carvalho Chehab static const struct cx24120_modfec_table modfec_table[] = {
10185afc9a25SJemma Denson /*delsys mod fec val */
10195afc9a25SJemma Denson { SYS_DVBS, QPSK, FEC_1_2, 0x2e },
10205afc9a25SJemma Denson { SYS_DVBS, QPSK, FEC_2_3, 0x2f },
10215afc9a25SJemma Denson { SYS_DVBS, QPSK, FEC_3_4, 0x30 },
10225afc9a25SJemma Denson { SYS_DVBS, QPSK, FEC_5_6, 0x31 },
10235afc9a25SJemma Denson { SYS_DVBS, QPSK, FEC_6_7, 0x32 },
10245afc9a25SJemma Denson { SYS_DVBS, QPSK, FEC_7_8, 0x33 },
10255afc9a25SJemma Denson
10265c0a1c28SJemma Denson { SYS_DVBS2, QPSK, FEC_1_2, 0x04 },
10275c0a1c28SJemma Denson { SYS_DVBS2, QPSK, FEC_3_5, 0x05 },
10285c0a1c28SJemma Denson { SYS_DVBS2, QPSK, FEC_2_3, 0x06 },
10295afc9a25SJemma Denson { SYS_DVBS2, QPSK, FEC_3_4, 0x07 },
10305c0a1c28SJemma Denson { SYS_DVBS2, QPSK, FEC_4_5, 0x08 },
10315c0a1c28SJemma Denson { SYS_DVBS2, QPSK, FEC_5_6, 0x09 },
10325c0a1c28SJemma Denson { SYS_DVBS2, QPSK, FEC_8_9, 0x0a },
10335c0a1c28SJemma Denson { SYS_DVBS2, QPSK, FEC_9_10, 0x0b },
10345afc9a25SJemma Denson
10355c0a1c28SJemma Denson { SYS_DVBS2, PSK_8, FEC_3_5, 0x0c },
10365afc9a25SJemma Denson { SYS_DVBS2, PSK_8, FEC_2_3, 0x0d },
10375afc9a25SJemma Denson { SYS_DVBS2, PSK_8, FEC_3_4, 0x0e },
10385c0a1c28SJemma Denson { SYS_DVBS2, PSK_8, FEC_5_6, 0x0f },
10395c0a1c28SJemma Denson { SYS_DVBS2, PSK_8, FEC_8_9, 0x10 },
10405c0a1c28SJemma Denson { SYS_DVBS2, PSK_8, FEC_9_10, 0x11 },
10415afc9a25SJemma Denson };
10425afc9a25SJemma Denson
10435afc9a25SJemma Denson /* Set fec_val & fec_mask values from delsys, modulation & fec */
cx24120_set_fec(struct cx24120_state * state,enum fe_modulation mod,enum fe_code_rate fec)10440df289a2SMauro Carvalho Chehab static int cx24120_set_fec(struct cx24120_state *state, enum fe_modulation mod,
10450df289a2SMauro Carvalho Chehab enum fe_code_rate fec)
10465afc9a25SJemma Denson {
10475afc9a25SJemma Denson int idx;
10485afc9a25SJemma Denson
10492f3f07fbSJemma Denson dev_dbg(&state->i2c->dev, "(0x%02x,0x%02x)\n", mod, fec);
10505afc9a25SJemma Denson
10515afc9a25SJemma Denson state->dnxt.fec = fec;
10525afc9a25SJemma Denson
10535afc9a25SJemma Denson /* Lookup fec_val from modfec table */
10545afc9a25SJemma Denson for (idx = 0; idx < ARRAY_SIZE(modfec_table); idx++) {
10555afc9a25SJemma Denson if (modfec_table[idx].delsys != state->dnxt.delsys)
10565afc9a25SJemma Denson continue;
10575afc9a25SJemma Denson if (modfec_table[idx].mod != mod)
10585afc9a25SJemma Denson continue;
10595afc9a25SJemma Denson if (modfec_table[idx].fec != fec)
10605afc9a25SJemma Denson continue;
10615afc9a25SJemma Denson
10625afc9a25SJemma Denson /* found */
10635afc9a25SJemma Denson state->dnxt.fec_mask = 0x00;
10645afc9a25SJemma Denson state->dnxt.fec_val = modfec_table[idx].val;
10655afc9a25SJemma Denson return 0;
10665afc9a25SJemma Denson }
10675afc9a25SJemma Denson
10685afc9a25SJemma Denson if (state->dnxt.delsys == SYS_DVBS2) {
10695afc9a25SJemma Denson /* DVBS2 auto is 0x00/0x00 */
10705afc9a25SJemma Denson state->dnxt.fec_mask = 0x00;
10715afc9a25SJemma Denson state->dnxt.fec_val = 0x00;
10725afc9a25SJemma Denson } else {
10735afc9a25SJemma Denson /* Set DVB-S to auto */
10745afc9a25SJemma Denson state->dnxt.fec_val = 0x2e;
10755afc9a25SJemma Denson state->dnxt.fec_mask = 0xac;
10765afc9a25SJemma Denson }
10775afc9a25SJemma Denson
10785afc9a25SJemma Denson return 0;
10795afc9a25SJemma Denson }
10805afc9a25SJemma Denson
10815afc9a25SJemma Denson /* Set pilot */
cx24120_set_pilot(struct cx24120_state * state,enum fe_pilot pilot)10820df289a2SMauro Carvalho Chehab static int cx24120_set_pilot(struct cx24120_state *state, enum fe_pilot pilot)
10831ff2e8edSPatrick Boettcher {
10842f3f07fbSJemma Denson dev_dbg(&state->i2c->dev, "(%d)\n", pilot);
10855afc9a25SJemma Denson
10865afc9a25SJemma Denson /* Pilot only valid in DVBS2 */
10875afc9a25SJemma Denson if (state->dnxt.delsys != SYS_DVBS2) {
10885afc9a25SJemma Denson state->dnxt.pilot_val = CX24120_PILOT_OFF;
10895afc9a25SJemma Denson return 0;
10905afc9a25SJemma Denson }
10915afc9a25SJemma Denson
10925afc9a25SJemma Denson switch (pilot) {
10935afc9a25SJemma Denson case PILOT_OFF:
10945afc9a25SJemma Denson state->dnxt.pilot_val = CX24120_PILOT_OFF;
10955afc9a25SJemma Denson break;
10965afc9a25SJemma Denson case PILOT_ON:
10975afc9a25SJemma Denson state->dnxt.pilot_val = CX24120_PILOT_ON;
10985afc9a25SJemma Denson break;
10995afc9a25SJemma Denson case PILOT_AUTO:
11005afc9a25SJemma Denson default:
11015afc9a25SJemma Denson state->dnxt.pilot_val = CX24120_PILOT_AUTO;
11025afc9a25SJemma Denson }
11035afc9a25SJemma Denson
11045afc9a25SJemma Denson return 0;
11055afc9a25SJemma Denson }
11065afc9a25SJemma Denson
11075afc9a25SJemma Denson /* Set symbol rate */
cx24120_set_symbolrate(struct cx24120_state * state,u32 rate)11085afc9a25SJemma Denson static int cx24120_set_symbolrate(struct cx24120_state *state, u32 rate)
11095afc9a25SJemma Denson {
11102f3f07fbSJemma Denson dev_dbg(&state->i2c->dev, "(%d)\n", rate);
11115afc9a25SJemma Denson
11125afc9a25SJemma Denson state->dnxt.symbol_rate = rate;
11135afc9a25SJemma Denson
11145afc9a25SJemma Denson /* Check symbol rate */
11155afc9a25SJemma Denson if (rate > 31000000) {
11165afc9a25SJemma Denson state->dnxt.clkdiv = (-(rate < 31000001) & 3) + 2;
11175afc9a25SJemma Denson state->dnxt.ratediv = (-(rate < 31000001) & 6) + 4;
11185afc9a25SJemma Denson } else {
11195afc9a25SJemma Denson state->dnxt.clkdiv = 3;
11205afc9a25SJemma Denson state->dnxt.ratediv = 6;
11215afc9a25SJemma Denson }
11225afc9a25SJemma Denson
11235afc9a25SJemma Denson return 0;
11245afc9a25SJemma Denson }
11255afc9a25SJemma Denson
11265afc9a25SJemma Denson /* Overwrite the current tuning params, we are about to tune */
cx24120_clone_params(struct dvb_frontend * fe)11275afc9a25SJemma Denson static void cx24120_clone_params(struct dvb_frontend *fe)
11285afc9a25SJemma Denson {
11295afc9a25SJemma Denson struct cx24120_state *state = fe->demodulator_priv;
11305afc9a25SJemma Denson
11315afc9a25SJemma Denson state->dcur = state->dnxt;
11325afc9a25SJemma Denson }
11335afc9a25SJemma Denson
cx24120_set_frontend(struct dvb_frontend * fe)11345afc9a25SJemma Denson static int cx24120_set_frontend(struct dvb_frontend *fe)
11355afc9a25SJemma Denson {
11365afc9a25SJemma Denson struct dtv_frontend_properties *c = &fe->dtv_property_cache;
11375afc9a25SJemma Denson struct cx24120_state *state = fe->demodulator_priv;
11385afc9a25SJemma Denson struct cx24120_cmd cmd;
11395afc9a25SJemma Denson int ret;
11405afc9a25SJemma Denson
11415afc9a25SJemma Denson switch (c->delivery_system) {
11425afc9a25SJemma Denson case SYS_DVBS2:
11432f3f07fbSJemma Denson dev_dbg(&state->i2c->dev, "DVB-S2\n");
11445afc9a25SJemma Denson break;
11455afc9a25SJemma Denson case SYS_DVBS:
11462f3f07fbSJemma Denson dev_dbg(&state->i2c->dev, "DVB-S\n");
11475afc9a25SJemma Denson break;
11485afc9a25SJemma Denson default:
11495afc9a25SJemma Denson dev_dbg(&state->i2c->dev,
11502f3f07fbSJemma Denson "delivery system(%d) not supported\n",
11512f3f07fbSJemma Denson c->delivery_system);
1152c0b34ab1SColin Ian King return -EINVAL;
11535afc9a25SJemma Denson }
11545afc9a25SJemma Denson
11555afc9a25SJemma Denson state->dnxt.delsys = c->delivery_system;
11565afc9a25SJemma Denson state->dnxt.modulation = c->modulation;
11575afc9a25SJemma Denson state->dnxt.frequency = c->frequency;
11585afc9a25SJemma Denson state->dnxt.pilot = c->pilot;
11595afc9a25SJemma Denson
11605afc9a25SJemma Denson ret = cx24120_set_inversion(state, c->inversion);
11615afc9a25SJemma Denson if (ret != 0)
11625afc9a25SJemma Denson return ret;
11635afc9a25SJemma Denson
11645afc9a25SJemma Denson ret = cx24120_set_fec(state, c->modulation, c->fec_inner);
11655afc9a25SJemma Denson if (ret != 0)
11665afc9a25SJemma Denson return ret;
11675afc9a25SJemma Denson
11685afc9a25SJemma Denson ret = cx24120_set_pilot(state, c->pilot);
11695afc9a25SJemma Denson if (ret != 0)
11705afc9a25SJemma Denson return ret;
11715afc9a25SJemma Denson
11725afc9a25SJemma Denson ret = cx24120_set_symbolrate(state, c->symbol_rate);
11735afc9a25SJemma Denson if (ret != 0)
11745afc9a25SJemma Denson return ret;
11755afc9a25SJemma Denson
11765afc9a25SJemma Denson /* discard the 'current' tuning parameters and prepare to tune */
11775afc9a25SJemma Denson cx24120_clone_params(fe);
11785afc9a25SJemma Denson
11795afc9a25SJemma Denson dev_dbg(&state->i2c->dev,
11802f3f07fbSJemma Denson "delsys = %d\n", state->dcur.delsys);
11815afc9a25SJemma Denson dev_dbg(&state->i2c->dev,
11822f3f07fbSJemma Denson "modulation = %d\n", state->dcur.modulation);
11835afc9a25SJemma Denson dev_dbg(&state->i2c->dev,
11842f3f07fbSJemma Denson "frequency = %d\n", state->dcur.frequency);
11855afc9a25SJemma Denson dev_dbg(&state->i2c->dev,
11862f3f07fbSJemma Denson "pilot = %d (val = 0x%02x)\n",
11875afc9a25SJemma Denson state->dcur.pilot, state->dcur.pilot_val);
11885afc9a25SJemma Denson dev_dbg(&state->i2c->dev,
11892f3f07fbSJemma Denson "symbol_rate = %d (clkdiv/ratediv = 0x%02x/0x%02x)\n",
11902f3f07fbSJemma Denson state->dcur.symbol_rate,
11915afc9a25SJemma Denson state->dcur.clkdiv, state->dcur.ratediv);
11925afc9a25SJemma Denson dev_dbg(&state->i2c->dev,
11932f3f07fbSJemma Denson "FEC = %d (mask/val = 0x%02x/0x%02x)\n",
11945afc9a25SJemma Denson state->dcur.fec, state->dcur.fec_mask, state->dcur.fec_val);
11955afc9a25SJemma Denson dev_dbg(&state->i2c->dev,
11962f3f07fbSJemma Denson "Inversion = %d (val = 0x%02x)\n",
11975afc9a25SJemma Denson state->dcur.inversion, state->dcur.inversion_val);
11985afc9a25SJemma Denson
11996138dc2fSJemma Denson /* Flag that clock needs to be set after tune */
12006138dc2fSJemma Denson state->need_clock_set = 1;
12016138dc2fSJemma Denson
12025afc9a25SJemma Denson /* Tune in */
12035afc9a25SJemma Denson cmd.id = CMD_TUNEREQUEST;
12045afc9a25SJemma Denson cmd.len = 15;
12055afc9a25SJemma Denson cmd.arg[0] = 0;
12065afc9a25SJemma Denson cmd.arg[1] = (state->dcur.frequency & 0xff0000) >> 16;
12075afc9a25SJemma Denson cmd.arg[2] = (state->dcur.frequency & 0x00ff00) >> 8;
12085afc9a25SJemma Denson cmd.arg[3] = (state->dcur.frequency & 0x0000ff);
12095afc9a25SJemma Denson cmd.arg[4] = ((state->dcur.symbol_rate / 1000) & 0xff00) >> 8;
12105afc9a25SJemma Denson cmd.arg[5] = ((state->dcur.symbol_rate / 1000) & 0x00ff);
12115afc9a25SJemma Denson cmd.arg[6] = state->dcur.inversion;
12125afc9a25SJemma Denson cmd.arg[7] = state->dcur.fec_val | state->dcur.pilot_val;
12135afc9a25SJemma Denson cmd.arg[8] = CX24120_SEARCH_RANGE_KHZ >> 8;
12145afc9a25SJemma Denson cmd.arg[9] = CX24120_SEARCH_RANGE_KHZ & 0xff;
12155afc9a25SJemma Denson cmd.arg[10] = 0; /* maybe rolloff? */
12165afc9a25SJemma Denson cmd.arg[11] = state->dcur.fec_mask;
12175afc9a25SJemma Denson cmd.arg[12] = state->dcur.ratediv;
12185afc9a25SJemma Denson cmd.arg[13] = state->dcur.clkdiv;
12195afc9a25SJemma Denson cmd.arg[14] = 0;
12205afc9a25SJemma Denson
12215afc9a25SJemma Denson /* Send tune command */
12225afc9a25SJemma Denson ret = cx24120_message_send(state, &cmd);
12235afc9a25SJemma Denson if (ret != 0)
12245afc9a25SJemma Denson return ret;
12255afc9a25SJemma Denson
12265afc9a25SJemma Denson /* Write symbol rate values */
12275afc9a25SJemma Denson ret = cx24120_writereg(state, CX24120_REG_CLKDIV, state->dcur.clkdiv);
12285afc9a25SJemma Denson ret = cx24120_readreg(state, CX24120_REG_RATEDIV);
12295afc9a25SJemma Denson ret &= 0xfffffff0;
12305afc9a25SJemma Denson ret |= state->dcur.ratediv;
12315afc9a25SJemma Denson ret = cx24120_writereg(state, CX24120_REG_RATEDIV, ret);
12325afc9a25SJemma Denson
12335afc9a25SJemma Denson return 0;
12345afc9a25SJemma Denson }
12355afc9a25SJemma Denson
1236c84251bbSJemma Denson /* Set vco from config */
cx24120_set_vco(struct cx24120_state * state)1237c84251bbSJemma Denson static int cx24120_set_vco(struct cx24120_state *state)
12385afc9a25SJemma Denson {
1239c84251bbSJemma Denson struct cx24120_cmd cmd;
1240c84251bbSJemma Denson u32 nxtal_khz, vco;
1241c84251bbSJemma Denson u64 inv_vco;
12425afc9a25SJemma Denson u32 xtal_khz = state->config->xtal_khz;
12435afc9a25SJemma Denson
1244c84251bbSJemma Denson nxtal_khz = xtal_khz * 4;
1245c84251bbSJemma Denson vco = nxtal_khz * 10;
1246c84251bbSJemma Denson inv_vco = DIV_ROUND_CLOSEST_ULL(0x400000000ULL, vco);
12475afc9a25SJemma Denson
12482f3f07fbSJemma Denson dev_dbg(&state->i2c->dev, "xtal=%d, vco=%d, inv_vco=%lld\n",
12492f3f07fbSJemma Denson xtal_khz, vco, inv_vco);
12505afc9a25SJemma Denson
1251c84251bbSJemma Denson cmd.id = CMD_VCO_SET;
1252c84251bbSJemma Denson cmd.len = 12;
1253c84251bbSJemma Denson cmd.arg[0] = (vco >> 16) & 0xff;
1254c84251bbSJemma Denson cmd.arg[1] = (vco >> 8) & 0xff;
1255c84251bbSJemma Denson cmd.arg[2] = vco & 0xff;
1256c84251bbSJemma Denson cmd.arg[3] = (inv_vco >> 8) & 0xff;
1257c84251bbSJemma Denson cmd.arg[4] = (inv_vco) & 0xff;
1258c84251bbSJemma Denson cmd.arg[5] = 0x03;
1259c84251bbSJemma Denson cmd.arg[6] = (nxtal_khz >> 8) & 0xff;
1260c84251bbSJemma Denson cmd.arg[7] = nxtal_khz & 0xff;
1261c84251bbSJemma Denson cmd.arg[8] = 0x06;
1262c84251bbSJemma Denson cmd.arg[9] = 0x03;
1263c84251bbSJemma Denson cmd.arg[10] = (xtal_khz >> 16) & 0xff;
1264c84251bbSJemma Denson cmd.arg[11] = xtal_khz & 0xff;
1265c84251bbSJemma Denson
1266c84251bbSJemma Denson return cx24120_message_send(state, &cmd);
12675afc9a25SJemma Denson }
12685afc9a25SJemma Denson
cx24120_init(struct dvb_frontend * fe)12695b8bc802SMauro Carvalho Chehab static int cx24120_init(struct dvb_frontend *fe)
12705afc9a25SJemma Denson {
12715afc9a25SJemma Denson const struct firmware *fw;
1272d3cf06bbSJemma Denson struct dtv_frontend_properties *c = &fe->dtv_property_cache;
12735afc9a25SJemma Denson struct cx24120_state *state = fe->demodulator_priv;
12745afc9a25SJemma Denson struct cx24120_cmd cmd;
12754133601cSJemma Denson u8 reg;
12764133601cSJemma Denson int ret, i;
12775afc9a25SJemma Denson unsigned char vers[4];
12785afc9a25SJemma Denson
12795afc9a25SJemma Denson if (state->cold_init)
12805afc9a25SJemma Denson return 0;
12815afc9a25SJemma Denson
12825afc9a25SJemma Denson /* ???? */
128392443cdbSJemma Denson cx24120_writereg(state, 0xea, 0x00);
128492443cdbSJemma Denson cx24120_test_rom(state);
12851668797dSJemma Denson reg = cx24120_readreg(state, 0xfb) & 0xfe;
12861668797dSJemma Denson cx24120_writereg(state, 0xfb, reg);
12871668797dSJemma Denson reg = cx24120_readreg(state, 0xfc) & 0xfe;
12881668797dSJemma Denson cx24120_writereg(state, 0xfc, reg);
128992443cdbSJemma Denson cx24120_writereg(state, 0xc3, 0x04);
129092443cdbSJemma Denson cx24120_writereg(state, 0xc4, 0x04);
129192443cdbSJemma Denson cx24120_writereg(state, 0xce, 0x00);
129292443cdbSJemma Denson cx24120_writereg(state, 0xcf, 0x00);
12931668797dSJemma Denson reg = cx24120_readreg(state, 0xea) & 0xfe;
12941668797dSJemma Denson cx24120_writereg(state, 0xea, reg);
129592443cdbSJemma Denson cx24120_writereg(state, 0xeb, 0x0c);
129692443cdbSJemma Denson cx24120_writereg(state, 0xec, 0x06);
129792443cdbSJemma Denson cx24120_writereg(state, 0xed, 0x05);
129892443cdbSJemma Denson cx24120_writereg(state, 0xee, 0x03);
129992443cdbSJemma Denson cx24120_writereg(state, 0xef, 0x05);
130092443cdbSJemma Denson cx24120_writereg(state, 0xf3, 0x03);
130192443cdbSJemma Denson cx24120_writereg(state, 0xf4, 0x44);
13025afc9a25SJemma Denson
13034133601cSJemma Denson for (i = 0; i < 3; i++) {
13044133601cSJemma Denson cx24120_writereg(state, 0xf0 + i, 0x04);
13054133601cSJemma Denson cx24120_writereg(state, 0xe6 + i, 0x02);
13065afc9a25SJemma Denson }
13075afc9a25SJemma Denson
13081668797dSJemma Denson cx24120_writereg(state, 0xea, (reg | 0x01));
13094133601cSJemma Denson for (i = 0; i < 6; i += 2) {
13104133601cSJemma Denson cx24120_writereg(state, 0xc5 + i, 0x00);
13114133601cSJemma Denson cx24120_writereg(state, 0xc6 + i, 0x00);
13125afc9a25SJemma Denson }
13135afc9a25SJemma Denson
131492443cdbSJemma Denson cx24120_writereg(state, 0xe4, 0x03);
131592443cdbSJemma Denson cx24120_writereg(state, 0xeb, 0x0a);
13165afc9a25SJemma Denson
13172f3f07fbSJemma Denson dev_dbg(&state->i2c->dev, "requesting firmware (%s) to download...\n",
13182f3f07fbSJemma Denson CX24120_FIRMWARE);
13195afc9a25SJemma Denson
13205afc9a25SJemma Denson ret = state->config->request_firmware(fe, &fw, CX24120_FIRMWARE);
13215afc9a25SJemma Denson if (ret) {
13221ff2e8edSPatrick Boettcher err("Could not load firmware (%s): %d\n", CX24120_FIRMWARE,
13231ff2e8edSPatrick Boettcher ret);
13245afc9a25SJemma Denson return ret;
13255afc9a25SJemma Denson }
13265afc9a25SJemma Denson
13275afc9a25SJemma Denson dev_dbg(&state->i2c->dev,
13282f3f07fbSJemma Denson "Firmware found, size %d bytes (%02x %02x .. %02x %02x)\n",
13295afc9a25SJemma Denson (int)fw->size, /* firmware_size in bytes */
13305afc9a25SJemma Denson fw->data[0], /* fw 1st byte */
13315afc9a25SJemma Denson fw->data[1], /* fw 2d byte */
13325afc9a25SJemma Denson fw->data[fw->size - 2], /* fw before last byte */
13335afc9a25SJemma Denson fw->data[fw->size - 1]); /* fw last byte */
13345afc9a25SJemma Denson
133592443cdbSJemma Denson cx24120_test_rom(state);
13361668797dSJemma Denson reg = cx24120_readreg(state, 0xfb) & 0xfe;
13371668797dSJemma Denson cx24120_writereg(state, 0xfb, reg);
133892443cdbSJemma Denson cx24120_writereg(state, 0xe0, 0x76);
133992443cdbSJemma Denson cx24120_writereg(state, 0xf7, 0x81);
134092443cdbSJemma Denson cx24120_writereg(state, 0xf8, 0x00);
134192443cdbSJemma Denson cx24120_writereg(state, 0xf9, 0x00);
134292443cdbSJemma Denson cx24120_writeregs(state, 0xfa, fw->data, (fw->size - 1), 0x00);
134392443cdbSJemma Denson cx24120_writereg(state, 0xf7, 0xc0);
134492443cdbSJemma Denson cx24120_writereg(state, 0xe0, 0x00);
13451668797dSJemma Denson reg = (fw->size - 2) & 0x00ff;
13461668797dSJemma Denson cx24120_writereg(state, 0xf8, reg);
13471668797dSJemma Denson reg = ((fw->size - 2) >> 8) & 0x00ff;
13481668797dSJemma Denson cx24120_writereg(state, 0xf9, reg);
134992443cdbSJemma Denson cx24120_writereg(state, 0xf7, 0x00);
135092443cdbSJemma Denson cx24120_writereg(state, 0xdc, 0x00);
135192443cdbSJemma Denson cx24120_writereg(state, 0xdc, 0x07);
13525afc9a25SJemma Denson msleep(500);
13535afc9a25SJemma Denson
13545afc9a25SJemma Denson /* Check final byte matches final byte of firmware */
13551668797dSJemma Denson reg = cx24120_readreg(state, 0xe1);
13561668797dSJemma Denson if (reg == fw->data[fw->size - 1]) {
13572f3f07fbSJemma Denson dev_dbg(&state->i2c->dev, "Firmware uploaded successfully\n");
13584133601cSJemma Denson ret = 0;
13595afc9a25SJemma Denson } else {
13605afc9a25SJemma Denson err("Firmware upload failed. Last byte returned=0x%x\n", ret);
13614133601cSJemma Denson ret = -EREMOTEIO;
13625afc9a25SJemma Denson }
136392443cdbSJemma Denson cx24120_writereg(state, 0xdc, 0x00);
13645afc9a25SJemma Denson release_firmware(fw);
13654133601cSJemma Denson if (ret != 0)
13664133601cSJemma Denson return ret;
13675afc9a25SJemma Denson
13685afc9a25SJemma Denson /* Start tuner */
13695afc9a25SJemma Denson cmd.id = CMD_START_TUNER;
13705afc9a25SJemma Denson cmd.len = 3;
13715afc9a25SJemma Denson cmd.arg[0] = 0x00;
13725afc9a25SJemma Denson cmd.arg[1] = 0x00;
13735afc9a25SJemma Denson cmd.arg[2] = 0x00;
13745afc9a25SJemma Denson
13755afc9a25SJemma Denson if (cx24120_message_send(state, &cmd) != 0) {
13765afc9a25SJemma Denson err("Error tuner start! :(\n");
13775afc9a25SJemma Denson return -EREMOTEIO;
13785afc9a25SJemma Denson }
13795afc9a25SJemma Denson
13805afc9a25SJemma Denson /* Set VCO */
1381c84251bbSJemma Denson ret = cx24120_set_vco(state);
1382c84251bbSJemma Denson if (ret != 0) {
13835afc9a25SJemma Denson err("Error set VCO! :(\n");
1384c84251bbSJemma Denson return ret;
13855afc9a25SJemma Denson }
13865afc9a25SJemma Denson
13875afc9a25SJemma Denson /* set bandwidth */
13885afc9a25SJemma Denson cmd.id = CMD_BANDWIDTH;
13895afc9a25SJemma Denson cmd.len = 12;
13905afc9a25SJemma Denson cmd.arg[0] = 0x00;
13915afc9a25SJemma Denson cmd.arg[1] = 0x00;
13925afc9a25SJemma Denson cmd.arg[2] = 0x00;
13935afc9a25SJemma Denson cmd.arg[3] = 0x00;
13945afc9a25SJemma Denson cmd.arg[4] = 0x05;
13955afc9a25SJemma Denson cmd.arg[5] = 0x02;
13965afc9a25SJemma Denson cmd.arg[6] = 0x02;
13975afc9a25SJemma Denson cmd.arg[7] = 0x00;
13985afc9a25SJemma Denson cmd.arg[8] = 0x05;
13995afc9a25SJemma Denson cmd.arg[9] = 0x02;
14005afc9a25SJemma Denson cmd.arg[10] = 0x02;
14015afc9a25SJemma Denson cmd.arg[11] = 0x00;
14025afc9a25SJemma Denson
14035afc9a25SJemma Denson if (cx24120_message_send(state, &cmd)) {
14045afc9a25SJemma Denson err("Error set bandwidth!\n");
14055afc9a25SJemma Denson return -EREMOTEIO;
14065afc9a25SJemma Denson }
14075afc9a25SJemma Denson
14081668797dSJemma Denson reg = cx24120_readreg(state, 0xba);
14091668797dSJemma Denson if (reg > 3) {
14102f3f07fbSJemma Denson dev_dbg(&state->i2c->dev, "Reset-readreg 0xba: %x\n", ret);
14115afc9a25SJemma Denson err("Error initialising tuner!\n");
14125afc9a25SJemma Denson return -EREMOTEIO;
14135afc9a25SJemma Denson }
14145afc9a25SJemma Denson
14152f3f07fbSJemma Denson dev_dbg(&state->i2c->dev, "Tuner initialised correctly.\n");
14165afc9a25SJemma Denson
14175afc9a25SJemma Denson /* Initialise mpeg outputs */
141892443cdbSJemma Denson cx24120_writereg(state, 0xeb, 0x0a);
14195afc9a25SJemma Denson if (cx24120_msg_mpeg_output_global_config(state, 0) ||
14205afc9a25SJemma Denson cx24120_msg_mpeg_output_config(state, 0) ||
14215afc9a25SJemma Denson cx24120_msg_mpeg_output_config(state, 1) ||
14225afc9a25SJemma Denson cx24120_msg_mpeg_output_config(state, 2)) {
14235afc9a25SJemma Denson err("Error initialising mpeg output. :(\n");
14245afc9a25SJemma Denson return -EREMOTEIO;
14255afc9a25SJemma Denson }
14265afc9a25SJemma Denson
1427ddcb252eSJemma Denson /* Set size of BER window */
1428ddcb252eSJemma Denson cmd.id = CMD_BER_CTRL;
14295afc9a25SJemma Denson cmd.len = 3;
14305afc9a25SJemma Denson cmd.arg[0] = 0x00;
1431ddcb252eSJemma Denson cmd.arg[1] = CX24120_BER_WINDOW;
1432ddcb252eSJemma Denson cmd.arg[2] = CX24120_BER_WINDOW;
14335afc9a25SJemma Denson if (cx24120_message_send(state, &cmd)) {
1434ddcb252eSJemma Denson err("Error setting ber window\n");
14355afc9a25SJemma Denson return -EREMOTEIO;
14365afc9a25SJemma Denson }
14375afc9a25SJemma Denson
14385afc9a25SJemma Denson /* Firmware CMD 35: Get firmware version */
14395afc9a25SJemma Denson cmd.id = CMD_FWVERSION;
14405afc9a25SJemma Denson cmd.len = 1;
14415afc9a25SJemma Denson for (i = 0; i < 4; i++) {
14425afc9a25SJemma Denson cmd.arg[0] = i;
14435afc9a25SJemma Denson ret = cx24120_message_send(state, &cmd);
14445afc9a25SJemma Denson if (ret != 0)
14455afc9a25SJemma Denson return ret;
14465afc9a25SJemma Denson vers[i] = cx24120_readreg(state, CX24120_REG_MAILBOX);
14475afc9a25SJemma Denson }
14485afc9a25SJemma Denson info("FW version %i.%i.%i.%i\n", vers[0], vers[1], vers[2], vers[3]);
14495afc9a25SJemma Denson
1450d3cf06bbSJemma Denson /* init stats here in order signal app which stats are supported */
1451d3cf06bbSJemma Denson c->strength.len = 1;
1452d3cf06bbSJemma Denson c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
14533b5eb504SJemma Denson c->cnr.len = 1;
14543b5eb504SJemma Denson c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
1455ddcb252eSJemma Denson c->post_bit_error.len = 1;
1456ddcb252eSJemma Denson c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
1457ddcb252eSJemma Denson c->post_bit_count.len = 1;
1458ddcb252eSJemma Denson c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
145980e9710bSJemma Denson c->block_error.len = 1;
146080e9710bSJemma Denson c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
146180e9710bSJemma Denson c->block_count.len = 1;
146280e9710bSJemma Denson c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
146380e9710bSJemma Denson
14645afc9a25SJemma Denson state->cold_init = 1;
146571df6731SPatrick Boettcher
14665afc9a25SJemma Denson return 0;
14675afc9a25SJemma Denson }
14685afc9a25SJemma Denson
cx24120_tune(struct dvb_frontend * fe,bool re_tune,unsigned int mode_flags,unsigned int * delay,enum fe_status * status)14695afc9a25SJemma Denson static int cx24120_tune(struct dvb_frontend *fe, bool re_tune,
14701ff2e8edSPatrick Boettcher unsigned int mode_flags, unsigned int *delay,
14710df289a2SMauro Carvalho Chehab enum fe_status *status)
14725afc9a25SJemma Denson {
14735afc9a25SJemma Denson struct cx24120_state *state = fe->demodulator_priv;
14745afc9a25SJemma Denson int ret;
14755afc9a25SJemma Denson
14762f3f07fbSJemma Denson dev_dbg(&state->i2c->dev, "(%d)\n", re_tune);
14775afc9a25SJemma Denson
14785afc9a25SJemma Denson /* TODO: Do we need to set delay? */
14795afc9a25SJemma Denson
14805afc9a25SJemma Denson if (re_tune) {
14815afc9a25SJemma Denson ret = cx24120_set_frontend(fe);
14825afc9a25SJemma Denson if (ret)
14835afc9a25SJemma Denson return ret;
14845afc9a25SJemma Denson }
14855afc9a25SJemma Denson
14865afc9a25SJemma Denson return cx24120_read_status(fe, status);
14875afc9a25SJemma Denson }
14885afc9a25SJemma Denson
cx24120_get_algo(struct dvb_frontend * fe)14898d718e53SLuc Van Oostenryck static enum dvbfe_algo cx24120_get_algo(struct dvb_frontend *fe)
14905afc9a25SJemma Denson {
14915afc9a25SJemma Denson return DVBFE_ALGO_HW;
14925afc9a25SJemma Denson }
14935afc9a25SJemma Denson
cx24120_sleep(struct dvb_frontend * fe)14945afc9a25SJemma Denson static int cx24120_sleep(struct dvb_frontend *fe)
14955afc9a25SJemma Denson {
14965afc9a25SJemma Denson return 0;
14975afc9a25SJemma Denson }
14985afc9a25SJemma Denson
cx24120_get_frontend(struct dvb_frontend * fe,struct dtv_frontend_properties * c)14997e3e68bcSMauro Carvalho Chehab static int cx24120_get_frontend(struct dvb_frontend *fe,
15007e3e68bcSMauro Carvalho Chehab struct dtv_frontend_properties *c)
15015afc9a25SJemma Denson {
15025afc9a25SJemma Denson struct cx24120_state *state = fe->demodulator_priv;
15035afc9a25SJemma Denson u8 freq1, freq2, freq3;
1504035cad57SJemma Denson int status;
15055afc9a25SJemma Denson
15062f3f07fbSJemma Denson dev_dbg(&state->i2c->dev, "\n");
15075afc9a25SJemma Denson
15085afc9a25SJemma Denson /* don't return empty data if we're not tuned in */
1509035cad57SJemma Denson status = cx24120_readreg(state, CX24120_REG_STATUS);
1510035cad57SJemma Denson if (!(status & CX24120_HAS_LOCK))
15115afc9a25SJemma Denson return 0;
15125afc9a25SJemma Denson
15135afc9a25SJemma Denson /* Get frequency */
15145afc9a25SJemma Denson freq1 = cx24120_readreg(state, CX24120_REG_FREQ1);
15155afc9a25SJemma Denson freq2 = cx24120_readreg(state, CX24120_REG_FREQ2);
15165afc9a25SJemma Denson freq3 = cx24120_readreg(state, CX24120_REG_FREQ3);
15175afc9a25SJemma Denson c->frequency = (freq3 << 16) | (freq2 << 8) | freq1;
15182f3f07fbSJemma Denson dev_dbg(&state->i2c->dev, "frequency = %d\n", c->frequency);
15195afc9a25SJemma Denson
15205afc9a25SJemma Denson /* Get modulation, fec, pilot */
15215afc9a25SJemma Denson cx24120_get_fec(fe);
15225afc9a25SJemma Denson
15235afc9a25SJemma Denson return 0;
15245afc9a25SJemma Denson }
15255afc9a25SJemma Denson
cx24120_release(struct dvb_frontend * fe)15265afc9a25SJemma Denson static void cx24120_release(struct dvb_frontend *fe)
15275afc9a25SJemma Denson {
15285afc9a25SJemma Denson struct cx24120_state *state = fe->demodulator_priv;
15295afc9a25SJemma Denson
15302f3f07fbSJemma Denson dev_dbg(&state->i2c->dev, "Clear state structure\n");
15315afc9a25SJemma Denson kfree(state);
15325afc9a25SJemma Denson }
15335afc9a25SJemma Denson
cx24120_read_ucblocks(struct dvb_frontend * fe,u32 * ucblocks)15345afc9a25SJemma Denson static int cx24120_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
15355afc9a25SJemma Denson {
15365afc9a25SJemma Denson struct cx24120_state *state = fe->demodulator_priv;
153780e9710bSJemma Denson struct dtv_frontend_properties *c = &fe->dtv_property_cache;
15385afc9a25SJemma Denson
153980e9710bSJemma Denson if (c->block_error.stat[0].scale != FE_SCALE_COUNTER) {
154080e9710bSJemma Denson *ucblocks = 0;
154180e9710bSJemma Denson return 0;
154280e9710bSJemma Denson }
15435afc9a25SJemma Denson
1544bf8de2d3SJemma Denson *ucblocks = c->block_error.stat[0].uvalue - state->ucb_offset;
154580e9710bSJemma Denson
15465afc9a25SJemma Denson return 0;
15475afc9a25SJemma Denson }
15485afc9a25SJemma Denson
1549bd336e63SMax Kellermann static const struct dvb_frontend_ops cx24120_ops = {
15505afc9a25SJemma Denson .delsys = { SYS_DVBS, SYS_DVBS2 },
15515afc9a25SJemma Denson .info = {
15525afc9a25SJemma Denson .name = "Conexant CX24120/CX24118",
1553f1b1eabfSMauro Carvalho Chehab .frequency_min_hz = 950 * MHz,
1554f1b1eabfSMauro Carvalho Chehab .frequency_max_hz = 2150 * MHz,
1555f1b1eabfSMauro Carvalho Chehab .frequency_stepsize_hz = 1011 * kHz,
1556f1b1eabfSMauro Carvalho Chehab .frequency_tolerance_hz = 5 * MHz,
15575afc9a25SJemma Denson .symbol_rate_min = 1000000,
15585afc9a25SJemma Denson .symbol_rate_max = 45000000,
15595afc9a25SJemma Denson .caps = FE_CAN_INVERSION_AUTO |
15605afc9a25SJemma Denson FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
15615afc9a25SJemma Denson FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
15625afc9a25SJemma Denson FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
15635afc9a25SJemma Denson FE_CAN_2G_MODULATION |
15645afc9a25SJemma Denson FE_CAN_QPSK | FE_CAN_RECOVER
15655afc9a25SJemma Denson },
15665afc9a25SJemma Denson .release = cx24120_release,
15675afc9a25SJemma Denson
15685afc9a25SJemma Denson .init = cx24120_init,
15695afc9a25SJemma Denson .sleep = cx24120_sleep,
15705afc9a25SJemma Denson
15715afc9a25SJemma Denson .tune = cx24120_tune,
15725afc9a25SJemma Denson .get_frontend_algo = cx24120_get_algo,
15735afc9a25SJemma Denson .set_frontend = cx24120_set_frontend,
15745afc9a25SJemma Denson
15755afc9a25SJemma Denson .get_frontend = cx24120_get_frontend,
15765afc9a25SJemma Denson .read_status = cx24120_read_status,
15775afc9a25SJemma Denson .read_ber = cx24120_read_ber,
15785afc9a25SJemma Denson .read_signal_strength = cx24120_read_signal_strength,
15795afc9a25SJemma Denson .read_snr = cx24120_read_snr,
15805afc9a25SJemma Denson .read_ucblocks = cx24120_read_ucblocks,
15815afc9a25SJemma Denson
15825afc9a25SJemma Denson .diseqc_send_master_cmd = cx24120_send_diseqc_msg,
15835afc9a25SJemma Denson
15845afc9a25SJemma Denson .diseqc_send_burst = cx24120_diseqc_send_burst,
15855afc9a25SJemma Denson .set_tone = cx24120_set_tone,
15865afc9a25SJemma Denson .set_voltage = cx24120_set_voltage,
15875afc9a25SJemma Denson };
15885afc9a25SJemma Denson
15895afc9a25SJemma Denson MODULE_DESCRIPTION("DVB Frontend module for Conexant CX24120/CX24118 hardware");
15905afc9a25SJemma Denson MODULE_AUTHOR("Jemma Denson");
15915afc9a25SJemma Denson MODULE_LICENSE("GPL");
1592