18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci    Conexant cx24116/cx24118 - DVBS/S2 Satellite demod/tuner driver
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci    Copyright (C) 2006-2008 Steven Toth <stoth@hauppauge.com>
68c2ecf20Sopenharmony_ci    Copyright (C) 2006-2007 Georg Acher
78c2ecf20Sopenharmony_ci    Copyright (C) 2007-2008 Darron Broad
88c2ecf20Sopenharmony_ci	March 2007
98c2ecf20Sopenharmony_ci	    Fixed some bugs.
108c2ecf20Sopenharmony_ci	    Added diseqc support.
118c2ecf20Sopenharmony_ci	    Added corrected signal strength support.
128c2ecf20Sopenharmony_ci	August 2007
138c2ecf20Sopenharmony_ci	    Sync with legacy version.
148c2ecf20Sopenharmony_ci	    Some clean ups.
158c2ecf20Sopenharmony_ci    Copyright (C) 2008 Igor Liplianin
168c2ecf20Sopenharmony_ci	September, 9th 2008
178c2ecf20Sopenharmony_ci	    Fixed locking on high symbol rates (>30000).
188c2ecf20Sopenharmony_ci	    Implement MPEG initialization parameter.
198c2ecf20Sopenharmony_ci	January, 17th 2009
208c2ecf20Sopenharmony_ci	    Fill set_voltage with actually control voltage code.
218c2ecf20Sopenharmony_ci	    Correct set tone to not affect voltage.
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci*/
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include <linux/slab.h>
268c2ecf20Sopenharmony_ci#include <linux/kernel.h>
278c2ecf20Sopenharmony_ci#include <linux/module.h>
288c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
298c2ecf20Sopenharmony_ci#include <linux/init.h>
308c2ecf20Sopenharmony_ci#include <linux/firmware.h>
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h>
338c2ecf20Sopenharmony_ci#include "cx24116.h"
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic int debug;
368c2ecf20Sopenharmony_cimodule_param(debug, int, 0644);
378c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#define dprintk(args...) \
408c2ecf20Sopenharmony_ci	do { \
418c2ecf20Sopenharmony_ci		if (debug) \
428c2ecf20Sopenharmony_ci			printk(KERN_INFO "cx24116: " args); \
438c2ecf20Sopenharmony_ci	} while (0)
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#define CX24116_DEFAULT_FIRMWARE "dvb-fe-cx24116.fw"
468c2ecf20Sopenharmony_ci#define CX24116_SEARCH_RANGE_KHZ 5000
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci/* known registers */
498c2ecf20Sopenharmony_ci#define CX24116_REG_COMMAND (0x00)      /* command args 0x00..0x1e */
508c2ecf20Sopenharmony_ci#define CX24116_REG_EXECUTE (0x1f)      /* execute command */
518c2ecf20Sopenharmony_ci#define CX24116_REG_MAILBOX (0x96)      /* FW or multipurpose mailbox? */
528c2ecf20Sopenharmony_ci#define CX24116_REG_RESET   (0x20)      /* reset status > 0     */
538c2ecf20Sopenharmony_ci#define CX24116_REG_SIGNAL  (0x9e)      /* signal low           */
548c2ecf20Sopenharmony_ci#define CX24116_REG_SSTATUS (0x9d)      /* signal high / status */
558c2ecf20Sopenharmony_ci#define CX24116_REG_QUALITY8 (0xa3)
568c2ecf20Sopenharmony_ci#define CX24116_REG_QSTATUS (0xbc)
578c2ecf20Sopenharmony_ci#define CX24116_REG_QUALITY0 (0xd5)
588c2ecf20Sopenharmony_ci#define CX24116_REG_BER0    (0xc9)
598c2ecf20Sopenharmony_ci#define CX24116_REG_BER8    (0xc8)
608c2ecf20Sopenharmony_ci#define CX24116_REG_BER16   (0xc7)
618c2ecf20Sopenharmony_ci#define CX24116_REG_BER24   (0xc6)
628c2ecf20Sopenharmony_ci#define CX24116_REG_UCB0    (0xcb)
638c2ecf20Sopenharmony_ci#define CX24116_REG_UCB8    (0xca)
648c2ecf20Sopenharmony_ci#define CX24116_REG_CLKDIV  (0xf3)
658c2ecf20Sopenharmony_ci#define CX24116_REG_RATEDIV (0xf9)
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci/* configured fec (not tuned) or actual FEC (tuned) 1=1/2 2=2/3 etc */
688c2ecf20Sopenharmony_ci#define CX24116_REG_FECSTATUS (0x9c)
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci/* FECSTATUS bits */
718c2ecf20Sopenharmony_ci/* mask to determine configured fec (not tuned) or actual fec (tuned) */
728c2ecf20Sopenharmony_ci#define CX24116_FEC_FECMASK   (0x1f)
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci/* Select DVB-S demodulator, else DVB-S2 */
758c2ecf20Sopenharmony_ci#define CX24116_FEC_DVBS      (0x20)
768c2ecf20Sopenharmony_ci#define CX24116_FEC_UNKNOWN   (0x40)    /* Unknown/unused */
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci/* Pilot mode requested when tuning else always reset when tuned */
798c2ecf20Sopenharmony_ci#define CX24116_FEC_PILOT     (0x80)
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci/* arg buffer size */
828c2ecf20Sopenharmony_ci#define CX24116_ARGLEN (0x1e)
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci/* rolloff */
858c2ecf20Sopenharmony_ci#define CX24116_ROLLOFF_020 (0x00)
868c2ecf20Sopenharmony_ci#define CX24116_ROLLOFF_025 (0x01)
878c2ecf20Sopenharmony_ci#define CX24116_ROLLOFF_035 (0x02)
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci/* pilot bit */
908c2ecf20Sopenharmony_ci#define CX24116_PILOT_OFF (0x00)
918c2ecf20Sopenharmony_ci#define CX24116_PILOT_ON (0x40)
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci/* signal status */
948c2ecf20Sopenharmony_ci#define CX24116_HAS_SIGNAL   (0x01)
958c2ecf20Sopenharmony_ci#define CX24116_HAS_CARRIER  (0x02)
968c2ecf20Sopenharmony_ci#define CX24116_HAS_VITERBI  (0x04)
978c2ecf20Sopenharmony_ci#define CX24116_HAS_SYNCLOCK (0x08)
988c2ecf20Sopenharmony_ci#define CX24116_HAS_UNKNOWN1 (0x10)
998c2ecf20Sopenharmony_ci#define CX24116_HAS_UNKNOWN2 (0x20)
1008c2ecf20Sopenharmony_ci#define CX24116_STATUS_MASK  (0x0f)
1018c2ecf20Sopenharmony_ci#define CX24116_SIGNAL_MASK  (0xc0)
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci#define CX24116_DISEQC_TONEOFF   (0)    /* toneburst never sent */
1048c2ecf20Sopenharmony_ci#define CX24116_DISEQC_TONECACHE (1)    /* toneburst cached     */
1058c2ecf20Sopenharmony_ci#define CX24116_DISEQC_MESGCACHE (2)    /* message cached       */
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci/* arg offset for DiSEqC */
1088c2ecf20Sopenharmony_ci#define CX24116_DISEQC_BURST  (1)
1098c2ecf20Sopenharmony_ci#define CX24116_DISEQC_ARG2_2 (2)   /* unknown value=2 */
1108c2ecf20Sopenharmony_ci#define CX24116_DISEQC_ARG3_0 (3)   /* unknown value=0 */
1118c2ecf20Sopenharmony_ci#define CX24116_DISEQC_ARG4_0 (4)   /* unknown value=0 */
1128c2ecf20Sopenharmony_ci#define CX24116_DISEQC_MSGLEN (5)
1138c2ecf20Sopenharmony_ci#define CX24116_DISEQC_MSGOFS (6)
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci/* DiSEqC burst */
1168c2ecf20Sopenharmony_ci#define CX24116_DISEQC_MINI_A (0)
1178c2ecf20Sopenharmony_ci#define CX24116_DISEQC_MINI_B (1)
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci/* DiSEqC tone burst */
1208c2ecf20Sopenharmony_cistatic int toneburst = 1;
1218c2ecf20Sopenharmony_cimodule_param(toneburst, int, 0644);
1228c2ecf20Sopenharmony_ciMODULE_PARM_DESC(toneburst, "DiSEqC toneburst 0=OFF, 1=TONE CACHE, "\
1238c2ecf20Sopenharmony_ci	"2=MESSAGE CACHE (default:1)");
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci/* SNR measurements */
1268c2ecf20Sopenharmony_cistatic int esno_snr;
1278c2ecf20Sopenharmony_cimodule_param(esno_snr, int, 0644);
1288c2ecf20Sopenharmony_ciMODULE_PARM_DESC(esno_snr, "SNR return units, 0=PERCENTAGE 0-100, "\
1298c2ecf20Sopenharmony_ci	"1=ESNO(db * 10) (default:0)");
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cienum cmds {
1328c2ecf20Sopenharmony_ci	CMD_SET_VCO     = 0x10,
1338c2ecf20Sopenharmony_ci	CMD_TUNEREQUEST = 0x11,
1348c2ecf20Sopenharmony_ci	CMD_MPEGCONFIG  = 0x13,
1358c2ecf20Sopenharmony_ci	CMD_TUNERINIT   = 0x14,
1368c2ecf20Sopenharmony_ci	CMD_BANDWIDTH   = 0x15,
1378c2ecf20Sopenharmony_ci	CMD_GETAGC      = 0x19,
1388c2ecf20Sopenharmony_ci	CMD_LNBCONFIG   = 0x20,
1398c2ecf20Sopenharmony_ci	CMD_LNBSEND     = 0x21, /* Formerly CMD_SEND_DISEQC */
1408c2ecf20Sopenharmony_ci	CMD_LNBDCLEVEL  = 0x22,
1418c2ecf20Sopenharmony_ci	CMD_SET_TONE    = 0x23,
1428c2ecf20Sopenharmony_ci	CMD_UPDFWVERS   = 0x35,
1438c2ecf20Sopenharmony_ci	CMD_TUNERSLEEP  = 0x36,
1448c2ecf20Sopenharmony_ci	CMD_AGCCONTROL  = 0x3b, /* Unknown */
1458c2ecf20Sopenharmony_ci};
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci/* The Demod/Tuner can't easily provide these, we cache them */
1488c2ecf20Sopenharmony_cistruct cx24116_tuning {
1498c2ecf20Sopenharmony_ci	u32 frequency;
1508c2ecf20Sopenharmony_ci	u32 symbol_rate;
1518c2ecf20Sopenharmony_ci	enum fe_spectral_inversion inversion;
1528c2ecf20Sopenharmony_ci	enum fe_code_rate fec;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	enum fe_delivery_system delsys;
1558c2ecf20Sopenharmony_ci	enum fe_modulation modulation;
1568c2ecf20Sopenharmony_ci	enum fe_pilot pilot;
1578c2ecf20Sopenharmony_ci	enum fe_rolloff rolloff;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	/* Demod values */
1608c2ecf20Sopenharmony_ci	u8 fec_val;
1618c2ecf20Sopenharmony_ci	u8 fec_mask;
1628c2ecf20Sopenharmony_ci	u8 inversion_val;
1638c2ecf20Sopenharmony_ci	u8 pilot_val;
1648c2ecf20Sopenharmony_ci	u8 rolloff_val;
1658c2ecf20Sopenharmony_ci};
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci/* Basic commands that are sent to the firmware */
1688c2ecf20Sopenharmony_cistruct cx24116_cmd {
1698c2ecf20Sopenharmony_ci	u8 len;
1708c2ecf20Sopenharmony_ci	u8 args[CX24116_ARGLEN];
1718c2ecf20Sopenharmony_ci};
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistruct cx24116_state {
1748c2ecf20Sopenharmony_ci	struct i2c_adapter *i2c;
1758c2ecf20Sopenharmony_ci	const struct cx24116_config *config;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	struct dvb_frontend frontend;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	struct cx24116_tuning dcur;
1808c2ecf20Sopenharmony_ci	struct cx24116_tuning dnxt;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	u8 skip_fw_load;
1838c2ecf20Sopenharmony_ci	u8 burst;
1848c2ecf20Sopenharmony_ci	struct cx24116_cmd dsec_cmd;
1858c2ecf20Sopenharmony_ci};
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_cistatic int cx24116_writereg(struct cx24116_state *state, int reg, int data)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	u8 buf[] = { reg, data };
1908c2ecf20Sopenharmony_ci	struct i2c_msg msg = { .addr = state->config->demod_address,
1918c2ecf20Sopenharmony_ci		.flags = 0, .buf = buf, .len = 2 };
1928c2ecf20Sopenharmony_ci	int err;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	if (debug > 1)
1958c2ecf20Sopenharmony_ci		printk("cx24116: %s: write reg 0x%02x, value 0x%02x\n",
1968c2ecf20Sopenharmony_ci			__func__, reg, data);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	err = i2c_transfer(state->i2c, &msg, 1);
1998c2ecf20Sopenharmony_ci	if (err != 1) {
2008c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x, value == 0x%02x)\n",
2018c2ecf20Sopenharmony_ci		       __func__, err, reg, data);
2028c2ecf20Sopenharmony_ci		return -EREMOTEIO;
2038c2ecf20Sopenharmony_ci	}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	return 0;
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci/* Bulk byte writes to a single I2C address, for 32k firmware load */
2098c2ecf20Sopenharmony_cistatic int cx24116_writeregN(struct cx24116_state *state, int reg,
2108c2ecf20Sopenharmony_ci			     const u8 *data, u16 len)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	int ret;
2138c2ecf20Sopenharmony_ci	struct i2c_msg msg;
2148c2ecf20Sopenharmony_ci	u8 *buf;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	buf = kmalloc(len + 1, GFP_KERNEL);
2178c2ecf20Sopenharmony_ci	if (!buf)
2188c2ecf20Sopenharmony_ci		return -ENOMEM;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	*(buf) = reg;
2218c2ecf20Sopenharmony_ci	memcpy(buf + 1, data, len);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	msg.addr = state->config->demod_address;
2248c2ecf20Sopenharmony_ci	msg.flags = 0;
2258c2ecf20Sopenharmony_ci	msg.buf = buf;
2268c2ecf20Sopenharmony_ci	msg.len = len + 1;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	if (debug > 1)
2298c2ecf20Sopenharmony_ci		printk(KERN_INFO "cx24116: %s:  write regN 0x%02x, len = %d\n",
2308c2ecf20Sopenharmony_ci			__func__, reg, len);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	ret = i2c_transfer(state->i2c, &msg, 1);
2338c2ecf20Sopenharmony_ci	if (ret != 1) {
2348c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x\n",
2358c2ecf20Sopenharmony_ci			 __func__, ret, reg);
2368c2ecf20Sopenharmony_ci		ret = -EREMOTEIO;
2378c2ecf20Sopenharmony_ci	}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	kfree(buf);
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	return ret;
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistatic int cx24116_readreg(struct cx24116_state *state, u8 reg)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	int ret;
2478c2ecf20Sopenharmony_ci	u8 b0[] = { reg };
2488c2ecf20Sopenharmony_ci	u8 b1[] = { 0 };
2498c2ecf20Sopenharmony_ci	struct i2c_msg msg[] = {
2508c2ecf20Sopenharmony_ci		{ .addr = state->config->demod_address, .flags = 0,
2518c2ecf20Sopenharmony_ci			.buf = b0, .len = 1 },
2528c2ecf20Sopenharmony_ci		{ .addr = state->config->demod_address, .flags = I2C_M_RD,
2538c2ecf20Sopenharmony_ci			.buf = b1, .len = 1 }
2548c2ecf20Sopenharmony_ci	};
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	ret = i2c_transfer(state->i2c, msg, 2);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	if (ret != 2) {
2598c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: reg=0x%x (error=%d)\n",
2608c2ecf20Sopenharmony_ci			__func__, reg, ret);
2618c2ecf20Sopenharmony_ci		return ret;
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	if (debug > 1)
2658c2ecf20Sopenharmony_ci		printk(KERN_INFO "cx24116: read reg 0x%02x, value 0x%02x\n",
2668c2ecf20Sopenharmony_ci			reg, b1[0]);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	return b1[0];
2698c2ecf20Sopenharmony_ci}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistatic int cx24116_set_inversion(struct cx24116_state *state,
2728c2ecf20Sopenharmony_ci	enum fe_spectral_inversion inversion)
2738c2ecf20Sopenharmony_ci{
2748c2ecf20Sopenharmony_ci	dprintk("%s(%d)\n", __func__, inversion);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	switch (inversion) {
2778c2ecf20Sopenharmony_ci	case INVERSION_OFF:
2788c2ecf20Sopenharmony_ci		state->dnxt.inversion_val = 0x00;
2798c2ecf20Sopenharmony_ci		break;
2808c2ecf20Sopenharmony_ci	case INVERSION_ON:
2818c2ecf20Sopenharmony_ci		state->dnxt.inversion_val = 0x04;
2828c2ecf20Sopenharmony_ci		break;
2838c2ecf20Sopenharmony_ci	case INVERSION_AUTO:
2848c2ecf20Sopenharmony_ci		state->dnxt.inversion_val = 0x0C;
2858c2ecf20Sopenharmony_ci		break;
2868c2ecf20Sopenharmony_ci	default:
2878c2ecf20Sopenharmony_ci		return -EINVAL;
2888c2ecf20Sopenharmony_ci	}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	state->dnxt.inversion = inversion;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	return 0;
2938c2ecf20Sopenharmony_ci}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci/*
2968c2ecf20Sopenharmony_ci * modfec (modulation and FEC)
2978c2ecf20Sopenharmony_ci * ===========================
2988c2ecf20Sopenharmony_ci *
2998c2ecf20Sopenharmony_ci * MOD          FEC             mask/val    standard
3008c2ecf20Sopenharmony_ci * ----         --------        ----------- --------
3018c2ecf20Sopenharmony_ci * QPSK         FEC_1_2         0x02 0x02+X DVB-S
3028c2ecf20Sopenharmony_ci * QPSK         FEC_2_3         0x04 0x02+X DVB-S
3038c2ecf20Sopenharmony_ci * QPSK         FEC_3_4         0x08 0x02+X DVB-S
3048c2ecf20Sopenharmony_ci * QPSK         FEC_4_5         0x10 0x02+X DVB-S (?)
3058c2ecf20Sopenharmony_ci * QPSK         FEC_5_6         0x20 0x02+X DVB-S
3068c2ecf20Sopenharmony_ci * QPSK         FEC_6_7         0x40 0x02+X DVB-S
3078c2ecf20Sopenharmony_ci * QPSK         FEC_7_8         0x80 0x02+X DVB-S
3088c2ecf20Sopenharmony_ci * QPSK         FEC_8_9         0x01 0x02+X DVB-S (?) (NOT SUPPORTED?)
3098c2ecf20Sopenharmony_ci * QPSK         AUTO            0xff 0x02+X DVB-S
3108c2ecf20Sopenharmony_ci *
3118c2ecf20Sopenharmony_ci * For DVB-S high byte probably represents FEC
3128c2ecf20Sopenharmony_ci * and low byte selects the modulator. The high
3138c2ecf20Sopenharmony_ci * byte is search range mask. Bit 5 may turn
3148c2ecf20Sopenharmony_ci * on DVB-S and remaining bits represent some
3158c2ecf20Sopenharmony_ci * kind of calibration (how/what i do not know).
3168c2ecf20Sopenharmony_ci *
3178c2ecf20Sopenharmony_ci * Eg.(2/3) szap "Zone Horror"
3188c2ecf20Sopenharmony_ci *
3198c2ecf20Sopenharmony_ci * mask/val = 0x04, 0x20
3208c2ecf20Sopenharmony_ci * status 1f | signal c3c0 | snr a333 | ber 00000098 | unc 0 | FE_HAS_LOCK
3218c2ecf20Sopenharmony_ci *
3228c2ecf20Sopenharmony_ci * mask/val = 0x04, 0x30
3238c2ecf20Sopenharmony_ci * status 1f | signal c3c0 | snr a333 | ber 00000000 | unc 0 | FE_HAS_LOCK
3248c2ecf20Sopenharmony_ci *
3258c2ecf20Sopenharmony_ci * After tuning FECSTATUS contains actual FEC
3268c2ecf20Sopenharmony_ci * in use numbered 1 through to 8 for 1/2 .. 2/3 etc
3278c2ecf20Sopenharmony_ci *
3288c2ecf20Sopenharmony_ci * NBC=NOT/NON BACKWARD COMPATIBLE WITH DVB-S (DVB-S2 only)
3298c2ecf20Sopenharmony_ci *
3308c2ecf20Sopenharmony_ci * NBC-QPSK     FEC_1_2         0x00, 0x04      DVB-S2
3318c2ecf20Sopenharmony_ci * NBC-QPSK     FEC_3_5         0x00, 0x05      DVB-S2
3328c2ecf20Sopenharmony_ci * NBC-QPSK     FEC_2_3         0x00, 0x06      DVB-S2
3338c2ecf20Sopenharmony_ci * NBC-QPSK     FEC_3_4         0x00, 0x07      DVB-S2
3348c2ecf20Sopenharmony_ci * NBC-QPSK     FEC_4_5         0x00, 0x08      DVB-S2
3358c2ecf20Sopenharmony_ci * NBC-QPSK     FEC_5_6         0x00, 0x09      DVB-S2
3368c2ecf20Sopenharmony_ci * NBC-QPSK     FEC_8_9         0x00, 0x0a      DVB-S2
3378c2ecf20Sopenharmony_ci * NBC-QPSK     FEC_9_10        0x00, 0x0b      DVB-S2
3388c2ecf20Sopenharmony_ci *
3398c2ecf20Sopenharmony_ci * NBC-8PSK     FEC_3_5         0x00, 0x0c      DVB-S2
3408c2ecf20Sopenharmony_ci * NBC-8PSK     FEC_2_3         0x00, 0x0d      DVB-S2
3418c2ecf20Sopenharmony_ci * NBC-8PSK     FEC_3_4         0x00, 0x0e      DVB-S2
3428c2ecf20Sopenharmony_ci * NBC-8PSK     FEC_5_6         0x00, 0x0f      DVB-S2
3438c2ecf20Sopenharmony_ci * NBC-8PSK     FEC_8_9         0x00, 0x10      DVB-S2
3448c2ecf20Sopenharmony_ci * NBC-8PSK     FEC_9_10        0x00, 0x11      DVB-S2
3458c2ecf20Sopenharmony_ci *
3468c2ecf20Sopenharmony_ci * For DVB-S2 low bytes selects both modulator
3478c2ecf20Sopenharmony_ci * and FEC. High byte is meaningless here. To
3488c2ecf20Sopenharmony_ci * set pilot, bit 6 (0x40) is set. When inspecting
3498c2ecf20Sopenharmony_ci * FECSTATUS bit 7 (0x80) represents the pilot
3508c2ecf20Sopenharmony_ci * selection whilst not tuned. When tuned, actual FEC
3518c2ecf20Sopenharmony_ci * in use is found in FECSTATUS as per above. Pilot
3528c2ecf20Sopenharmony_ci * value is reset.
3538c2ecf20Sopenharmony_ci */
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci/* A table of modulation, fec and configuration bytes for the demod.
3568c2ecf20Sopenharmony_ci * Not all S2 mmodulation schemes are support and not all rates with
3578c2ecf20Sopenharmony_ci * a scheme are support. Especially, no auto detect when in S2 mode.
3588c2ecf20Sopenharmony_ci */
3598c2ecf20Sopenharmony_cistatic struct cx24116_modfec {
3608c2ecf20Sopenharmony_ci	enum fe_delivery_system delivery_system;
3618c2ecf20Sopenharmony_ci	enum fe_modulation modulation;
3628c2ecf20Sopenharmony_ci	enum fe_code_rate fec;
3638c2ecf20Sopenharmony_ci	u8 mask;	/* In DVBS mode this is used to autodetect */
3648c2ecf20Sopenharmony_ci	u8 val;		/* Passed to the firmware to indicate mode selection */
3658c2ecf20Sopenharmony_ci} CX24116_MODFEC_MODES[] = {
3668c2ecf20Sopenharmony_ci /* QPSK. For unknown rates we set hardware to auto detect 0xfe 0x30 */
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci /*mod   fec       mask  val */
3698c2ecf20Sopenharmony_ci { SYS_DVBS, QPSK, FEC_NONE, 0xfe, 0x30 },
3708c2ecf20Sopenharmony_ci { SYS_DVBS, QPSK, FEC_1_2,  0x02, 0x2e }, /* 00000010 00101110 */
3718c2ecf20Sopenharmony_ci { SYS_DVBS, QPSK, FEC_2_3,  0x04, 0x2f }, /* 00000100 00101111 */
3728c2ecf20Sopenharmony_ci { SYS_DVBS, QPSK, FEC_3_4,  0x08, 0x30 }, /* 00001000 00110000 */
3738c2ecf20Sopenharmony_ci { SYS_DVBS, QPSK, FEC_4_5,  0xfe, 0x30 }, /* 000?0000 ?        */
3748c2ecf20Sopenharmony_ci { SYS_DVBS, QPSK, FEC_5_6,  0x20, 0x31 }, /* 00100000 00110001 */
3758c2ecf20Sopenharmony_ci { SYS_DVBS, QPSK, FEC_6_7,  0xfe, 0x30 }, /* 0?000000 ?        */
3768c2ecf20Sopenharmony_ci { SYS_DVBS, QPSK, FEC_7_8,  0x80, 0x32 }, /* 10000000 00110010 */
3778c2ecf20Sopenharmony_ci { SYS_DVBS, QPSK, FEC_8_9,  0xfe, 0x30 }, /* 0000000? ?        */
3788c2ecf20Sopenharmony_ci { SYS_DVBS, QPSK, FEC_AUTO, 0xfe, 0x30 },
3798c2ecf20Sopenharmony_ci /* NBC-QPSK */
3808c2ecf20Sopenharmony_ci { SYS_DVBS2, QPSK, FEC_1_2,  0x00, 0x04 },
3818c2ecf20Sopenharmony_ci { SYS_DVBS2, QPSK, FEC_3_5,  0x00, 0x05 },
3828c2ecf20Sopenharmony_ci { SYS_DVBS2, QPSK, FEC_2_3,  0x00, 0x06 },
3838c2ecf20Sopenharmony_ci { SYS_DVBS2, QPSK, FEC_3_4,  0x00, 0x07 },
3848c2ecf20Sopenharmony_ci { SYS_DVBS2, QPSK, FEC_4_5,  0x00, 0x08 },
3858c2ecf20Sopenharmony_ci { SYS_DVBS2, QPSK, FEC_5_6,  0x00, 0x09 },
3868c2ecf20Sopenharmony_ci { SYS_DVBS2, QPSK, FEC_8_9,  0x00, 0x0a },
3878c2ecf20Sopenharmony_ci { SYS_DVBS2, QPSK, FEC_9_10, 0x00, 0x0b },
3888c2ecf20Sopenharmony_ci /* 8PSK */
3898c2ecf20Sopenharmony_ci { SYS_DVBS2, PSK_8, FEC_3_5,  0x00, 0x0c },
3908c2ecf20Sopenharmony_ci { SYS_DVBS2, PSK_8, FEC_2_3,  0x00, 0x0d },
3918c2ecf20Sopenharmony_ci { SYS_DVBS2, PSK_8, FEC_3_4,  0x00, 0x0e },
3928c2ecf20Sopenharmony_ci { SYS_DVBS2, PSK_8, FEC_5_6,  0x00, 0x0f },
3938c2ecf20Sopenharmony_ci { SYS_DVBS2, PSK_8, FEC_8_9,  0x00, 0x10 },
3948c2ecf20Sopenharmony_ci { SYS_DVBS2, PSK_8, FEC_9_10, 0x00, 0x11 },
3958c2ecf20Sopenharmony_ci /*
3968c2ecf20Sopenharmony_ci  * `val' can be found in the FECSTATUS register when tuning.
3978c2ecf20Sopenharmony_ci  * FECSTATUS will give the actual FEC in use if tuning was successful.
3988c2ecf20Sopenharmony_ci  */
3998c2ecf20Sopenharmony_ci};
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_cistatic int cx24116_lookup_fecmod(struct cx24116_state *state,
4028c2ecf20Sopenharmony_ci	enum fe_delivery_system d, enum fe_modulation m, enum fe_code_rate f)
4038c2ecf20Sopenharmony_ci{
4048c2ecf20Sopenharmony_ci	int i, ret = -EOPNOTSUPP;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	dprintk("%s(0x%02x,0x%02x)\n", __func__, m, f);
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(CX24116_MODFEC_MODES); i++) {
4098c2ecf20Sopenharmony_ci		if ((d == CX24116_MODFEC_MODES[i].delivery_system) &&
4108c2ecf20Sopenharmony_ci			(m == CX24116_MODFEC_MODES[i].modulation) &&
4118c2ecf20Sopenharmony_ci			(f == CX24116_MODFEC_MODES[i].fec)) {
4128c2ecf20Sopenharmony_ci				ret = i;
4138c2ecf20Sopenharmony_ci				break;
4148c2ecf20Sopenharmony_ci			}
4158c2ecf20Sopenharmony_ci	}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	return ret;
4188c2ecf20Sopenharmony_ci}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_cistatic int cx24116_set_fec(struct cx24116_state *state,
4218c2ecf20Sopenharmony_ci			   enum fe_delivery_system delsys,
4228c2ecf20Sopenharmony_ci			   enum fe_modulation mod,
4238c2ecf20Sopenharmony_ci			   enum fe_code_rate fec)
4248c2ecf20Sopenharmony_ci{
4258c2ecf20Sopenharmony_ci	int ret = 0;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	dprintk("%s(0x%02x,0x%02x)\n", __func__, mod, fec);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	ret = cx24116_lookup_fecmod(state, delsys, mod, fec);
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	if (ret < 0)
4328c2ecf20Sopenharmony_ci		return ret;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	state->dnxt.fec = fec;
4358c2ecf20Sopenharmony_ci	state->dnxt.fec_val = CX24116_MODFEC_MODES[ret].val;
4368c2ecf20Sopenharmony_ci	state->dnxt.fec_mask = CX24116_MODFEC_MODES[ret].mask;
4378c2ecf20Sopenharmony_ci	dprintk("%s() mask/val = 0x%02x/0x%02x\n", __func__,
4388c2ecf20Sopenharmony_ci		state->dnxt.fec_mask, state->dnxt.fec_val);
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	return 0;
4418c2ecf20Sopenharmony_ci}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_cistatic int cx24116_set_symbolrate(struct cx24116_state *state, u32 rate)
4448c2ecf20Sopenharmony_ci{
4458c2ecf20Sopenharmony_ci	dprintk("%s(%d)\n", __func__, rate);
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	/*  check if symbol rate is within limits */
4488c2ecf20Sopenharmony_ci	if ((rate > state->frontend.ops.info.symbol_rate_max) ||
4498c2ecf20Sopenharmony_ci	    (rate < state->frontend.ops.info.symbol_rate_min)) {
4508c2ecf20Sopenharmony_ci		dprintk("%s() unsupported symbol_rate = %d\n", __func__, rate);
4518c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
4528c2ecf20Sopenharmony_ci	}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	state->dnxt.symbol_rate = rate;
4558c2ecf20Sopenharmony_ci	dprintk("%s() symbol_rate = %d\n", __func__, rate);
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	return 0;
4588c2ecf20Sopenharmony_ci}
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_cistatic int cx24116_load_firmware(struct dvb_frontend *fe,
4618c2ecf20Sopenharmony_ci	const struct firmware *fw);
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_cistatic int cx24116_firmware_ondemand(struct dvb_frontend *fe)
4648c2ecf20Sopenharmony_ci{
4658c2ecf20Sopenharmony_ci	struct cx24116_state *state = fe->demodulator_priv;
4668c2ecf20Sopenharmony_ci	const struct firmware *fw;
4678c2ecf20Sopenharmony_ci	int ret = 0;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	dprintk("%s()\n", __func__);
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	if (cx24116_readreg(state, 0x20) > 0) {
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci		if (state->skip_fw_load)
4748c2ecf20Sopenharmony_ci			return 0;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci		/* Load firmware */
4778c2ecf20Sopenharmony_ci		/* request the firmware, this will block until loaded */
4788c2ecf20Sopenharmony_ci		printk(KERN_INFO "%s: Waiting for firmware upload (%s)...\n",
4798c2ecf20Sopenharmony_ci			__func__, CX24116_DEFAULT_FIRMWARE);
4808c2ecf20Sopenharmony_ci		ret = request_firmware(&fw, CX24116_DEFAULT_FIRMWARE,
4818c2ecf20Sopenharmony_ci			state->i2c->dev.parent);
4828c2ecf20Sopenharmony_ci		printk(KERN_INFO "%s: Waiting for firmware upload(2)...\n",
4838c2ecf20Sopenharmony_ci			__func__);
4848c2ecf20Sopenharmony_ci		if (ret) {
4858c2ecf20Sopenharmony_ci			printk(KERN_ERR "%s: No firmware uploaded (timeout or file not found?)\n",
4868c2ecf20Sopenharmony_ci			       __func__);
4878c2ecf20Sopenharmony_ci			return ret;
4888c2ecf20Sopenharmony_ci		}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci		/* Make sure we don't recurse back through here
4918c2ecf20Sopenharmony_ci		 * during loading */
4928c2ecf20Sopenharmony_ci		state->skip_fw_load = 1;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci		ret = cx24116_load_firmware(fe, fw);
4958c2ecf20Sopenharmony_ci		if (ret)
4968c2ecf20Sopenharmony_ci			printk(KERN_ERR "%s: Writing firmware to device failed\n",
4978c2ecf20Sopenharmony_ci				__func__);
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci		release_firmware(fw);
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci		printk(KERN_INFO "%s: Firmware upload %s\n", __func__,
5028c2ecf20Sopenharmony_ci			ret == 0 ? "complete" : "failed");
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci		/* Ensure firmware is always loaded if required */
5058c2ecf20Sopenharmony_ci		state->skip_fw_load = 0;
5068c2ecf20Sopenharmony_ci	}
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	return ret;
5098c2ecf20Sopenharmony_ci}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci/* Take a basic firmware command structure, format it
5128c2ecf20Sopenharmony_ci * and forward it for processing
5138c2ecf20Sopenharmony_ci */
5148c2ecf20Sopenharmony_cistatic int cx24116_cmd_execute(struct dvb_frontend *fe, struct cx24116_cmd *cmd)
5158c2ecf20Sopenharmony_ci{
5168c2ecf20Sopenharmony_ci	struct cx24116_state *state = fe->demodulator_priv;
5178c2ecf20Sopenharmony_ci	int i, ret;
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	dprintk("%s()\n", __func__);
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	/* Load the firmware if required */
5228c2ecf20Sopenharmony_ci	ret = cx24116_firmware_ondemand(fe);
5238c2ecf20Sopenharmony_ci	if (ret != 0) {
5248c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s(): Unable initialise the firmware\n",
5258c2ecf20Sopenharmony_ci			__func__);
5268c2ecf20Sopenharmony_ci		return ret;
5278c2ecf20Sopenharmony_ci	}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	/* Write the command */
5308c2ecf20Sopenharmony_ci	for (i = 0; i < cmd->len ; i++) {
5318c2ecf20Sopenharmony_ci		dprintk("%s: 0x%02x == 0x%02x\n", __func__, i, cmd->args[i]);
5328c2ecf20Sopenharmony_ci		cx24116_writereg(state, i, cmd->args[i]);
5338c2ecf20Sopenharmony_ci	}
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	/* Start execution and wait for cmd to terminate */
5368c2ecf20Sopenharmony_ci	cx24116_writereg(state, CX24116_REG_EXECUTE, 0x01);
5378c2ecf20Sopenharmony_ci	while (cx24116_readreg(state, CX24116_REG_EXECUTE)) {
5388c2ecf20Sopenharmony_ci		msleep(10);
5398c2ecf20Sopenharmony_ci		if (i++ > 64) {
5408c2ecf20Sopenharmony_ci			/* Avoid looping forever if the firmware does
5418c2ecf20Sopenharmony_ci				not respond */
5428c2ecf20Sopenharmony_ci			printk(KERN_WARNING "%s() Firmware not responding\n",
5438c2ecf20Sopenharmony_ci				__func__);
5448c2ecf20Sopenharmony_ci			return -EREMOTEIO;
5458c2ecf20Sopenharmony_ci		}
5468c2ecf20Sopenharmony_ci	}
5478c2ecf20Sopenharmony_ci	return 0;
5488c2ecf20Sopenharmony_ci}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_cistatic int cx24116_load_firmware(struct dvb_frontend *fe,
5518c2ecf20Sopenharmony_ci	const struct firmware *fw)
5528c2ecf20Sopenharmony_ci{
5538c2ecf20Sopenharmony_ci	struct cx24116_state *state = fe->demodulator_priv;
5548c2ecf20Sopenharmony_ci	struct cx24116_cmd cmd;
5558c2ecf20Sopenharmony_ci	int i, ret, len, max, remaining;
5568c2ecf20Sopenharmony_ci	unsigned char vers[4];
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	dprintk("%s\n", __func__);
5598c2ecf20Sopenharmony_ci	dprintk("Firmware is %zu bytes (%02x %02x .. %02x %02x)\n",
5608c2ecf20Sopenharmony_ci			fw->size,
5618c2ecf20Sopenharmony_ci			fw->data[0],
5628c2ecf20Sopenharmony_ci			fw->data[1],
5638c2ecf20Sopenharmony_ci			fw->data[fw->size-2],
5648c2ecf20Sopenharmony_ci			fw->data[fw->size-1]);
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	/* Toggle 88x SRST pin to reset demod */
5678c2ecf20Sopenharmony_ci	if (state->config->reset_device)
5688c2ecf20Sopenharmony_ci		state->config->reset_device(fe);
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	/* Begin the firmware load process */
5718c2ecf20Sopenharmony_ci	/* Prepare the demod, load the firmware, cleanup after load */
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	/* Init PLL */
5748c2ecf20Sopenharmony_ci	cx24116_writereg(state, 0xE5, 0x00);
5758c2ecf20Sopenharmony_ci	cx24116_writereg(state, 0xF1, 0x08);
5768c2ecf20Sopenharmony_ci	cx24116_writereg(state, 0xF2, 0x13);
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	/* Start PLL */
5798c2ecf20Sopenharmony_ci	cx24116_writereg(state, 0xe0, 0x03);
5808c2ecf20Sopenharmony_ci	cx24116_writereg(state, 0xe0, 0x00);
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	/* Unknown */
5838c2ecf20Sopenharmony_ci	cx24116_writereg(state, CX24116_REG_CLKDIV, 0x46);
5848c2ecf20Sopenharmony_ci	cx24116_writereg(state, CX24116_REG_RATEDIV, 0x00);
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	/* Unknown */
5878c2ecf20Sopenharmony_ci	cx24116_writereg(state, 0xF0, 0x03);
5888c2ecf20Sopenharmony_ci	cx24116_writereg(state, 0xF4, 0x81);
5898c2ecf20Sopenharmony_ci	cx24116_writereg(state, 0xF5, 0x00);
5908c2ecf20Sopenharmony_ci	cx24116_writereg(state, 0xF6, 0x00);
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	/* Split firmware to the max I2C write len and write.
5938c2ecf20Sopenharmony_ci	 * Writes whole firmware as one write when i2c_wr_max is set to 0. */
5948c2ecf20Sopenharmony_ci	if (state->config->i2c_wr_max)
5958c2ecf20Sopenharmony_ci		max = state->config->i2c_wr_max;
5968c2ecf20Sopenharmony_ci	else
5978c2ecf20Sopenharmony_ci		max = INT_MAX; /* enough for 32k firmware */
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	for (remaining = fw->size; remaining > 0; remaining -= max - 1) {
6008c2ecf20Sopenharmony_ci		len = remaining;
6018c2ecf20Sopenharmony_ci		if (len > max - 1)
6028c2ecf20Sopenharmony_ci			len = max - 1;
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci		cx24116_writeregN(state, 0xF7, &fw->data[fw->size - remaining],
6058c2ecf20Sopenharmony_ci			len);
6068c2ecf20Sopenharmony_ci	}
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	cx24116_writereg(state, 0xF4, 0x10);
6098c2ecf20Sopenharmony_ci	cx24116_writereg(state, 0xF0, 0x00);
6108c2ecf20Sopenharmony_ci	cx24116_writereg(state, 0xF8, 0x06);
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	/* Firmware CMD 10: VCO config */
6138c2ecf20Sopenharmony_ci	cmd.args[0x00] = CMD_SET_VCO;
6148c2ecf20Sopenharmony_ci	cmd.args[0x01] = 0x05;
6158c2ecf20Sopenharmony_ci	cmd.args[0x02] = 0xdc;
6168c2ecf20Sopenharmony_ci	cmd.args[0x03] = 0xda;
6178c2ecf20Sopenharmony_ci	cmd.args[0x04] = 0xae;
6188c2ecf20Sopenharmony_ci	cmd.args[0x05] = 0xaa;
6198c2ecf20Sopenharmony_ci	cmd.args[0x06] = 0x04;
6208c2ecf20Sopenharmony_ci	cmd.args[0x07] = 0x9d;
6218c2ecf20Sopenharmony_ci	cmd.args[0x08] = 0xfc;
6228c2ecf20Sopenharmony_ci	cmd.args[0x09] = 0x06;
6238c2ecf20Sopenharmony_ci	cmd.len = 0x0a;
6248c2ecf20Sopenharmony_ci	ret = cx24116_cmd_execute(fe, &cmd);
6258c2ecf20Sopenharmony_ci	if (ret != 0)
6268c2ecf20Sopenharmony_ci		return ret;
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	cx24116_writereg(state, CX24116_REG_SSTATUS, 0x00);
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	/* Firmware CMD 14: Tuner config */
6318c2ecf20Sopenharmony_ci	cmd.args[0x00] = CMD_TUNERINIT;
6328c2ecf20Sopenharmony_ci	cmd.args[0x01] = 0x00;
6338c2ecf20Sopenharmony_ci	cmd.args[0x02] = 0x00;
6348c2ecf20Sopenharmony_ci	cmd.len = 0x03;
6358c2ecf20Sopenharmony_ci	ret = cx24116_cmd_execute(fe, &cmd);
6368c2ecf20Sopenharmony_ci	if (ret != 0)
6378c2ecf20Sopenharmony_ci		return ret;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	cx24116_writereg(state, 0xe5, 0x00);
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	/* Firmware CMD 13: MPEG config */
6428c2ecf20Sopenharmony_ci	cmd.args[0x00] = CMD_MPEGCONFIG;
6438c2ecf20Sopenharmony_ci	cmd.args[0x01] = 0x01;
6448c2ecf20Sopenharmony_ci	cmd.args[0x02] = 0x75;
6458c2ecf20Sopenharmony_ci	cmd.args[0x03] = 0x00;
6468c2ecf20Sopenharmony_ci	if (state->config->mpg_clk_pos_pol)
6478c2ecf20Sopenharmony_ci		cmd.args[0x04] = state->config->mpg_clk_pos_pol;
6488c2ecf20Sopenharmony_ci	else
6498c2ecf20Sopenharmony_ci		cmd.args[0x04] = 0x02;
6508c2ecf20Sopenharmony_ci	cmd.args[0x05] = 0x00;
6518c2ecf20Sopenharmony_ci	cmd.len = 0x06;
6528c2ecf20Sopenharmony_ci	ret = cx24116_cmd_execute(fe, &cmd);
6538c2ecf20Sopenharmony_ci	if (ret != 0)
6548c2ecf20Sopenharmony_ci		return ret;
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	/* Firmware CMD 35: Get firmware version */
6578c2ecf20Sopenharmony_ci	cmd.args[0x00] = CMD_UPDFWVERS;
6588c2ecf20Sopenharmony_ci	cmd.len = 0x02;
6598c2ecf20Sopenharmony_ci	for (i = 0; i < 4; i++) {
6608c2ecf20Sopenharmony_ci		cmd.args[0x01] = i;
6618c2ecf20Sopenharmony_ci		ret = cx24116_cmd_execute(fe, &cmd);
6628c2ecf20Sopenharmony_ci		if (ret != 0)
6638c2ecf20Sopenharmony_ci			return ret;
6648c2ecf20Sopenharmony_ci		vers[i] = cx24116_readreg(state, CX24116_REG_MAILBOX);
6658c2ecf20Sopenharmony_ci	}
6668c2ecf20Sopenharmony_ci	printk(KERN_INFO "%s: FW version %i.%i.%i.%i\n", __func__,
6678c2ecf20Sopenharmony_ci		vers[0], vers[1], vers[2], vers[3]);
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	return 0;
6708c2ecf20Sopenharmony_ci}
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_cistatic int cx24116_read_status(struct dvb_frontend *fe, enum fe_status *status)
6738c2ecf20Sopenharmony_ci{
6748c2ecf20Sopenharmony_ci	struct cx24116_state *state = fe->demodulator_priv;
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	int lock = cx24116_readreg(state, CX24116_REG_SSTATUS) &
6778c2ecf20Sopenharmony_ci		CX24116_STATUS_MASK;
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	dprintk("%s: status = 0x%02x\n", __func__, lock);
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	*status = 0;
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	if (lock & CX24116_HAS_SIGNAL)
6848c2ecf20Sopenharmony_ci		*status |= FE_HAS_SIGNAL;
6858c2ecf20Sopenharmony_ci	if (lock & CX24116_HAS_CARRIER)
6868c2ecf20Sopenharmony_ci		*status |= FE_HAS_CARRIER;
6878c2ecf20Sopenharmony_ci	if (lock & CX24116_HAS_VITERBI)
6888c2ecf20Sopenharmony_ci		*status |= FE_HAS_VITERBI;
6898c2ecf20Sopenharmony_ci	if (lock & CX24116_HAS_SYNCLOCK)
6908c2ecf20Sopenharmony_ci		*status |= FE_HAS_SYNC | FE_HAS_LOCK;
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	return 0;
6938c2ecf20Sopenharmony_ci}
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_cistatic int cx24116_read_ber(struct dvb_frontend *fe, u32 *ber)
6968c2ecf20Sopenharmony_ci{
6978c2ecf20Sopenharmony_ci	struct cx24116_state *state = fe->demodulator_priv;
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	dprintk("%s()\n", __func__);
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	*ber =  (cx24116_readreg(state, CX24116_REG_BER24) << 24) |
7028c2ecf20Sopenharmony_ci		(cx24116_readreg(state, CX24116_REG_BER16) << 16) |
7038c2ecf20Sopenharmony_ci		(cx24116_readreg(state, CX24116_REG_BER8)  << 8)  |
7048c2ecf20Sopenharmony_ci		 cx24116_readreg(state, CX24116_REG_BER0);
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	return 0;
7078c2ecf20Sopenharmony_ci}
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci/* TODO Determine function and scale appropriately */
7108c2ecf20Sopenharmony_cistatic int cx24116_read_signal_strength(struct dvb_frontend *fe,
7118c2ecf20Sopenharmony_ci	u16 *signal_strength)
7128c2ecf20Sopenharmony_ci{
7138c2ecf20Sopenharmony_ci	struct cx24116_state *state = fe->demodulator_priv;
7148c2ecf20Sopenharmony_ci	struct cx24116_cmd cmd;
7158c2ecf20Sopenharmony_ci	int ret;
7168c2ecf20Sopenharmony_ci	u16 sig_reading;
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	dprintk("%s()\n", __func__);
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	/* Firmware CMD 19: Get AGC */
7218c2ecf20Sopenharmony_ci	cmd.args[0x00] = CMD_GETAGC;
7228c2ecf20Sopenharmony_ci	cmd.len = 0x01;
7238c2ecf20Sopenharmony_ci	ret = cx24116_cmd_execute(fe, &cmd);
7248c2ecf20Sopenharmony_ci	if (ret != 0)
7258c2ecf20Sopenharmony_ci		return ret;
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	sig_reading =
7288c2ecf20Sopenharmony_ci		(cx24116_readreg(state,
7298c2ecf20Sopenharmony_ci			CX24116_REG_SSTATUS) & CX24116_SIGNAL_MASK) |
7308c2ecf20Sopenharmony_ci		(cx24116_readreg(state, CX24116_REG_SIGNAL) << 6);
7318c2ecf20Sopenharmony_ci	*signal_strength = 0 - sig_reading;
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	dprintk("%s: raw / cooked = 0x%04x / 0x%04x\n",
7348c2ecf20Sopenharmony_ci		__func__, sig_reading, *signal_strength);
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	return 0;
7378c2ecf20Sopenharmony_ci}
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci/* SNR (0..100)% = (sig & 0xf0) * 10 + (sig & 0x0f) * 10 / 16 */
7408c2ecf20Sopenharmony_cistatic int cx24116_read_snr_pct(struct dvb_frontend *fe, u16 *snr)
7418c2ecf20Sopenharmony_ci{
7428c2ecf20Sopenharmony_ci	struct cx24116_state *state = fe->demodulator_priv;
7438c2ecf20Sopenharmony_ci	u8 snr_reading;
7448c2ecf20Sopenharmony_ci	static const u32 snr_tab[] = { /* 10 x Table (rounded up) */
7458c2ecf20Sopenharmony_ci		0x00000, 0x0199A, 0x03333, 0x04ccD, 0x06667,
7468c2ecf20Sopenharmony_ci		0x08000, 0x0999A, 0x0b333, 0x0cccD, 0x0e667,
7478c2ecf20Sopenharmony_ci		0x10000, 0x1199A, 0x13333, 0x14ccD, 0x16667,
7488c2ecf20Sopenharmony_ci		0x18000 };
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	dprintk("%s()\n", __func__);
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	snr_reading = cx24116_readreg(state, CX24116_REG_QUALITY0);
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	if (snr_reading >= 0xa0 /* 100% */)
7558c2ecf20Sopenharmony_ci		*snr = 0xffff;
7568c2ecf20Sopenharmony_ci	else
7578c2ecf20Sopenharmony_ci		*snr = snr_tab[(snr_reading & 0xf0) >> 4] +
7588c2ecf20Sopenharmony_ci			(snr_tab[(snr_reading & 0x0f)] >> 4);
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	dprintk("%s: raw / cooked = 0x%02x / 0x%04x\n", __func__,
7618c2ecf20Sopenharmony_ci		snr_reading, *snr);
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	return 0;
7648c2ecf20Sopenharmony_ci}
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci/* The reelbox patches show the value in the registers represents
7678c2ecf20Sopenharmony_ci * ESNO, from 0->30db (values 0->300). We provide this value by
7688c2ecf20Sopenharmony_ci * default.
7698c2ecf20Sopenharmony_ci */
7708c2ecf20Sopenharmony_cistatic int cx24116_read_snr_esno(struct dvb_frontend *fe, u16 *snr)
7718c2ecf20Sopenharmony_ci{
7728c2ecf20Sopenharmony_ci	struct cx24116_state *state = fe->demodulator_priv;
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	dprintk("%s()\n", __func__);
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	*snr = cx24116_readreg(state, CX24116_REG_QUALITY8) << 8 |
7778c2ecf20Sopenharmony_ci		cx24116_readreg(state, CX24116_REG_QUALITY0);
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	dprintk("%s: raw 0x%04x\n", __func__, *snr);
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	return 0;
7828c2ecf20Sopenharmony_ci}
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_cistatic int cx24116_read_snr(struct dvb_frontend *fe, u16 *snr)
7858c2ecf20Sopenharmony_ci{
7868c2ecf20Sopenharmony_ci	if (esno_snr == 1)
7878c2ecf20Sopenharmony_ci		return cx24116_read_snr_esno(fe, snr);
7888c2ecf20Sopenharmony_ci	else
7898c2ecf20Sopenharmony_ci		return cx24116_read_snr_pct(fe, snr);
7908c2ecf20Sopenharmony_ci}
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_cistatic int cx24116_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
7938c2ecf20Sopenharmony_ci{
7948c2ecf20Sopenharmony_ci	struct cx24116_state *state = fe->demodulator_priv;
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	dprintk("%s()\n", __func__);
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	*ucblocks = (cx24116_readreg(state, CX24116_REG_UCB8) << 8) |
7998c2ecf20Sopenharmony_ci		cx24116_readreg(state, CX24116_REG_UCB0);
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	return 0;
8028c2ecf20Sopenharmony_ci}
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci/* Overwrite the current tuning params, we are about to tune */
8058c2ecf20Sopenharmony_cistatic void cx24116_clone_params(struct dvb_frontend *fe)
8068c2ecf20Sopenharmony_ci{
8078c2ecf20Sopenharmony_ci	struct cx24116_state *state = fe->demodulator_priv;
8088c2ecf20Sopenharmony_ci	state->dcur = state->dnxt;
8098c2ecf20Sopenharmony_ci}
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci/* Wait for LNB */
8128c2ecf20Sopenharmony_cistatic int cx24116_wait_for_lnb(struct dvb_frontend *fe)
8138c2ecf20Sopenharmony_ci{
8148c2ecf20Sopenharmony_ci	struct cx24116_state *state = fe->demodulator_priv;
8158c2ecf20Sopenharmony_ci	int i;
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	dprintk("%s() qstatus = 0x%02x\n", __func__,
8188c2ecf20Sopenharmony_ci		cx24116_readreg(state, CX24116_REG_QSTATUS));
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	/* Wait for up to 300 ms */
8218c2ecf20Sopenharmony_ci	for (i = 0; i < 30 ; i++) {
8228c2ecf20Sopenharmony_ci		if (cx24116_readreg(state, CX24116_REG_QSTATUS) & 0x20)
8238c2ecf20Sopenharmony_ci			return 0;
8248c2ecf20Sopenharmony_ci		msleep(10);
8258c2ecf20Sopenharmony_ci	}
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	dprintk("%s(): LNB not ready\n", __func__);
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	return -ETIMEDOUT; /* -EBUSY ? */
8308c2ecf20Sopenharmony_ci}
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_cistatic int cx24116_set_voltage(struct dvb_frontend *fe,
8338c2ecf20Sopenharmony_ci	enum fe_sec_voltage voltage)
8348c2ecf20Sopenharmony_ci{
8358c2ecf20Sopenharmony_ci	struct cx24116_cmd cmd;
8368c2ecf20Sopenharmony_ci	int ret;
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	dprintk("%s: %s\n", __func__,
8398c2ecf20Sopenharmony_ci		voltage == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" :
8408c2ecf20Sopenharmony_ci		voltage == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??");
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	/* Wait for LNB ready */
8438c2ecf20Sopenharmony_ci	ret = cx24116_wait_for_lnb(fe);
8448c2ecf20Sopenharmony_ci	if (ret != 0)
8458c2ecf20Sopenharmony_ci		return ret;
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	/* Wait for voltage/min repeat delay */
8488c2ecf20Sopenharmony_ci	msleep(100);
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	cmd.args[0x00] = CMD_LNBDCLEVEL;
8518c2ecf20Sopenharmony_ci	cmd.args[0x01] = (voltage == SEC_VOLTAGE_18 ? 0x01 : 0x00);
8528c2ecf20Sopenharmony_ci	cmd.len = 0x02;
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	/* Min delay time before DiSEqC send */
8558c2ecf20Sopenharmony_ci	msleep(15);
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci	return cx24116_cmd_execute(fe, &cmd);
8588c2ecf20Sopenharmony_ci}
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_cistatic int cx24116_set_tone(struct dvb_frontend *fe,
8618c2ecf20Sopenharmony_ci	enum fe_sec_tone_mode tone)
8628c2ecf20Sopenharmony_ci{
8638c2ecf20Sopenharmony_ci	struct cx24116_cmd cmd;
8648c2ecf20Sopenharmony_ci	int ret;
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	dprintk("%s(%d)\n", __func__, tone);
8678c2ecf20Sopenharmony_ci	if ((tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF)) {
8688c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: Invalid, tone=%d\n", __func__, tone);
8698c2ecf20Sopenharmony_ci		return -EINVAL;
8708c2ecf20Sopenharmony_ci	}
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci	/* Wait for LNB ready */
8738c2ecf20Sopenharmony_ci	ret = cx24116_wait_for_lnb(fe);
8748c2ecf20Sopenharmony_ci	if (ret != 0)
8758c2ecf20Sopenharmony_ci		return ret;
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	/* Min delay time after DiSEqC send */
8788c2ecf20Sopenharmony_ci	msleep(15); /* XXX determine is FW does this, see send_diseqc/burst */
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci	/* Now we set the tone */
8818c2ecf20Sopenharmony_ci	cmd.args[0x00] = CMD_SET_TONE;
8828c2ecf20Sopenharmony_ci	cmd.args[0x01] = 0x00;
8838c2ecf20Sopenharmony_ci	cmd.args[0x02] = 0x00;
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci	switch (tone) {
8868c2ecf20Sopenharmony_ci	case SEC_TONE_ON:
8878c2ecf20Sopenharmony_ci		dprintk("%s: setting tone on\n", __func__);
8888c2ecf20Sopenharmony_ci		cmd.args[0x03] = 0x01;
8898c2ecf20Sopenharmony_ci		break;
8908c2ecf20Sopenharmony_ci	case SEC_TONE_OFF:
8918c2ecf20Sopenharmony_ci		dprintk("%s: setting tone off\n", __func__);
8928c2ecf20Sopenharmony_ci		cmd.args[0x03] = 0x00;
8938c2ecf20Sopenharmony_ci		break;
8948c2ecf20Sopenharmony_ci	}
8958c2ecf20Sopenharmony_ci	cmd.len = 0x04;
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci	/* Min delay time before DiSEqC send */
8988c2ecf20Sopenharmony_ci	msleep(15); /* XXX determine is FW does this, see send_diseqc/burst */
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci	return cx24116_cmd_execute(fe, &cmd);
9018c2ecf20Sopenharmony_ci}
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci/* Initialise DiSEqC */
9048c2ecf20Sopenharmony_cistatic int cx24116_diseqc_init(struct dvb_frontend *fe)
9058c2ecf20Sopenharmony_ci{
9068c2ecf20Sopenharmony_ci	struct cx24116_state *state = fe->demodulator_priv;
9078c2ecf20Sopenharmony_ci	struct cx24116_cmd cmd;
9088c2ecf20Sopenharmony_ci	int ret;
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci	/* Firmware CMD 20: LNB/DiSEqC config */
9118c2ecf20Sopenharmony_ci	cmd.args[0x00] = CMD_LNBCONFIG;
9128c2ecf20Sopenharmony_ci	cmd.args[0x01] = 0x00;
9138c2ecf20Sopenharmony_ci	cmd.args[0x02] = 0x10;
9148c2ecf20Sopenharmony_ci	cmd.args[0x03] = 0x00;
9158c2ecf20Sopenharmony_ci	cmd.args[0x04] = 0x8f;
9168c2ecf20Sopenharmony_ci	cmd.args[0x05] = 0x28;
9178c2ecf20Sopenharmony_ci	cmd.args[0x06] = (toneburst == CX24116_DISEQC_TONEOFF) ? 0x00 : 0x01;
9188c2ecf20Sopenharmony_ci	cmd.args[0x07] = 0x01;
9198c2ecf20Sopenharmony_ci	cmd.len = 0x08;
9208c2ecf20Sopenharmony_ci	ret = cx24116_cmd_execute(fe, &cmd);
9218c2ecf20Sopenharmony_ci	if (ret != 0)
9228c2ecf20Sopenharmony_ci		return ret;
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	/* Prepare a DiSEqC command */
9258c2ecf20Sopenharmony_ci	state->dsec_cmd.args[0x00] = CMD_LNBSEND;
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci	/* DiSEqC burst */
9288c2ecf20Sopenharmony_ci	state->dsec_cmd.args[CX24116_DISEQC_BURST]  = CX24116_DISEQC_MINI_A;
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_ci	/* Unknown */
9318c2ecf20Sopenharmony_ci	state->dsec_cmd.args[CX24116_DISEQC_ARG2_2] = 0x02;
9328c2ecf20Sopenharmony_ci	state->dsec_cmd.args[CX24116_DISEQC_ARG3_0] = 0x00;
9338c2ecf20Sopenharmony_ci	/* Continuation flag? */
9348c2ecf20Sopenharmony_ci	state->dsec_cmd.args[CX24116_DISEQC_ARG4_0] = 0x00;
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci	/* DiSEqC message length */
9378c2ecf20Sopenharmony_ci	state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] = 0x00;
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci	/* Command length */
9408c2ecf20Sopenharmony_ci	state->dsec_cmd.len = CX24116_DISEQC_MSGOFS;
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci	return 0;
9438c2ecf20Sopenharmony_ci}
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci/* Send DiSEqC message with derived burst (hack) || previous burst */
9468c2ecf20Sopenharmony_cistatic int cx24116_send_diseqc_msg(struct dvb_frontend *fe,
9478c2ecf20Sopenharmony_ci	struct dvb_diseqc_master_cmd *d)
9488c2ecf20Sopenharmony_ci{
9498c2ecf20Sopenharmony_ci	struct cx24116_state *state = fe->demodulator_priv;
9508c2ecf20Sopenharmony_ci	int i, ret;
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci	/* Validate length */
9538c2ecf20Sopenharmony_ci	if (d->msg_len > sizeof(d->msg))
9548c2ecf20Sopenharmony_ci		return -EINVAL;
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci	/* Dump DiSEqC message */
9578c2ecf20Sopenharmony_ci	if (debug) {
9588c2ecf20Sopenharmony_ci		printk(KERN_INFO "cx24116: %s(", __func__);
9598c2ecf20Sopenharmony_ci		for (i = 0 ; i < d->msg_len ;) {
9608c2ecf20Sopenharmony_ci			printk(KERN_INFO "0x%02x", d->msg[i]);
9618c2ecf20Sopenharmony_ci			if (++i < d->msg_len)
9628c2ecf20Sopenharmony_ci				printk(KERN_INFO ", ");
9638c2ecf20Sopenharmony_ci		}
9648c2ecf20Sopenharmony_ci		printk(") toneburst=%d\n", toneburst);
9658c2ecf20Sopenharmony_ci	}
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	/* DiSEqC message */
9688c2ecf20Sopenharmony_ci	for (i = 0; i < d->msg_len; i++)
9698c2ecf20Sopenharmony_ci		state->dsec_cmd.args[CX24116_DISEQC_MSGOFS + i] = d->msg[i];
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci	/* DiSEqC message length */
9728c2ecf20Sopenharmony_ci	state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] = d->msg_len;
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci	/* Command length */
9758c2ecf20Sopenharmony_ci	state->dsec_cmd.len = CX24116_DISEQC_MSGOFS +
9768c2ecf20Sopenharmony_ci		state->dsec_cmd.args[CX24116_DISEQC_MSGLEN];
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci	/* DiSEqC toneburst */
9798c2ecf20Sopenharmony_ci	if (toneburst == CX24116_DISEQC_MESGCACHE)
9808c2ecf20Sopenharmony_ci		/* Message is cached */
9818c2ecf20Sopenharmony_ci		return 0;
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	else if (toneburst == CX24116_DISEQC_TONEOFF)
9848c2ecf20Sopenharmony_ci		/* Message is sent without burst */
9858c2ecf20Sopenharmony_ci		state->dsec_cmd.args[CX24116_DISEQC_BURST] = 0;
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci	else if (toneburst == CX24116_DISEQC_TONECACHE) {
9888c2ecf20Sopenharmony_ci		/*
9898c2ecf20Sopenharmony_ci		 * Message is sent with derived else cached burst
9908c2ecf20Sopenharmony_ci		 *
9918c2ecf20Sopenharmony_ci		 * WRITE PORT GROUP COMMAND 38
9928c2ecf20Sopenharmony_ci		 *
9938c2ecf20Sopenharmony_ci		 * 0/A/A: E0 10 38 F0..F3
9948c2ecf20Sopenharmony_ci		 * 1/B/B: E0 10 38 F4..F7
9958c2ecf20Sopenharmony_ci		 * 2/C/A: E0 10 38 F8..FB
9968c2ecf20Sopenharmony_ci		 * 3/D/B: E0 10 38 FC..FF
9978c2ecf20Sopenharmony_ci		 *
9988c2ecf20Sopenharmony_ci		 * databyte[3]= 8421:8421
9998c2ecf20Sopenharmony_ci		 *              ABCD:WXYZ
10008c2ecf20Sopenharmony_ci		 *              CLR :SET
10018c2ecf20Sopenharmony_ci		 *
10028c2ecf20Sopenharmony_ci		 *              WX= PORT SELECT 0..3    (X=TONEBURST)
10038c2ecf20Sopenharmony_ci		 *              Y = VOLTAGE             (0=13V, 1=18V)
10048c2ecf20Sopenharmony_ci		 *              Z = BAND                (0=LOW, 1=HIGH(22K))
10058c2ecf20Sopenharmony_ci		 */
10068c2ecf20Sopenharmony_ci		if (d->msg_len >= 4 && d->msg[2] == 0x38)
10078c2ecf20Sopenharmony_ci			state->dsec_cmd.args[CX24116_DISEQC_BURST] =
10088c2ecf20Sopenharmony_ci				((d->msg[3] & 4) >> 2);
10098c2ecf20Sopenharmony_ci		if (debug)
10108c2ecf20Sopenharmony_ci			dprintk("%s burst=%d\n", __func__,
10118c2ecf20Sopenharmony_ci				state->dsec_cmd.args[CX24116_DISEQC_BURST]);
10128c2ecf20Sopenharmony_ci	}
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	/* Wait for LNB ready */
10158c2ecf20Sopenharmony_ci	ret = cx24116_wait_for_lnb(fe);
10168c2ecf20Sopenharmony_ci	if (ret != 0)
10178c2ecf20Sopenharmony_ci		return ret;
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci	/* Wait for voltage/min repeat delay */
10208c2ecf20Sopenharmony_ci	msleep(100);
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci	/* Command */
10238c2ecf20Sopenharmony_ci	ret = cx24116_cmd_execute(fe, &state->dsec_cmd);
10248c2ecf20Sopenharmony_ci	if (ret != 0)
10258c2ecf20Sopenharmony_ci		return ret;
10268c2ecf20Sopenharmony_ci	/*
10278c2ecf20Sopenharmony_ci	 * Wait for send
10288c2ecf20Sopenharmony_ci	 *
10298c2ecf20Sopenharmony_ci	 * Eutelsat spec:
10308c2ecf20Sopenharmony_ci	 * >15ms delay          + (XXX determine if FW does this, see set_tone)
10318c2ecf20Sopenharmony_ci	 *  13.5ms per byte     +
10328c2ecf20Sopenharmony_ci	 * >15ms delay          +
10338c2ecf20Sopenharmony_ci	 *  12.5ms burst        +
10348c2ecf20Sopenharmony_ci	 * >15ms delay            (XXX determine if FW does this, see set_tone)
10358c2ecf20Sopenharmony_ci	 */
10368c2ecf20Sopenharmony_ci	msleep((state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] << 4) +
10378c2ecf20Sopenharmony_ci		((toneburst == CX24116_DISEQC_TONEOFF) ? 30 : 60));
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci	return 0;
10408c2ecf20Sopenharmony_ci}
10418c2ecf20Sopenharmony_ci
10428c2ecf20Sopenharmony_ci/* Send DiSEqC burst */
10438c2ecf20Sopenharmony_cistatic int cx24116_diseqc_send_burst(struct dvb_frontend *fe,
10448c2ecf20Sopenharmony_ci	enum fe_sec_mini_cmd burst)
10458c2ecf20Sopenharmony_ci{
10468c2ecf20Sopenharmony_ci	struct cx24116_state *state = fe->demodulator_priv;
10478c2ecf20Sopenharmony_ci	int ret;
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci	dprintk("%s(%d) toneburst=%d\n", __func__, burst, toneburst);
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_ci	/* DiSEqC burst */
10528c2ecf20Sopenharmony_ci	if (burst == SEC_MINI_A)
10538c2ecf20Sopenharmony_ci		state->dsec_cmd.args[CX24116_DISEQC_BURST] =
10548c2ecf20Sopenharmony_ci			CX24116_DISEQC_MINI_A;
10558c2ecf20Sopenharmony_ci	else if (burst == SEC_MINI_B)
10568c2ecf20Sopenharmony_ci		state->dsec_cmd.args[CX24116_DISEQC_BURST] =
10578c2ecf20Sopenharmony_ci			CX24116_DISEQC_MINI_B;
10588c2ecf20Sopenharmony_ci	else
10598c2ecf20Sopenharmony_ci		return -EINVAL;
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci	/* DiSEqC toneburst */
10628c2ecf20Sopenharmony_ci	if (toneburst != CX24116_DISEQC_MESGCACHE)
10638c2ecf20Sopenharmony_ci		/* Burst is cached */
10648c2ecf20Sopenharmony_ci		return 0;
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ci	/* Burst is to be sent with cached message */
10678c2ecf20Sopenharmony_ci
10688c2ecf20Sopenharmony_ci	/* Wait for LNB ready */
10698c2ecf20Sopenharmony_ci	ret = cx24116_wait_for_lnb(fe);
10708c2ecf20Sopenharmony_ci	if (ret != 0)
10718c2ecf20Sopenharmony_ci		return ret;
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci	/* Wait for voltage/min repeat delay */
10748c2ecf20Sopenharmony_ci	msleep(100);
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci	/* Command */
10778c2ecf20Sopenharmony_ci	ret = cx24116_cmd_execute(fe, &state->dsec_cmd);
10788c2ecf20Sopenharmony_ci	if (ret != 0)
10798c2ecf20Sopenharmony_ci		return ret;
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci	/*
10828c2ecf20Sopenharmony_ci	 * Wait for send
10838c2ecf20Sopenharmony_ci	 *
10848c2ecf20Sopenharmony_ci	 * Eutelsat spec:
10858c2ecf20Sopenharmony_ci	 * >15ms delay          + (XXX determine if FW does this, see set_tone)
10868c2ecf20Sopenharmony_ci	 *  13.5ms per byte     +
10878c2ecf20Sopenharmony_ci	 * >15ms delay          +
10888c2ecf20Sopenharmony_ci	 *  12.5ms burst        +
10898c2ecf20Sopenharmony_ci	 * >15ms delay            (XXX determine if FW does this, see set_tone)
10908c2ecf20Sopenharmony_ci	 */
10918c2ecf20Sopenharmony_ci	msleep((state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] << 4) + 60);
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci	return 0;
10948c2ecf20Sopenharmony_ci}
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_cistatic void cx24116_release(struct dvb_frontend *fe)
10978c2ecf20Sopenharmony_ci{
10988c2ecf20Sopenharmony_ci	struct cx24116_state *state = fe->demodulator_priv;
10998c2ecf20Sopenharmony_ci	dprintk("%s\n", __func__);
11008c2ecf20Sopenharmony_ci	kfree(state);
11018c2ecf20Sopenharmony_ci}
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops cx24116_ops;
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_cistruct dvb_frontend *cx24116_attach(const struct cx24116_config *config,
11068c2ecf20Sopenharmony_ci	struct i2c_adapter *i2c)
11078c2ecf20Sopenharmony_ci{
11088c2ecf20Sopenharmony_ci	struct cx24116_state *state;
11098c2ecf20Sopenharmony_ci	int ret;
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci	dprintk("%s\n", __func__);
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci	/* allocate memory for the internal state */
11148c2ecf20Sopenharmony_ci	state = kzalloc(sizeof(*state), GFP_KERNEL);
11158c2ecf20Sopenharmony_ci	if (state == NULL)
11168c2ecf20Sopenharmony_ci		return NULL;
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci	state->config = config;
11198c2ecf20Sopenharmony_ci	state->i2c = i2c;
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci	/* check if the demod is present */
11228c2ecf20Sopenharmony_ci	ret = (cx24116_readreg(state, 0xFF) << 8) |
11238c2ecf20Sopenharmony_ci		cx24116_readreg(state, 0xFE);
11248c2ecf20Sopenharmony_ci	if (ret != 0x0501) {
11258c2ecf20Sopenharmony_ci		kfree(state);
11268c2ecf20Sopenharmony_ci		printk(KERN_INFO "Invalid probe, probably not a CX24116 device\n");
11278c2ecf20Sopenharmony_ci		return NULL;
11288c2ecf20Sopenharmony_ci	}
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_ci	/* create dvb_frontend */
11318c2ecf20Sopenharmony_ci	memcpy(&state->frontend.ops, &cx24116_ops,
11328c2ecf20Sopenharmony_ci		sizeof(struct dvb_frontend_ops));
11338c2ecf20Sopenharmony_ci	state->frontend.demodulator_priv = state;
11348c2ecf20Sopenharmony_ci	return &state->frontend;
11358c2ecf20Sopenharmony_ci}
11368c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cx24116_attach);
11378c2ecf20Sopenharmony_ci
11388c2ecf20Sopenharmony_ci/*
11398c2ecf20Sopenharmony_ci * Initialise or wake up device
11408c2ecf20Sopenharmony_ci *
11418c2ecf20Sopenharmony_ci * Power config will reset and load initial firmware if required
11428c2ecf20Sopenharmony_ci */
11438c2ecf20Sopenharmony_cistatic int cx24116_initfe(struct dvb_frontend *fe)
11448c2ecf20Sopenharmony_ci{
11458c2ecf20Sopenharmony_ci	struct cx24116_state *state = fe->demodulator_priv;
11468c2ecf20Sopenharmony_ci	struct cx24116_cmd cmd;
11478c2ecf20Sopenharmony_ci	int ret;
11488c2ecf20Sopenharmony_ci
11498c2ecf20Sopenharmony_ci	dprintk("%s()\n", __func__);
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_ci	/* Power on */
11528c2ecf20Sopenharmony_ci	cx24116_writereg(state, 0xe0, 0);
11538c2ecf20Sopenharmony_ci	cx24116_writereg(state, 0xe1, 0);
11548c2ecf20Sopenharmony_ci	cx24116_writereg(state, 0xea, 0);
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ci	/* Firmware CMD 36: Power config */
11578c2ecf20Sopenharmony_ci	cmd.args[0x00] = CMD_TUNERSLEEP;
11588c2ecf20Sopenharmony_ci	cmd.args[0x01] = 0;
11598c2ecf20Sopenharmony_ci	cmd.len = 0x02;
11608c2ecf20Sopenharmony_ci	ret = cx24116_cmd_execute(fe, &cmd);
11618c2ecf20Sopenharmony_ci	if (ret != 0)
11628c2ecf20Sopenharmony_ci		return ret;
11638c2ecf20Sopenharmony_ci
11648c2ecf20Sopenharmony_ci	ret = cx24116_diseqc_init(fe);
11658c2ecf20Sopenharmony_ci	if (ret != 0)
11668c2ecf20Sopenharmony_ci		return ret;
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci	/* HVR-4000 needs this */
11698c2ecf20Sopenharmony_ci	return cx24116_set_voltage(fe, SEC_VOLTAGE_13);
11708c2ecf20Sopenharmony_ci}
11718c2ecf20Sopenharmony_ci
11728c2ecf20Sopenharmony_ci/*
11738c2ecf20Sopenharmony_ci * Put device to sleep
11748c2ecf20Sopenharmony_ci */
11758c2ecf20Sopenharmony_cistatic int cx24116_sleep(struct dvb_frontend *fe)
11768c2ecf20Sopenharmony_ci{
11778c2ecf20Sopenharmony_ci	struct cx24116_state *state = fe->demodulator_priv;
11788c2ecf20Sopenharmony_ci	struct cx24116_cmd cmd;
11798c2ecf20Sopenharmony_ci	int ret;
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci	dprintk("%s()\n", __func__);
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci	/* Firmware CMD 36: Power config */
11848c2ecf20Sopenharmony_ci	cmd.args[0x00] = CMD_TUNERSLEEP;
11858c2ecf20Sopenharmony_ci	cmd.args[0x01] = 1;
11868c2ecf20Sopenharmony_ci	cmd.len = 0x02;
11878c2ecf20Sopenharmony_ci	ret = cx24116_cmd_execute(fe, &cmd);
11888c2ecf20Sopenharmony_ci	if (ret != 0)
11898c2ecf20Sopenharmony_ci		return ret;
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_ci	/* Power off (Shutdown clocks) */
11928c2ecf20Sopenharmony_ci	cx24116_writereg(state, 0xea, 0xff);
11938c2ecf20Sopenharmony_ci	cx24116_writereg(state, 0xe1, 1);
11948c2ecf20Sopenharmony_ci	cx24116_writereg(state, 0xe0, 1);
11958c2ecf20Sopenharmony_ci
11968c2ecf20Sopenharmony_ci	return 0;
11978c2ecf20Sopenharmony_ci}
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_ci/* dvb-core told us to tune, the tv property cache will be complete,
12008c2ecf20Sopenharmony_ci * it's safe for is to pull values and use them for tuning purposes.
12018c2ecf20Sopenharmony_ci */
12028c2ecf20Sopenharmony_cistatic int cx24116_set_frontend(struct dvb_frontend *fe)
12038c2ecf20Sopenharmony_ci{
12048c2ecf20Sopenharmony_ci	struct cx24116_state *state = fe->demodulator_priv;
12058c2ecf20Sopenharmony_ci	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
12068c2ecf20Sopenharmony_ci	struct cx24116_cmd cmd;
12078c2ecf20Sopenharmony_ci	enum fe_status tunerstat;
12088c2ecf20Sopenharmony_ci	int i, status, ret, retune = 1;
12098c2ecf20Sopenharmony_ci
12108c2ecf20Sopenharmony_ci	dprintk("%s()\n", __func__);
12118c2ecf20Sopenharmony_ci
12128c2ecf20Sopenharmony_ci	switch (c->delivery_system) {
12138c2ecf20Sopenharmony_ci	case SYS_DVBS:
12148c2ecf20Sopenharmony_ci		dprintk("%s: DVB-S delivery system selected\n", __func__);
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci		/* Only QPSK is supported for DVB-S */
12178c2ecf20Sopenharmony_ci		if (c->modulation != QPSK) {
12188c2ecf20Sopenharmony_ci			dprintk("%s: unsupported modulation selected (%d)\n",
12198c2ecf20Sopenharmony_ci				__func__, c->modulation);
12208c2ecf20Sopenharmony_ci			return -EOPNOTSUPP;
12218c2ecf20Sopenharmony_ci		}
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci		/* Pilot doesn't exist in DVB-S, turn bit off */
12248c2ecf20Sopenharmony_ci		state->dnxt.pilot_val = CX24116_PILOT_OFF;
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci		/* DVB-S only supports 0.35 */
12278c2ecf20Sopenharmony_ci		if (c->rolloff != ROLLOFF_35) {
12288c2ecf20Sopenharmony_ci			dprintk("%s: unsupported rolloff selected (%d)\n",
12298c2ecf20Sopenharmony_ci				__func__, c->rolloff);
12308c2ecf20Sopenharmony_ci			return -EOPNOTSUPP;
12318c2ecf20Sopenharmony_ci		}
12328c2ecf20Sopenharmony_ci		state->dnxt.rolloff_val = CX24116_ROLLOFF_035;
12338c2ecf20Sopenharmony_ci		break;
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_ci	case SYS_DVBS2:
12368c2ecf20Sopenharmony_ci		dprintk("%s: DVB-S2 delivery system selected\n", __func__);
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci		/*
12398c2ecf20Sopenharmony_ci		 * NBC 8PSK/QPSK with DVB-S is supported for DVB-S2,
12408c2ecf20Sopenharmony_ci		 * but not hardware auto detection
12418c2ecf20Sopenharmony_ci		 */
12428c2ecf20Sopenharmony_ci		if (c->modulation != PSK_8 && c->modulation != QPSK) {
12438c2ecf20Sopenharmony_ci			dprintk("%s: unsupported modulation selected (%d)\n",
12448c2ecf20Sopenharmony_ci				__func__, c->modulation);
12458c2ecf20Sopenharmony_ci			return -EOPNOTSUPP;
12468c2ecf20Sopenharmony_ci		}
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_ci		switch (c->pilot) {
12498c2ecf20Sopenharmony_ci		case PILOT_AUTO:	/* Not supported but emulated */
12508c2ecf20Sopenharmony_ci			state->dnxt.pilot_val = (c->modulation == QPSK)
12518c2ecf20Sopenharmony_ci				? CX24116_PILOT_OFF : CX24116_PILOT_ON;
12528c2ecf20Sopenharmony_ci			retune++;
12538c2ecf20Sopenharmony_ci			break;
12548c2ecf20Sopenharmony_ci		case PILOT_OFF:
12558c2ecf20Sopenharmony_ci			state->dnxt.pilot_val = CX24116_PILOT_OFF;
12568c2ecf20Sopenharmony_ci			break;
12578c2ecf20Sopenharmony_ci		case PILOT_ON:
12588c2ecf20Sopenharmony_ci			state->dnxt.pilot_val = CX24116_PILOT_ON;
12598c2ecf20Sopenharmony_ci			break;
12608c2ecf20Sopenharmony_ci		default:
12618c2ecf20Sopenharmony_ci			dprintk("%s: unsupported pilot mode selected (%d)\n",
12628c2ecf20Sopenharmony_ci				__func__, c->pilot);
12638c2ecf20Sopenharmony_ci			return -EOPNOTSUPP;
12648c2ecf20Sopenharmony_ci		}
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ci		switch (c->rolloff) {
12678c2ecf20Sopenharmony_ci		case ROLLOFF_20:
12688c2ecf20Sopenharmony_ci			state->dnxt.rolloff_val = CX24116_ROLLOFF_020;
12698c2ecf20Sopenharmony_ci			break;
12708c2ecf20Sopenharmony_ci		case ROLLOFF_25:
12718c2ecf20Sopenharmony_ci			state->dnxt.rolloff_val = CX24116_ROLLOFF_025;
12728c2ecf20Sopenharmony_ci			break;
12738c2ecf20Sopenharmony_ci		case ROLLOFF_35:
12748c2ecf20Sopenharmony_ci			state->dnxt.rolloff_val = CX24116_ROLLOFF_035;
12758c2ecf20Sopenharmony_ci			break;
12768c2ecf20Sopenharmony_ci		case ROLLOFF_AUTO:	/* Rolloff must be explicit */
12778c2ecf20Sopenharmony_ci		default:
12788c2ecf20Sopenharmony_ci			dprintk("%s: unsupported rolloff selected (%d)\n",
12798c2ecf20Sopenharmony_ci				__func__, c->rolloff);
12808c2ecf20Sopenharmony_ci			return -EOPNOTSUPP;
12818c2ecf20Sopenharmony_ci		}
12828c2ecf20Sopenharmony_ci		break;
12838c2ecf20Sopenharmony_ci
12848c2ecf20Sopenharmony_ci	default:
12858c2ecf20Sopenharmony_ci		dprintk("%s: unsupported delivery system selected (%d)\n",
12868c2ecf20Sopenharmony_ci			__func__, c->delivery_system);
12878c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
12888c2ecf20Sopenharmony_ci	}
12898c2ecf20Sopenharmony_ci	state->dnxt.delsys = c->delivery_system;
12908c2ecf20Sopenharmony_ci	state->dnxt.modulation = c->modulation;
12918c2ecf20Sopenharmony_ci	state->dnxt.frequency = c->frequency;
12928c2ecf20Sopenharmony_ci	state->dnxt.pilot = c->pilot;
12938c2ecf20Sopenharmony_ci	state->dnxt.rolloff = c->rolloff;
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ci	ret = cx24116_set_inversion(state, c->inversion);
12968c2ecf20Sopenharmony_ci	if (ret !=  0)
12978c2ecf20Sopenharmony_ci		return ret;
12988c2ecf20Sopenharmony_ci
12998c2ecf20Sopenharmony_ci	/* FEC_NONE/AUTO for DVB-S2 is not supported and detected here */
13008c2ecf20Sopenharmony_ci	ret = cx24116_set_fec(state, c->delivery_system, c->modulation, c->fec_inner);
13018c2ecf20Sopenharmony_ci	if (ret !=  0)
13028c2ecf20Sopenharmony_ci		return ret;
13038c2ecf20Sopenharmony_ci
13048c2ecf20Sopenharmony_ci	ret = cx24116_set_symbolrate(state, c->symbol_rate);
13058c2ecf20Sopenharmony_ci	if (ret !=  0)
13068c2ecf20Sopenharmony_ci		return ret;
13078c2ecf20Sopenharmony_ci
13088c2ecf20Sopenharmony_ci	/* discard the 'current' tuning parameters and prepare to tune */
13098c2ecf20Sopenharmony_ci	cx24116_clone_params(fe);
13108c2ecf20Sopenharmony_ci
13118c2ecf20Sopenharmony_ci	dprintk("%s:   delsys      = %d\n", __func__, state->dcur.delsys);
13128c2ecf20Sopenharmony_ci	dprintk("%s:   modulation  = %d\n", __func__, state->dcur.modulation);
13138c2ecf20Sopenharmony_ci	dprintk("%s:   frequency   = %d\n", __func__, state->dcur.frequency);
13148c2ecf20Sopenharmony_ci	dprintk("%s:   pilot       = %d (val = 0x%02x)\n", __func__,
13158c2ecf20Sopenharmony_ci		state->dcur.pilot, state->dcur.pilot_val);
13168c2ecf20Sopenharmony_ci	dprintk("%s:   retune      = %d\n", __func__, retune);
13178c2ecf20Sopenharmony_ci	dprintk("%s:   rolloff     = %d (val = 0x%02x)\n", __func__,
13188c2ecf20Sopenharmony_ci		state->dcur.rolloff, state->dcur.rolloff_val);
13198c2ecf20Sopenharmony_ci	dprintk("%s:   symbol_rate = %d\n", __func__, state->dcur.symbol_rate);
13208c2ecf20Sopenharmony_ci	dprintk("%s:   FEC         = %d (mask/val = 0x%02x/0x%02x)\n", __func__,
13218c2ecf20Sopenharmony_ci		state->dcur.fec, state->dcur.fec_mask, state->dcur.fec_val);
13228c2ecf20Sopenharmony_ci	dprintk("%s:   Inversion   = %d (val = 0x%02x)\n", __func__,
13238c2ecf20Sopenharmony_ci		state->dcur.inversion, state->dcur.inversion_val);
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_ci	/* This is also done in advise/acquire on HVR4000 but not on LITE */
13268c2ecf20Sopenharmony_ci	if (state->config->set_ts_params)
13278c2ecf20Sopenharmony_ci		state->config->set_ts_params(fe, 0);
13288c2ecf20Sopenharmony_ci
13298c2ecf20Sopenharmony_ci	/* Set/Reset B/W */
13308c2ecf20Sopenharmony_ci	cmd.args[0x00] = CMD_BANDWIDTH;
13318c2ecf20Sopenharmony_ci	cmd.args[0x01] = 0x01;
13328c2ecf20Sopenharmony_ci	cmd.len = 0x02;
13338c2ecf20Sopenharmony_ci	ret = cx24116_cmd_execute(fe, &cmd);
13348c2ecf20Sopenharmony_ci	if (ret != 0)
13358c2ecf20Sopenharmony_ci		return ret;
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_ci	/* Prepare a tune request */
13388c2ecf20Sopenharmony_ci	cmd.args[0x00] = CMD_TUNEREQUEST;
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_ci	/* Frequency */
13418c2ecf20Sopenharmony_ci	cmd.args[0x01] = (state->dcur.frequency & 0xff0000) >> 16;
13428c2ecf20Sopenharmony_ci	cmd.args[0x02] = (state->dcur.frequency & 0x00ff00) >> 8;
13438c2ecf20Sopenharmony_ci	cmd.args[0x03] = (state->dcur.frequency & 0x0000ff);
13448c2ecf20Sopenharmony_ci
13458c2ecf20Sopenharmony_ci	/* Symbol Rate */
13468c2ecf20Sopenharmony_ci	cmd.args[0x04] = ((state->dcur.symbol_rate / 1000) & 0xff00) >> 8;
13478c2ecf20Sopenharmony_ci	cmd.args[0x05] = ((state->dcur.symbol_rate / 1000) & 0x00ff);
13488c2ecf20Sopenharmony_ci
13498c2ecf20Sopenharmony_ci	/* Automatic Inversion */
13508c2ecf20Sopenharmony_ci	cmd.args[0x06] = state->dcur.inversion_val;
13518c2ecf20Sopenharmony_ci
13528c2ecf20Sopenharmony_ci	/* Modulation / FEC / Pilot */
13538c2ecf20Sopenharmony_ci	cmd.args[0x07] = state->dcur.fec_val | state->dcur.pilot_val;
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ci	cmd.args[0x08] = CX24116_SEARCH_RANGE_KHZ >> 8;
13568c2ecf20Sopenharmony_ci	cmd.args[0x09] = CX24116_SEARCH_RANGE_KHZ & 0xff;
13578c2ecf20Sopenharmony_ci	cmd.args[0x0a] = 0x00;
13588c2ecf20Sopenharmony_ci	cmd.args[0x0b] = 0x00;
13598c2ecf20Sopenharmony_ci	cmd.args[0x0c] = state->dcur.rolloff_val;
13608c2ecf20Sopenharmony_ci	cmd.args[0x0d] = state->dcur.fec_mask;
13618c2ecf20Sopenharmony_ci
13628c2ecf20Sopenharmony_ci	if (state->dcur.symbol_rate > 30000000) {
13638c2ecf20Sopenharmony_ci		cmd.args[0x0e] = 0x04;
13648c2ecf20Sopenharmony_ci		cmd.args[0x0f] = 0x00;
13658c2ecf20Sopenharmony_ci		cmd.args[0x10] = 0x01;
13668c2ecf20Sopenharmony_ci		cmd.args[0x11] = 0x77;
13678c2ecf20Sopenharmony_ci		cmd.args[0x12] = 0x36;
13688c2ecf20Sopenharmony_ci		cx24116_writereg(state, CX24116_REG_CLKDIV, 0x44);
13698c2ecf20Sopenharmony_ci		cx24116_writereg(state, CX24116_REG_RATEDIV, 0x01);
13708c2ecf20Sopenharmony_ci	} else {
13718c2ecf20Sopenharmony_ci		cmd.args[0x0e] = 0x06;
13728c2ecf20Sopenharmony_ci		cmd.args[0x0f] = 0x00;
13738c2ecf20Sopenharmony_ci		cmd.args[0x10] = 0x00;
13748c2ecf20Sopenharmony_ci		cmd.args[0x11] = 0xFA;
13758c2ecf20Sopenharmony_ci		cmd.args[0x12] = 0x24;
13768c2ecf20Sopenharmony_ci		cx24116_writereg(state, CX24116_REG_CLKDIV, 0x46);
13778c2ecf20Sopenharmony_ci		cx24116_writereg(state, CX24116_REG_RATEDIV, 0x00);
13788c2ecf20Sopenharmony_ci	}
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_ci	cmd.len = 0x13;
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci	/* We need to support pilot and non-pilot tuning in the
13838c2ecf20Sopenharmony_ci	 * driver automatically. This is a workaround for because
13848c2ecf20Sopenharmony_ci	 * the demod does not support autodetect.
13858c2ecf20Sopenharmony_ci	 */
13868c2ecf20Sopenharmony_ci	do {
13878c2ecf20Sopenharmony_ci		/* Reset status register */
13888c2ecf20Sopenharmony_ci		status = cx24116_readreg(state, CX24116_REG_SSTATUS)
13898c2ecf20Sopenharmony_ci			& CX24116_SIGNAL_MASK;
13908c2ecf20Sopenharmony_ci		cx24116_writereg(state, CX24116_REG_SSTATUS, status);
13918c2ecf20Sopenharmony_ci
13928c2ecf20Sopenharmony_ci		/* Tune */
13938c2ecf20Sopenharmony_ci		ret = cx24116_cmd_execute(fe, &cmd);
13948c2ecf20Sopenharmony_ci		if (ret != 0)
13958c2ecf20Sopenharmony_ci			break;
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_ci		/*
13988c2ecf20Sopenharmony_ci		 * Wait for up to 500 ms before retrying
13998c2ecf20Sopenharmony_ci		 *
14008c2ecf20Sopenharmony_ci		 * If we are able to tune then generally it occurs within 100ms.
14018c2ecf20Sopenharmony_ci		 * If it takes longer, try a different toneburst setting.
14028c2ecf20Sopenharmony_ci		 */
14038c2ecf20Sopenharmony_ci		for (i = 0; i < 50 ; i++) {
14048c2ecf20Sopenharmony_ci			cx24116_read_status(fe, &tunerstat);
14058c2ecf20Sopenharmony_ci			status = tunerstat & (FE_HAS_SIGNAL | FE_HAS_SYNC);
14068c2ecf20Sopenharmony_ci			if (status == (FE_HAS_SIGNAL | FE_HAS_SYNC)) {
14078c2ecf20Sopenharmony_ci				dprintk("%s: Tuned\n", __func__);
14088c2ecf20Sopenharmony_ci				goto tuned;
14098c2ecf20Sopenharmony_ci			}
14108c2ecf20Sopenharmony_ci			msleep(10);
14118c2ecf20Sopenharmony_ci		}
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_ci		dprintk("%s: Not tuned\n", __func__);
14148c2ecf20Sopenharmony_ci
14158c2ecf20Sopenharmony_ci		/* Toggle pilot bit when in auto-pilot */
14168c2ecf20Sopenharmony_ci		if (state->dcur.pilot == PILOT_AUTO)
14178c2ecf20Sopenharmony_ci			cmd.args[0x07] ^= CX24116_PILOT_ON;
14188c2ecf20Sopenharmony_ci	} while (--retune);
14198c2ecf20Sopenharmony_ci
14208c2ecf20Sopenharmony_cituned:  /* Set/Reset B/W */
14218c2ecf20Sopenharmony_ci	cmd.args[0x00] = CMD_BANDWIDTH;
14228c2ecf20Sopenharmony_ci	cmd.args[0x01] = 0x00;
14238c2ecf20Sopenharmony_ci	cmd.len = 0x02;
14248c2ecf20Sopenharmony_ci	return cx24116_cmd_execute(fe, &cmd);
14258c2ecf20Sopenharmony_ci}
14268c2ecf20Sopenharmony_ci
14278c2ecf20Sopenharmony_cistatic int cx24116_tune(struct dvb_frontend *fe, bool re_tune,
14288c2ecf20Sopenharmony_ci	unsigned int mode_flags, unsigned int *delay, enum fe_status *status)
14298c2ecf20Sopenharmony_ci{
14308c2ecf20Sopenharmony_ci	/*
14318c2ecf20Sopenharmony_ci	 * It is safe to discard "params" here, as the DVB core will sync
14328c2ecf20Sopenharmony_ci	 * fe->dtv_property_cache with fepriv->parameters_in, where the
14338c2ecf20Sopenharmony_ci	 * DVBv3 params are stored. The only practical usage for it indicate
14348c2ecf20Sopenharmony_ci	 * that re-tuning is needed, e. g. (fepriv->state & FESTATE_RETUNE) is
14358c2ecf20Sopenharmony_ci	 * true.
14368c2ecf20Sopenharmony_ci	 */
14378c2ecf20Sopenharmony_ci
14388c2ecf20Sopenharmony_ci	*delay = HZ / 5;
14398c2ecf20Sopenharmony_ci	if (re_tune) {
14408c2ecf20Sopenharmony_ci		int ret = cx24116_set_frontend(fe);
14418c2ecf20Sopenharmony_ci		if (ret)
14428c2ecf20Sopenharmony_ci			return ret;
14438c2ecf20Sopenharmony_ci	}
14448c2ecf20Sopenharmony_ci	return cx24116_read_status(fe, status);
14458c2ecf20Sopenharmony_ci}
14468c2ecf20Sopenharmony_ci
14478c2ecf20Sopenharmony_cistatic enum dvbfe_algo cx24116_get_algo(struct dvb_frontend *fe)
14488c2ecf20Sopenharmony_ci{
14498c2ecf20Sopenharmony_ci	return DVBFE_ALGO_HW;
14508c2ecf20Sopenharmony_ci}
14518c2ecf20Sopenharmony_ci
14528c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops cx24116_ops = {
14538c2ecf20Sopenharmony_ci	.delsys = { SYS_DVBS, SYS_DVBS2 },
14548c2ecf20Sopenharmony_ci	.info = {
14558c2ecf20Sopenharmony_ci		.name = "Conexant CX24116/CX24118",
14568c2ecf20Sopenharmony_ci		.frequency_min_hz = 950 * MHz,
14578c2ecf20Sopenharmony_ci		.frequency_max_hz = 2150 * MHz,
14588c2ecf20Sopenharmony_ci		.frequency_stepsize_hz = 1011 * kHz,
14598c2ecf20Sopenharmony_ci		.frequency_tolerance_hz = 5 * MHz,
14608c2ecf20Sopenharmony_ci		.symbol_rate_min = 1000000,
14618c2ecf20Sopenharmony_ci		.symbol_rate_max = 45000000,
14628c2ecf20Sopenharmony_ci		.caps = FE_CAN_INVERSION_AUTO |
14638c2ecf20Sopenharmony_ci			FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
14648c2ecf20Sopenharmony_ci			FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
14658c2ecf20Sopenharmony_ci			FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
14668c2ecf20Sopenharmony_ci			FE_CAN_2G_MODULATION |
14678c2ecf20Sopenharmony_ci			FE_CAN_QPSK | FE_CAN_RECOVER
14688c2ecf20Sopenharmony_ci	},
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_ci	.release = cx24116_release,
14718c2ecf20Sopenharmony_ci
14728c2ecf20Sopenharmony_ci	.init = cx24116_initfe,
14738c2ecf20Sopenharmony_ci	.sleep = cx24116_sleep,
14748c2ecf20Sopenharmony_ci	.read_status = cx24116_read_status,
14758c2ecf20Sopenharmony_ci	.read_ber = cx24116_read_ber,
14768c2ecf20Sopenharmony_ci	.read_signal_strength = cx24116_read_signal_strength,
14778c2ecf20Sopenharmony_ci	.read_snr = cx24116_read_snr,
14788c2ecf20Sopenharmony_ci	.read_ucblocks = cx24116_read_ucblocks,
14798c2ecf20Sopenharmony_ci	.set_tone = cx24116_set_tone,
14808c2ecf20Sopenharmony_ci	.set_voltage = cx24116_set_voltage,
14818c2ecf20Sopenharmony_ci	.diseqc_send_master_cmd = cx24116_send_diseqc_msg,
14828c2ecf20Sopenharmony_ci	.diseqc_send_burst = cx24116_diseqc_send_burst,
14838c2ecf20Sopenharmony_ci	.get_frontend_algo = cx24116_get_algo,
14848c2ecf20Sopenharmony_ci	.tune = cx24116_tune,
14858c2ecf20Sopenharmony_ci
14868c2ecf20Sopenharmony_ci	.set_frontend = cx24116_set_frontend,
14878c2ecf20Sopenharmony_ci};
14888c2ecf20Sopenharmony_ci
14898c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("DVB Frontend module for Conexant cx24116/cx24118 hardware");
14908c2ecf20Sopenharmony_ciMODULE_AUTHOR("Steven Toth");
14918c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
14928c2ecf20Sopenharmony_ci
1493